1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-23 17:34:34 +03:00
samba-mirror/source3/torture/test_g_lock.c
Ralph Boehme fc57b88e6a smbd: remove process shortname arg from reinit_after_fork()
All callers pass NULL anyway, so it isn't used anymore.

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
2022-12-14 01:38:29 +00:00

1404 lines
29 KiB
C

/*
* Unix SMB/CIFS implementation.
* Test g_lock API
* Copyright (C) Volker Lendecke 2017
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
#include "torture/proto.h"
#include "system/filesys.h"
#include "g_lock.h"
#include "messages.h"
#include "lib/util/server_id.h"
#include "lib/util/sys_rw.h"
#include "lib/util/util_tdb.h"
#include "lib/util/tevent_ntstatus.h"
#include "lib/global_contexts.h"
static bool get_g_lock_ctx(TALLOC_CTX *mem_ctx,
struct tevent_context **ev,
struct messaging_context **msg,
struct g_lock_ctx **ctx)
{
*ev = global_event_context();
if (*ev == NULL) {
fprintf(stderr, "tevent_context_init failed\n");
return false;
}
*msg = global_messaging_context();
if (*msg == NULL) {
fprintf(stderr, "messaging_init failed\n");
TALLOC_FREE(*ev);
return false;
}
*ctx = g_lock_ctx_init(*ev, *msg);
if (*ctx == NULL) {
fprintf(stderr, "g_lock_ctx_init failed\n");
TALLOC_FREE(*msg);
TALLOC_FREE(*ev);
return false;
}
return true;
}
bool run_g_lock1(int dummy)
{
struct tevent_context *ev = NULL;
struct messaging_context *msg = NULL;
struct g_lock_ctx *ctx = NULL;
const char *lockname = "lock1";
NTSTATUS status;
bool ret = false;
bool ok;
ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
if (!ok) {
goto fail;
}
status = g_lock_lock(ctx, string_term_tdb_data(lockname), G_LOCK_WRITE,
(struct timeval) { .tv_sec = 1 },
NULL, NULL);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr, "g_lock_lock failed: %s\n",
nt_errstr(status));
goto fail;
}
status = g_lock_lock(ctx, string_term_tdb_data(lockname), G_LOCK_WRITE,
(struct timeval) { .tv_sec = 1 },
NULL, NULL);
if (!NT_STATUS_EQUAL(status, NT_STATUS_WAS_LOCKED)) {
fprintf(stderr, "Double lock got %s\n",
nt_errstr(status));
goto fail;
}
status = g_lock_unlock(ctx, string_term_tdb_data(lockname));
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr, "g_lock_unlock failed: %s\n",
nt_errstr(status));
goto fail;
}
status = g_lock_unlock(ctx, string_term_tdb_data(lockname));
if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
fprintf(stderr, "g_lock_unlock returned: %s\n",
nt_errstr(status));
goto fail;
}
ret = true;
fail:
TALLOC_FREE(ctx);
TALLOC_FREE(msg);
TALLOC_FREE(ev);
return ret;
}
struct lock2_parser_state {
uint8_t *rdata;
bool ok;
};
static void lock2_parser(struct server_id exclusive,
size_t num_shared,
const struct server_id *shared,
const uint8_t *data,
size_t datalen,
void *private_data)
{
struct lock2_parser_state *state = private_data;
if (datalen != sizeof(uint8_t)) {
return;
}
*state->rdata = *data;
state->ok = true;
}
/*
* Test g_lock_write_data
*/
bool run_g_lock2(int dummy)
{
struct tevent_context *ev = NULL;
struct messaging_context *msg = NULL;
struct g_lock_ctx *ctx = NULL;
const char *lockname = "lock2";
uint8_t data = 42;
uint8_t rdata;
struct lock2_parser_state state = { .rdata = &rdata };
NTSTATUS status;
bool ret = false;
bool ok;
ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
if (!ok) {
goto fail;
}
status = g_lock_write_data(ctx, string_term_tdb_data(lockname),
&data, sizeof(data));
if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_LOCKED)) {
fprintf(stderr, "unlocked g_lock_write_data returned %s\n",
nt_errstr(status));
goto fail;
}
status = g_lock_lock(ctx, string_term_tdb_data(lockname), G_LOCK_WRITE,
(struct timeval) { .tv_sec = 1 },
NULL, NULL);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr, "g_lock_lock returned %s\n",
nt_errstr(status));
goto fail;
}
status = g_lock_write_data(ctx, string_term_tdb_data(lockname),
&data, sizeof(data));
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr, "g_lock_write_data failed: %s\n",
nt_errstr(status));
goto fail;
}
status = g_lock_unlock(ctx, string_term_tdb_data(lockname));
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr, "g_lock_unlock failed: %s\n",
nt_errstr(status));
goto fail;
}
status = g_lock_dump(ctx, string_term_tdb_data(lockname),
lock2_parser, &state);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr, "g_lock_dump failed: %s\n",
nt_errstr(status));
goto fail;
}
if (!state.ok) {
fprintf(stderr, "Could not parse data\n");
goto fail;
}
if (rdata != data) {
fprintf(stderr, "Returned %"PRIu8", expected %"PRIu8"\n",
rdata, data);
goto fail;
}
ret = true;
fail:
TALLOC_FREE(ctx);
TALLOC_FREE(msg);
TALLOC_FREE(ev);
return ret;
}
struct lock3_parser_state {
struct server_id self;
enum g_lock_type lock_type;
bool ok;
};
static void lock3_parser(struct server_id exclusive,
size_t num_shared,
const struct server_id *shared,
const uint8_t *data,
size_t datalen,
void *private_data)
{
struct lock3_parser_state *state = private_data;
size_t num_locks = num_shared + ((exclusive.pid != 0) ? 1 : 0);
const struct server_id *pid;
if (datalen != 0) {
fprintf(stderr, "datalen=%zu\n", datalen);
return;
}
if (num_locks != 1) {
fprintf(stderr, "num_locks=%zu\n", num_locks);
return;
}
if (state->lock_type == G_LOCK_WRITE) {
if (exclusive.pid == 0) {
fprintf(stderr, "Found READ, expected WRITE\n");
return;
}
} else {
if (exclusive.pid != 0) {
fprintf(stderr, "Found WRITE, expected READ\n");
return;
}
}
pid = (exclusive.pid != 0) ? &exclusive : &shared[0];
if (!server_id_equal(pid, &state->self)) {
struct server_id_buf tmp1, tmp2;
fprintf(stderr, "found pid %s, expected %s\n",
server_id_str_buf(*pid, &tmp1),
server_id_str_buf(state->self, &tmp2));
return;
}
state->ok = true;
}
/*
* Test lock upgrade/downgrade
*/
bool run_g_lock3(int dummy)
{
struct tevent_context *ev = NULL;
struct messaging_context *msg = NULL;
struct g_lock_ctx *ctx = NULL;
const char *lockname = "lock3";
struct lock3_parser_state state;
NTSTATUS status;
bool ret = false;
bool ok;
ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
if (!ok) {
goto fail;
}
state.self = messaging_server_id(msg);
status = g_lock_lock(ctx, string_term_tdb_data(lockname), G_LOCK_READ,
(struct timeval) { .tv_sec = 1 },
NULL, NULL);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr, "g_lock_lock returned %s\n",
nt_errstr(status));
goto fail;
}
state.lock_type = G_LOCK_READ;
state.ok = false;
status = g_lock_dump(ctx, string_term_tdb_data(lockname),
lock3_parser, &state);
if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
fprintf(stderr, "g_lock_dump returned %s\n",
nt_errstr(status));
goto fail;
}
if (!state.ok) {
goto fail;
}
status = g_lock_lock(ctx, string_term_tdb_data(lockname), G_LOCK_UPGRADE,
(struct timeval) { .tv_sec = 1 },
NULL, NULL);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr, "g_lock_lock returned %s\n",
nt_errstr(status));
goto fail;
}
state.lock_type = G_LOCK_WRITE;
state.ok = false;
status = g_lock_dump(ctx, string_term_tdb_data(lockname),
lock3_parser, &state);
if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) {
fprintf(stderr, "g_lock_dump returned %s\n",
nt_errstr(status));
goto fail;
}
if (!state.ok) {
goto fail;
}
ret = true;
fail:
TALLOC_FREE(ctx);
TALLOC_FREE(msg);
TALLOC_FREE(ev);
return ret;
}
static bool lock4_child(const char *lockname,
enum g_lock_type lock_type,
int ready_pipe,
int exit_pipe)
{
struct tevent_context *ev = NULL;
struct messaging_context *msg = NULL;
struct g_lock_ctx *ctx = NULL;
NTSTATUS status;
ssize_t n;
bool ok;
ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
if (!ok) {
return false;
}
status = g_lock_lock(
ctx,
string_term_tdb_data(lockname),
lock_type,
(struct timeval) { .tv_sec = 1 },
NULL,
NULL);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr, "child: g_lock_lock returned %s\n",
nt_errstr(status));
return false;
}
n = sys_write(ready_pipe, &ok, sizeof(ok));
if (n != sizeof(ok)) {
fprintf(stderr, "child: write failed\n");
return false;
}
if (ok) {
n = sys_read(exit_pipe, &ok, sizeof(ok));
if (n != 0) {
fprintf(stderr, "child: read failed\n");
return false;
}
}
return true;
}
static void lock4_done(struct tevent_req *subreq)
{
int *done = tevent_req_callback_data_void(subreq);
NTSTATUS status;
status = g_lock_lock_recv(subreq);
TALLOC_FREE(subreq);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr, "g_lock_lock_recv returned %s\n",
nt_errstr(status));
*done = -1;
return;
}
*done = 1;
}
static void lock4_waited(struct tevent_req *subreq)
{
int *exit_pipe = tevent_req_callback_data_void(subreq);
pid_t child;
int status;
bool ok;
printf("waited\n");
ok = tevent_wakeup_recv(subreq);
TALLOC_FREE(subreq);
if (!ok) {
fprintf(stderr, "tevent_wakeup_recv failed\n");
}
close(*exit_pipe);
child = wait(&status);
printf("child %d exited with %d\n", (int)child, status);
}
struct lock4_check_state {
struct server_id me;
bool ok;
};
static void lock4_check(struct server_id exclusive,
size_t num_shared,
const struct server_id *shared,
const uint8_t *data,
size_t datalen,
void *private_data)
{
struct lock4_check_state *state = private_data;
size_t num_locks = num_shared + ((exclusive.pid != 0) ? 1 : 0);
if (num_locks != 1) {
fprintf(stderr, "num_locks=%zu\n", num_locks);
return;
}
if (exclusive.pid == 0) {
fprintf(stderr, "Wrong lock type, not WRITE\n");
return;
}
if (!server_id_equal(&state->me, &exclusive)) {
struct server_id_buf buf1, buf2;
fprintf(stderr, "me=%s, locker=%s\n",
server_id_str_buf(state->me, &buf1),
server_id_str_buf(exclusive, &buf2));
return;
}
state->ok = true;
}
/*
* Test a lock conflict: Contend with a WRITE lock
*/
bool run_g_lock4(int dummy)
{
struct tevent_context *ev = NULL;
struct messaging_context *msg = NULL;
struct g_lock_ctx *ctx = NULL;
const char *lockname = "lock4";
TDB_DATA key = string_term_tdb_data(lockname);
pid_t child;
int ready_pipe[2];
int exit_pipe[2];
NTSTATUS status;
bool ret = false;
struct tevent_req *req;
bool ok;
int done;
if ((pipe(ready_pipe) != 0) || (pipe(exit_pipe) != 0)) {
perror("pipe failed");
return false;
}
child = fork();
ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
if (!ok) {
goto fail;
}
if (child == -1) {
perror("fork failed");
return false;
}
if (child == 0) {
close(ready_pipe[0]);
close(exit_pipe[1]);
ok = lock4_child(
lockname, G_LOCK_WRITE, ready_pipe[1], exit_pipe[0]);
exit(ok ? 0 : 1);
}
close(ready_pipe[1]);
close(exit_pipe[0]);
if (sys_read(ready_pipe[0], &ok, sizeof(ok)) != sizeof(ok)) {
perror("read failed");
return false;
}
if (!ok) {
fprintf(stderr, "child returned error\n");
return false;
}
status = g_lock_lock(ctx, key, G_LOCK_WRITE,
(struct timeval) { .tv_usec = 1 },
NULL, NULL);
if (!NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
fprintf(stderr, "g_lock_lock returned %s\n",
nt_errstr(status));
goto fail;
}
status = g_lock_lock(ctx, key, G_LOCK_READ,
(struct timeval) { .tv_usec = 1 },
NULL, NULL);
if (!NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
fprintf(stderr, "g_lock_lock returned %s\n",
nt_errstr(status));
goto fail;
}
req = g_lock_lock_send(ev, ev, ctx, key, G_LOCK_WRITE, NULL, NULL);
if (req == NULL) {
fprintf(stderr, "g_lock_lock send failed\n");
goto fail;
}
tevent_req_set_callback(req, lock4_done, &done);
req = tevent_wakeup_send(ev, ev, timeval_current_ofs(1, 0));
if (req == NULL) {
fprintf(stderr, "tevent_wakeup_send failed\n");
goto fail;
}
tevent_req_set_callback(req, lock4_waited, &exit_pipe[1]);
done = 0;
while (done == 0) {
int tevent_ret = tevent_loop_once(ev);
if (tevent_ret != 0) {
perror("tevent_loop_once failed");
goto fail;
}
}
{
struct lock4_check_state state = {
.me = messaging_server_id(msg)
};
status = g_lock_dump(ctx, key, lock4_check, &state);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr, "g_lock_dump failed: %s\n",
nt_errstr(status));
goto fail;
}
if (!state.ok) {
fprintf(stderr, "lock4_check failed\n");
goto fail;
}
}
ret = true;
fail:
TALLOC_FREE(ctx);
TALLOC_FREE(msg);
TALLOC_FREE(ev);
return ret;
}
/*
* Test a lock conflict: Contend with a READ lock
*/
bool run_g_lock4a(int dummy)
{
struct tevent_context *ev = NULL;
struct messaging_context *msg = NULL;
struct g_lock_ctx *ctx = NULL;
const char *lockname = "lock4a";
TDB_DATA key = string_term_tdb_data(lockname);
pid_t child;
int ready_pipe[2];
int exit_pipe[2];
NTSTATUS status;
bool ret = false;
struct tevent_req *req;
bool ok;
int done;
if ((pipe(ready_pipe) != 0) || (pipe(exit_pipe) != 0)) {
perror("pipe failed");
return false;
}
child = fork();
ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
if (!ok) {
goto fail;
}
if (child == -1) {
perror("fork failed");
return false;
}
if (child == 0) {
close(ready_pipe[0]);
close(exit_pipe[1]);
ok = lock4_child(
lockname, G_LOCK_READ, ready_pipe[1], exit_pipe[0]);
exit(ok ? 0 : 1);
}
close(ready_pipe[1]);
close(exit_pipe[0]);
if (sys_read(ready_pipe[0], &ok, sizeof(ok)) != sizeof(ok)) {
perror("read failed");
return false;
}
if (!ok) {
fprintf(stderr, "child returned error\n");
return false;
}
status = g_lock_lock(ctx, key, G_LOCK_WRITE,
(struct timeval) { .tv_usec = 1 },
NULL, NULL);
if (!NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
fprintf(stderr, "g_lock_lock returned %s\n",
nt_errstr(status));
goto fail;
}
status = g_lock_lock(ctx, key, G_LOCK_READ,
(struct timeval) { .tv_usec = 1 },
NULL, NULL);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr, "g_lock_lock returned %s\n",
nt_errstr(status));
goto fail;
}
status = g_lock_unlock(ctx, key);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr,
"g_lock_unlock returned %s\n",
nt_errstr(status));
goto fail;
}
req = g_lock_lock_send(ev, ev, ctx, key, G_LOCK_WRITE, NULL, NULL);
if (req == NULL) {
fprintf(stderr, "g_lock_lock send failed\n");
goto fail;
}
tevent_req_set_callback(req, lock4_done, &done);
req = tevent_wakeup_send(ev, ev, timeval_current_ofs(1, 0));
if (req == NULL) {
fprintf(stderr, "tevent_wakeup_send failed\n");
goto fail;
}
tevent_req_set_callback(req, lock4_waited, &exit_pipe[1]);
done = 0;
while (done == 0) {
int tevent_ret = tevent_loop_once(ev);
if (tevent_ret != 0) {
perror("tevent_loop_once failed");
goto fail;
}
}
{
struct lock4_check_state state = {
.me = messaging_server_id(msg)
};
status = g_lock_dump(ctx, key, lock4_check, &state);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr, "g_lock_dump failed: %s\n",
nt_errstr(status));
goto fail;
}
if (!state.ok) {
fprintf(stderr, "lock4_check failed\n");
goto fail;
}
}
ret = true;
fail:
TALLOC_FREE(ctx);
TALLOC_FREE(msg);
TALLOC_FREE(ev);
return ret;
}
struct lock5_parser_state {
size_t num_locks;
};
static void lock5_parser(struct server_id exclusive,
size_t num_shared,
const struct server_id *shared,
const uint8_t *data,
size_t datalen,
void *private_data)
{
struct lock5_parser_state *state = private_data;
state->num_locks = num_shared + ((exclusive.pid != 0) ? 1 : 0);
}
/*
* Test heuristic cleanup
*/
bool run_g_lock5(int dummy)
{
struct tevent_context *ev = NULL;
struct messaging_context *msg = NULL;
struct g_lock_ctx *ctx = NULL;
const char *lockname = "lock5";
pid_t child;
int exit_pipe[2], ready_pipe[2];
NTSTATUS status;
size_t i, nprocs;
int ret;
bool ok;
ssize_t nread;
char c;
nprocs = 5;
if ((pipe(exit_pipe) != 0) || (pipe(ready_pipe) != 0)) {
perror("pipe failed");
return false;
}
ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
if (!ok) {
fprintf(stderr, "get_g_lock_ctx failed");
return false;
}
for (i=0; i<nprocs; i++) {
child = fork();
if (child == -1) {
perror("fork failed");
return false;
}
if (child == 0) {
TALLOC_FREE(ctx);
status = reinit_after_fork(msg, ev, false);
close(ready_pipe[0]);
close(exit_pipe[1]);
ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
if (!ok) {
fprintf(stderr, "get_g_lock_ctx failed");
exit(1);
}
status = g_lock_lock(ctx,
string_term_tdb_data(lockname),
G_LOCK_READ,
(struct timeval) { .tv_sec = 1 },
NULL, NULL);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr,
"child g_lock_lock failed %s\n",
nt_errstr(status));
exit(1);
}
close(ready_pipe[1]);
nread = sys_read(exit_pipe[0], &c, sizeof(c));
if (nread != 0) {
fprintf(stderr, "sys_read returned %zu (%s)\n",
nread, strerror(errno));
exit(1);
}
exit(0);
}
}
close(ready_pipe[1]);
nread = sys_read(ready_pipe[0], &c, sizeof(c));
if (nread != 0) {
fprintf(stderr, "sys_read returned %zu (%s)\n",
nread, strerror(errno));
return false;
}
close(exit_pipe[1]);
for (i=0; i<nprocs; i++) {
int child_status;
ret = waitpid(-1, &child_status, 0);
if (ret == -1) {
perror("waitpid failed");
return false;
}
}
for (i=0; i<nprocs; i++) {
struct lock5_parser_state state;
status = g_lock_dump(ctx, string_term_tdb_data(lockname),
lock5_parser, &state);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr, "g_lock_dump returned %s\n",
nt_errstr(status));
return false;
}
if (state.num_locks != (nprocs - i)) {
fprintf(stderr, "nlocks=%zu, expected %zu\n",
state.num_locks, (nprocs-i));
return false;
}
status = g_lock_lock(ctx, string_term_tdb_data(lockname),
G_LOCK_READ,
(struct timeval) { .tv_sec = 1 },
NULL, NULL);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr, "g_lock_lock failed %s\n",
nt_errstr(status));
return false;
}
status = g_lock_unlock(ctx, string_term_tdb_data(lockname));
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr, "g_lock_unlock failed %s\n",
nt_errstr(status));
return false;
}
}
return true;
}
struct lock6_parser_state {
size_t num_locks;
};
static void lock6_parser(struct server_id exclusive,
size_t num_shared,
const struct server_id *shared,
const uint8_t *data,
size_t datalen,
void *private_data)
{
struct lock6_parser_state *state = private_data;
state->num_locks = num_shared + ((exclusive.pid != 0) ? 1 : 0);
}
/*
* Test cleanup with contention and stale locks
*/
bool run_g_lock6(int dummy)
{
struct tevent_context *ev = NULL;
struct messaging_context *msg = NULL;
struct g_lock_ctx *ctx = NULL;
TDB_DATA lockname = string_term_tdb_data("lock6");
pid_t child;
int exit_pipe[2], ready_pipe[2];
NTSTATUS status;
size_t i, nprocs;
int ret;
bool ok;
ssize_t nread;
char c;
if ((pipe(exit_pipe) != 0) || (pipe(ready_pipe) != 0)) {
perror("pipe failed");
return false;
}
ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
if (!ok) {
fprintf(stderr, "get_g_lock_ctx failed");
return false;
}
/*
* Wipe all stale locks -- in clustered mode there's no
* CLEAR_IF_FIRST
*/
status = g_lock_lock(ctx, lockname, G_LOCK_WRITE,
(struct timeval) { .tv_sec = 1 },
NULL, NULL);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr, "g_lock_lock failed: %s\n",
nt_errstr(status));
return false;
}
status = g_lock_unlock(ctx, lockname);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr, "g_lock_unlock failed: %s\n",
nt_errstr(status));
return false;
}
nprocs = 2;
for (i=0; i<nprocs; i++) {
child = fork();
if (child == -1) {
perror("fork failed");
return false;
}
if (child == 0) {
TALLOC_FREE(ctx);
status = reinit_after_fork(msg, ev, false);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr, "reinit_after_fork failed: %s\n",
nt_errstr(status));
exit(1);
}
close(ready_pipe[0]);
close(exit_pipe[1]);
ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
if (!ok) {
fprintf(stderr, "get_g_lock_ctx failed");
exit(1);
}
status = g_lock_lock(ctx,
lockname,
G_LOCK_READ,
(struct timeval) { .tv_sec = 1 },
NULL, NULL);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr,
"child g_lock_lock failed %s\n",
nt_errstr(status));
exit(1);
}
if (i == 0) {
exit(0);
}
close(ready_pipe[1]);
nread = sys_read(exit_pipe[0], &c, sizeof(c));
if (nread != 0) {
fprintf(stderr, "sys_read returned %zu (%s)\n",
nread, strerror(errno));
exit(1);
}
exit(0);
}
}
close(ready_pipe[1]);
nread = sys_read(ready_pipe[0], &c, sizeof(c));
if (nread != 0) {
fprintf(stderr, "sys_read returned %zd (%s)\n",
nread, strerror(errno));
return false;
}
{
int child_status;
ret = waitpid(-1, &child_status, 0);
if (ret == -1) {
perror("waitpid failed");
return false;
}
}
{
struct lock6_parser_state state;
status = g_lock_dump(ctx, lockname, lock6_parser, &state);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr, "g_lock_dump returned %s\n",
nt_errstr(status));
return false;
}
if (state.num_locks != nprocs) {
fprintf(stderr, "nlocks=%zu, expected %zu\n",
state.num_locks, nprocs);
return false;
}
status = g_lock_lock(ctx,
lockname,
G_LOCK_WRITE,
(struct timeval) { .tv_sec = 1 },
NULL, NULL);
if (!NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
fprintf(stderr, "g_lock_lock should have failed with %s - %s\n",
nt_errstr(NT_STATUS_IO_TIMEOUT),
nt_errstr(status));
return false;
}
status = g_lock_lock(ctx, lockname, G_LOCK_READ,
(struct timeval) { .tv_sec = 1 },
NULL, NULL);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr, "g_lock_lock failed: %s\n",
nt_errstr(status));
return false;
}
}
close(exit_pipe[1]);
{
int child_status;
ret = waitpid(-1, &child_status, 0);
if (ret == -1) {
perror("waitpid failed");
return false;
}
}
status = g_lock_lock(ctx, lockname, G_LOCK_UPGRADE,
(struct timeval) { .tv_sec = 1 },
NULL, NULL);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr, "g_lock_lock failed: %s\n",
nt_errstr(status));
return false;
}
return true;
}
/*
* Test upgrade deadlock
*/
bool run_g_lock7(int dummy)
{
struct tevent_context *ev = NULL;
struct messaging_context *msg = NULL;
struct g_lock_ctx *ctx = NULL;
const char *lockname = "lock7";
TDB_DATA key = string_term_tdb_data(lockname);
pid_t child;
int ready_pipe[2];
int down_pipe[2];
ssize_t n;
NTSTATUS status;
bool ret = false;
bool ok = true;
if ((pipe(ready_pipe) != 0) || (pipe(down_pipe) != 0)) {
perror("pipe failed");
return false;
}
child = fork();
ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
if (!ok) {
goto fail;
}
if (child == -1) {
perror("fork failed");
return false;
}
if (child == 0) {
struct tevent_req *req = NULL;
close(ready_pipe[0]);
ready_pipe[0] = -1;
close(down_pipe[1]);
down_pipe[1] = -1;
status = reinit_after_fork(msg, ev, false);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr,
"reinit_after_fork failed: %s\n",
nt_errstr(status));
exit(1);
}
printf("%d: locking READ\n", (int)getpid());
status = g_lock_lock(
ctx,
key,
G_LOCK_READ,
(struct timeval) { .tv_usec = 1 },
NULL, NULL);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr,
"g_lock_lock(READ) failed: %s\n",
nt_errstr(status));
exit(1);
}
ok = true;
n = sys_write(ready_pipe[1], &ok, sizeof(ok));
if (n != sizeof(ok)) {
fprintf(stderr,
"sys_write failed: %s\n",
strerror(errno));
exit(1);
}
n = sys_read(down_pipe[0], &ok, sizeof(ok));
if (n != sizeof(ok)) {
fprintf(stderr,
"sys_read failed: %s\n",
strerror(errno));
exit(1);
}
printf("%d: starting UPGRADE\n", (int)getpid());
req = g_lock_lock_send(
msg,
ev,
ctx,
key,
G_LOCK_UPGRADE,
NULL, NULL);
if (req == NULL) {
fprintf(stderr, "g_lock_lock_send(UPGRADE) failed\n");
exit(1);
}
n = sys_write(ready_pipe[1], &ok, sizeof(ok));
if (n != sizeof(ok)) {
fprintf(stderr,
"sys_write failed: %s\n",
strerror(errno));
exit(1);
}
exit(0);
}
close(ready_pipe[1]);
close(down_pipe[0]);
if (sys_read(ready_pipe[0], &ok, sizeof(ok)) != sizeof(ok)) {
perror("read failed");
return false;
}
if (!ok) {
fprintf(stderr, "child returned error\n");
return false;
}
status = g_lock_lock(
ctx,
key,
G_LOCK_READ,
(struct timeval) { .tv_usec = 1 },
NULL, NULL);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr,
"g_lock_lock(READ) failed: %s\n",
nt_errstr(status));
goto fail;
}
n = sys_write(down_pipe[1], &ok, sizeof(ok));
if (n != sizeof(ok)) {
fprintf(stderr,
"sys_write failed: %s\n",
strerror(errno));
goto fail;
}
if (sys_read(ready_pipe[0], &ok, sizeof(ok)) != sizeof(ok)) {
perror("read failed");
goto fail;
}
status = g_lock_lock(
ctx,
key,
G_LOCK_UPGRADE,
(struct timeval) { .tv_sec = 10 },
NULL, NULL);
if (!NT_STATUS_EQUAL(status, NT_STATUS_POSSIBLE_DEADLOCK)) {
fprintf(stderr,
"g_lock_lock returned %s\n",
nt_errstr(status));
goto fail;
}
ret = true;
fail:
TALLOC_FREE(ctx);
TALLOC_FREE(msg);
TALLOC_FREE(ev);
return ret;
}
bool run_g_lock8(int dummy)
{
struct tevent_context *ev = NULL;
struct messaging_context *msg = NULL;
struct g_lock_ctx *ctx = NULL;
struct tevent_req *req = NULL;
TDB_DATA lockname = string_term_tdb_data("lock8");
NTSTATUS status;
bool ok;
ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
if (!ok) {
fprintf(stderr, "get_g_lock_ctx failed");
return false;
}
req = g_lock_watch_data_send(
ev, ev, ctx, lockname, (struct server_id) { .pid = 0 });
if (req == NULL) {
fprintf(stderr, "get_g_lock_ctx failed");
return false;
}
status = g_lock_lock(
ctx,
lockname,
G_LOCK_WRITE,
(struct timeval) { .tv_sec = 999 },
NULL, NULL);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr,
"g_lock_lock failed: %s\n",
nt_errstr(status));
return false;
}
status = g_lock_write_data(
ctx, lockname, lockname.dptr, lockname.dsize);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr,
"g_lock_write_data failed: %s\n",
nt_errstr(status));
return false;
}
status = g_lock_write_data(ctx, lockname, NULL, 0);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr,
"g_lock_write_data failed: %s\n",
nt_errstr(status));
return false;
}
status = g_lock_unlock(ctx, lockname);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr,
"g_lock_unlock failed: %s\n",
nt_errstr(status));
return false;
}
ok = tevent_req_poll_ntstatus(req, ev, &status);
if (!ok) {
fprintf(stderr, "tevent_req_poll_ntstatus failed\n");
return false;
}
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr,
"tevent_req_poll_ntstatus failed: %s\n",
nt_errstr(status));
return false;
}
return true;
}
extern int torture_numops;
extern int torture_nprocs;
static struct timeval tp1, tp2;
static void start_timer(void)
{
gettimeofday(&tp1,NULL);
}
static double end_timer(void)
{
gettimeofday(&tp2,NULL);
return (tp2.tv_sec + (tp2.tv_usec*1.0e-6)) -
(tp1.tv_sec + (tp1.tv_usec*1.0e-6));
}
/*
* g_lock ping_pong
*/
bool run_g_lock_ping_pong(int dummy)
{
struct tevent_context *ev = NULL;
struct messaging_context *msg = NULL;
struct g_lock_ctx *ctx = NULL;
fstring name;
NTSTATUS status;
int i = 0;
bool ret = false;
bool ok;
unsigned count = 0;
torture_nprocs = MAX(2, torture_nprocs);
ok = get_g_lock_ctx(talloc_tos(), &ev, &msg, &ctx);
if (!ok) {
goto fail;
}
start_timer();
snprintf(name, sizeof(name), "ping_pong_%d", i);
status = g_lock_lock(ctx, string_term_tdb_data(name), G_LOCK_WRITE,
(struct timeval) { .tv_sec = 60 },
NULL, NULL);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr, "g_lock_lock failed: %s\n",
nt_errstr(status));
goto fail;
}
for (i=0; i<torture_numops; i++) {
name[10] = '0' + ((i+1) % torture_nprocs);
status = g_lock_lock(ctx, string_term_tdb_data(name),
G_LOCK_WRITE,
(struct timeval) { .tv_sec = 60 },
NULL, NULL);
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr, "g_lock_lock failed: %s\n",
nt_errstr(status));
goto fail;
}
name[10] = '0' + ((i) % torture_nprocs);
status = g_lock_unlock(ctx, string_term_tdb_data(name));
if (!NT_STATUS_IS_OK(status)) {
fprintf(stderr, "g_lock_unlock failed: %s\n",
nt_errstr(status));
goto fail;
}
count++;
if (end_timer() > 1.0) {
printf("%8u locks/sec\r",
(unsigned)(2*count/end_timer()));
fflush(stdout);
start_timer();
count=0;
}
}
ret = true;
fail:
TALLOC_FREE(ctx);
TALLOC_FREE(msg);
TALLOC_FREE(ev);
return ret;
}