mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
fc57b88e6a
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>
1404 lines
29 KiB
C
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;
|
|
}
|