/*
sock daemon tests
Copyright (C) Amitay Isaacs 2016
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 .
*/
#include "replace.h"
#include "system/filesys.h"
#include "system/network.h"
#include "system/wait.h"
#include
#include "common/logging.c"
#include "common/pkt_read.c"
#include "common/pkt_write.c"
#include "common/comm.c"
#include "common/pidfile.c"
#include "common/sock_daemon.c"
#include "common/sock_io.c"
struct dummy_wait_state {
};
static struct tevent_req *dummy_wait_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
void *private_data)
{
struct tevent_req *req;
struct dummy_wait_state *state;
const char *sockpath = (const char *)private_data;
struct stat st;
int ret;
ret = stat(sockpath, &st);
assert(ret == 0);
assert(S_ISSOCK(st.st_mode));
req = tevent_req_create(mem_ctx, &state, struct dummy_wait_state);
if (req == NULL) {
return NULL;
}
tevent_req_done(req);
return tevent_req_post(req, ev);
}
static bool dummy_wait_recv(struct tevent_req *req, int *perr)
{
return true;
}
static int test1_startup_fail(void *private_data)
{
return 1;
}
static int test1_startup(void *private_data)
{
const char *sockpath = (const char *)private_data;
struct stat st;
int ret;
ret = stat(sockpath, &st);
assert(ret == -1);
return 0;
}
struct test1_startup_state {
};
static struct tevent_req *test1_startup_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
void *private_data)
{
struct tevent_req *req;
struct test1_startup_state *state;
req = tevent_req_create(mem_ctx, &state, struct test1_startup_state);
if (req == NULL) {
return NULL;
}
tevent_req_error(req, 2);
return tevent_req_post(req, ev);
}
static bool test1_startup_recv(struct tevent_req *req, int *perr)
{
if (tevent_req_is_unix_error(req, perr)) {
return false;
}
return true;
}
static struct tevent_req *dummy_read_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct sock_client_context *client,
uint8_t *buf, size_t buflen,
void *private_data)
{
return NULL;
}
static bool dummy_read_recv(struct tevent_req *req, int *perr)
{
if (perr != NULL) {
*perr = EINVAL;
}
return false;
}
static struct sock_socket_funcs dummy_socket_funcs = {
.read_send = dummy_read_send,
.read_recv = dummy_read_recv,
};
/*
* test1
*
* Check setup without actually running daemon
*/
static void test1(TALLOC_CTX *mem_ctx, const char *pidfile,
const char *sockpath)
{
struct tevent_context *ev;
struct sock_daemon_context *sockd;
struct sock_daemon_funcs test1_funcs;
struct stat st;
int ret;
ev = tevent_context_init(mem_ctx);
assert(ev != NULL);
test1_funcs = (struct sock_daemon_funcs){
.startup = test1_startup_fail,
};
ret = sock_daemon_setup(mem_ctx, "test1", "file:", "NOTICE",
&test1_funcs, NULL, &sockd);
assert(ret == 0);
assert(sockd != NULL);
ret = stat(pidfile, &st);
assert(ret == -1);
ret = sock_daemon_run(ev, sockd, NULL, false, false, -1);
assert(ret == EIO);
talloc_free(sockd);
test1_funcs = (struct sock_daemon_funcs){
.startup_send = test1_startup_send,
.startup_recv = test1_startup_recv,
};
ret = sock_daemon_setup(mem_ctx, "test1", "file:", "NOTICE",
&test1_funcs, NULL, &sockd);
assert(ret == 0);
assert(sockd != NULL);
ret = stat(pidfile, &st);
assert(ret == -1);
ret = sock_daemon_run(ev, sockd, NULL, false, false, -1);
assert(ret == EIO);
talloc_free(sockd);
test1_funcs = (struct sock_daemon_funcs){
.startup = test1_startup,
.wait_send = dummy_wait_send,
.wait_recv = dummy_wait_recv,
};
ret = sock_daemon_setup(mem_ctx, "test1", "file:", "NOTICE",
&test1_funcs, discard_const(sockpath), &sockd);
assert(ret == 0);
assert(sockd != NULL);
ret = sock_daemon_add_unix(sockd, sockpath, &dummy_socket_funcs, NULL);
assert(ret == 0);
ret = stat(sockpath, &st);
assert(ret == -1);
ret = sock_daemon_run(ev, sockd, NULL, false, false, -1);
assert(ret == 0);
talloc_free(mem_ctx);
}
/*
* test2
*
* Start daemon, check PID file, sock daemon functions, termination,
* exit code
*/
static int test2_startup(void *private_data)
{
int fd = *(int *)private_data;
int ret = 1;
ssize_t nwritten;
nwritten = write(fd, &ret, sizeof(ret));
assert(nwritten == sizeof(ret));
return 0;
}
static int test2_reconfigure(void *private_data)
{
static bool first_time = true;
int fd = *(int *)private_data;
int ret = 2;
ssize_t nwritten;
nwritten = write(fd, &ret, sizeof(ret));
assert(nwritten == sizeof(ret));
if (first_time) {
first_time = false;
return 1;
}
return 0;
}
struct test2_reconfigure_state {
int fd;
};
static struct tevent_req *test2_reconfigure_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
void *private_data)
{
struct tevent_req *req;
struct test2_reconfigure_state *state;
static bool first_time = true;
req = tevent_req_create(mem_ctx, &state,
struct test2_reconfigure_state);
if (req == NULL) {
return NULL;
}
state->fd = *(int *)private_data;
if (first_time) {
first_time = false;
tevent_req_error(req, 2);
} else {
tevent_req_done(req);
}
return tevent_req_post(req, ev);
}
static bool test2_reconfigure_recv(struct tevent_req *req, int *perr)
{
struct test2_reconfigure_state *state = tevent_req_data(
req, struct test2_reconfigure_state);
int ret = 2;
ssize_t nwritten;
nwritten = write(state->fd, &ret, sizeof(ret));
assert(nwritten == sizeof(ret));
if (tevent_req_is_unix_error(req, perr)) {
return false;
}
return true;
}
static int test2_reopen_logs(void *private_data)
{
static bool first_time = true;
int fd = *(int *)private_data;
int ret = 4;
ssize_t nwritten;
nwritten = write(fd, &ret, sizeof(ret));
assert(nwritten == sizeof(ret));
if (first_time) {
first_time = false;
return 1;
}
return 0;
}
struct test2_reopen_logs_state {
int fd;
};
static struct tevent_req *test2_reopen_logs_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
void *private_data)
{
struct tevent_req *req;
struct test2_reopen_logs_state *state;
static bool first_time = true;
req = tevent_req_create(mem_ctx, &state,
struct test2_reopen_logs_state);
if (req == NULL) {
return NULL;
}
state->fd = *(int *)private_data;
if (first_time) {
first_time = false;
tevent_req_error(req, 2);
} else {
tevent_req_done(req);
}
return tevent_req_post(req, ev);
}
static bool test2_reopen_logs_recv(struct tevent_req *req, int *perr)
{
struct test2_reopen_logs_state *state = tevent_req_data(
req, struct test2_reopen_logs_state);
int ret = 4;
ssize_t nwritten;
nwritten = write(state->fd, &ret, sizeof(ret));
assert(nwritten == sizeof(ret));
if (tevent_req_is_unix_error(req, perr)) {
return false;
}
return true;
}
static void test2_shutdown(void *private_data)
{
int fd = *(int *)private_data;
int ret = 3;
ssize_t nwritten;
nwritten = write(fd, &ret, sizeof(ret));
assert(nwritten == sizeof(ret));
}
struct test2_shutdown_state {
int fd;
};
static struct tevent_req *test2_shutdown_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
void *private_data)
{
struct tevent_req *req;
struct test2_shutdown_state *state;
req = tevent_req_create(mem_ctx, &state,
struct test2_shutdown_state);
if (req == NULL) {
return NULL;
}
state->fd = *(int *)private_data;
tevent_req_done(req);
return tevent_req_post(req, ev);
}
static void test2_shutdown_recv(struct tevent_req *req)
{
struct test2_shutdown_state *state = tevent_req_data(
req, struct test2_shutdown_state);
int ret = 3;
ssize_t nwritten;
nwritten = write(state->fd, &ret, sizeof(ret));
assert(nwritten == sizeof(ret));
}
static void test2(TALLOC_CTX *mem_ctx, const char *pidfile,
const char *sockpath)
{
struct stat st;
int fd[2];
pid_t pid, pid2;
int ret;
ssize_t n;
int pidfile_fd;
char pidstr[20] = { 0 };
ret = pipe(fd);
assert(ret == 0);
pid = fork();
assert(pid != -1);
if (pid == 0) {
struct tevent_context *ev;
struct sock_daemon_context *sockd;
struct sock_daemon_funcs test2_funcs = {
.startup = test2_startup,
.reconfigure = test2_reconfigure,
.reopen_logs = test2_reopen_logs,
.shutdown = test2_shutdown,
};
close(fd[0]);
ev = tevent_context_init(mem_ctx);
assert(ev != NULL);
ret = sock_daemon_setup(mem_ctx, "test2", "file:", "NOTICE",
&test2_funcs, &fd[1], &sockd);
assert(ret == 0);
ret = sock_daemon_add_unix(sockd, sockpath,
&dummy_socket_funcs, NULL);
assert(ret == 0);
ret = sock_daemon_run(ev, sockd, pidfile, false, false, -1);
assert(ret == EINTR);
exit(0);
}
close(fd[1]);
n = read(fd[0], &ret, sizeof(ret));
assert(n == sizeof(ret));
assert(ret == 1);
pidfile_fd = open(pidfile, O_RDONLY, 0644);
assert(pidfile_fd != -1);
ret = fstat(pidfile_fd, &st);
assert(ret == 0);
assert(S_ISREG(st.st_mode));
n = read(pidfile_fd, pidstr, sizeof(pidstr)-1);
assert(n != -1);
pid2 = (pid_t)atoi(pidstr);
assert(pid == pid2);
close(pidfile_fd);
ret = kill(pid, SIGUSR1);
assert(ret == 0);
n = read(fd[0], &ret, sizeof(ret));
assert(n == sizeof(ret));
assert(ret == 2);
ret = kill(pid, SIGUSR1);
assert(ret == 0);
n = read(fd[0], &ret, sizeof(ret));
assert(n == sizeof(ret));
assert(ret == 2);
ret = kill(pid, SIGHUP);
assert(ret == 0);
n = read(fd[0], &ret, sizeof(ret));
assert(n == sizeof(ret));
assert(ret == 4);
ret = kill(pid, SIGHUP);
assert(ret == 0);
n = read(fd[0], &ret, sizeof(ret));
assert(n == sizeof(ret));
assert(ret == 4);
ret = kill(pid, SIGTERM);
assert(ret == 0);
n = read(fd[0], &ret, sizeof(ret));
assert(n == sizeof(ret));
assert(ret == 3);
pid2 = waitpid(pid, &ret, 0);
assert(pid2 == pid);
assert(WEXITSTATUS(ret) == 0);
close(fd[0]);
ret = stat(pidfile, &st);
assert(ret == -1);
ret = stat(sockpath, &st);
assert(ret == -1);
ret = pipe(fd);
assert(ret == 0);
pid = fork();
assert(pid != -1);
if (pid == 0) {
struct tevent_context *ev;
struct sock_daemon_context *sockd;
struct sock_daemon_funcs test2_funcs = {
.startup = test2_startup,
.reconfigure_send = test2_reconfigure_send,
.reconfigure_recv = test2_reconfigure_recv,
.reopen_logs_send = test2_reopen_logs_send,
.reopen_logs_recv = test2_reopen_logs_recv,
.shutdown_send = test2_shutdown_send,
.shutdown_recv = test2_shutdown_recv,
};
close(fd[0]);
ev = tevent_context_init(mem_ctx);
assert(ev != NULL);
ret = sock_daemon_setup(mem_ctx, "test2", "file:", "NOTICE",
&test2_funcs, &fd[1], &sockd);
assert(ret == 0);
ret = sock_daemon_add_unix(sockd, sockpath,
&dummy_socket_funcs, NULL);
assert(ret == 0);
ret = sock_daemon_run(ev, sockd, pidfile, false, false, -1);
assert(ret == EINTR);
exit(0);
}
close(fd[1]);
n = read(fd[0], &ret, sizeof(ret));
assert(n == sizeof(ret));
assert(ret == 1);
ret = kill(pid, SIGUSR1);
assert(ret == 0);
n = read(fd[0], &ret, sizeof(ret));
assert(n == sizeof(ret));
assert(ret == 2);
ret = kill(pid, SIGUSR1);
assert(ret == 0);
n = read(fd[0], &ret, sizeof(ret));
assert(n == sizeof(ret));
assert(ret == 2);
ret = kill(pid, SIGHUP);
assert(ret == 0);
n = read(fd[0], &ret, sizeof(ret));
assert(n == sizeof(ret));
assert(ret == 4);
ret = kill(pid, SIGHUP);
assert(ret == 0);
n = read(fd[0], &ret, sizeof(ret));
assert(n == sizeof(ret));
assert(ret == 4);
ret = kill(pid, SIGTERM);
assert(ret == 0);
n = read(fd[0], &ret, sizeof(ret));
assert(n == sizeof(ret));
assert(ret == 3);
pid2 = waitpid(pid, &ret, 0);
assert(pid2 == pid);
assert(WEXITSTATUS(ret) == 0);
close(fd[0]);
}
/*
* test3
*
* Start daemon, test watching of (parent) PID
*/
static void test3(TALLOC_CTX *mem_ctx, const char *pidfile,
const char *sockpath)
{
struct stat st;
pid_t pid_watch, pid, pid2;
int ret;
pid_watch = fork();
assert(pid_watch != -1);
if (pid_watch == 0) {
sleep(10);
exit(0);
}
pid = fork();
assert(pid != -1);
if (pid == 0) {
struct tevent_context *ev;
struct sock_daemon_context *sockd;
ev = tevent_context_init(mem_ctx);
assert(ev != NULL);
ret = sock_daemon_setup(mem_ctx, "test3", "file:", "NOTICE",
NULL, NULL, &sockd);
assert(ret == 0);
ret = sock_daemon_add_unix(sockd, sockpath,
&dummy_socket_funcs, NULL);
assert(ret == 0);
ret = sock_daemon_run(ev, sockd, NULL, false, false, pid_watch);
assert(ret == ESRCH);
exit(0);
}
pid2 = waitpid(pid_watch, &ret, 0);
assert(pid2 == pid_watch);
assert(WEXITSTATUS(ret) == 0);
pid2 = waitpid(pid, &ret, 0);
assert(pid2 == pid);
assert(WEXITSTATUS(ret) == 0);
ret = stat(pidfile, &st);
assert(ret == -1);
ret = stat(sockpath, &st);
assert(ret == -1);
}
/*
* test4
*
* Start daemon, test termination via wait_send function
*/
struct test4_wait_state {
};
static void test4_wait_done(struct tevent_req *subreq);
static struct tevent_req *test4_wait_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
void *private_data)
{
struct tevent_req *req, *subreq;
struct test4_wait_state *state;
req = tevent_req_create(mem_ctx, &state, struct test4_wait_state);
if (req == NULL) {
return NULL;
}
subreq = tevent_wakeup_send(state, ev,
tevent_timeval_current_ofs(10,0));
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
tevent_req_set_callback(subreq, test4_wait_done, req);
return req;
}
static void test4_wait_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
bool status;
status = tevent_wakeup_recv(subreq);
TALLOC_FREE(subreq);
if (! status) {
tevent_req_error(req, EIO);
} else {
tevent_req_done(req);
}
}
static bool test4_wait_recv(struct tevent_req *req, int *perr)
{
int ret;
if (tevent_req_is_unix_error(req, &ret)) {
if (perr != NULL) {
*perr = ret;
}
return false;
}
return true;
}
static struct sock_daemon_funcs test4_funcs = {
.wait_send = test4_wait_send,
.wait_recv = test4_wait_recv,
};
static void test4(TALLOC_CTX *mem_ctx, const char *pidfile,
const char *sockpath)
{
struct stat st;
pid_t pid, pid2;
int ret;
pid = fork();
assert(pid != -1);
if (pid == 0) {
struct tevent_context *ev;
struct sock_daemon_context *sockd;
ev = tevent_context_init(mem_ctx);
assert(ev != NULL);
ret = sock_daemon_setup(mem_ctx, "test4", "file:", "NOTICE",
&test4_funcs, NULL, &sockd);
assert(ret == 0);
ret = sock_daemon_run(ev, sockd, pidfile, false, false, -1);
assert(ret == 0);
exit(0);
}
pid2 = waitpid(pid, &ret, 0);
assert(pid2 == pid);
assert(WEXITSTATUS(ret) == 0);
ret = stat(pidfile, &st);
assert(ret == -1);
ret = stat(sockpath, &st);
assert(ret == -1);
}
/*
* test5
*
* Start daemon, multiple client connects, requests, disconnects
*/
#define TEST5_VALID_CLIENTS 10
#define TEST5_MAX_CLIENTS 100
struct test5_pkt {
uint32_t len;
int data;
};
struct test5_client_state {
int id;
int fd;
bool done;
};
static void test5_client_callback(uint8_t *buf, size_t buflen,
void *private_data)
{
struct test5_client_state *state =
(struct test5_client_state *)private_data;
struct test5_pkt *pkt;
ssize_t n;
int ret;
if (buf == NULL) {
assert(buflen == 0);
ret = 0;
} else {
assert(buflen == sizeof(struct test5_pkt));
pkt = (struct test5_pkt *)buf;
assert(pkt->len == sizeof(struct test5_pkt));
ret = pkt->data;
}
assert(state->fd != -1);
n = write(state->fd, (void *)&ret, sizeof(int));
assert(n == sizeof(int));
state->done = true;
}
static int test5_client(const char *sockpath, int id, pid_t pid_server,
pid_t *client_pid)
{
pid_t pid;
int fd[2];
int ret;
ssize_t n;
ret = pipe(fd);
assert(ret == 0);
pid = fork();
assert(pid != -1);
if (pid == 0) {
struct tevent_context *ev;
struct test5_client_state state;
struct sock_queue *queue;
struct test5_pkt pkt;
int conn;
close(fd[0]);
ev = tevent_context_init(NULL);
assert(ev != NULL);
conn = sock_connect(sockpath);
assert(conn != -1);
state.id = id;
state.fd = fd[1];
state.done = false;
queue = sock_queue_setup(ev, ev, conn,
test5_client_callback, &state);
assert(queue != NULL);
pkt.len = 8;
pkt.data = 0xbaba;
ret = sock_queue_write(queue, (uint8_t *)&pkt,
sizeof(struct test5_pkt));
assert(ret == 0);
while (! state.done) {
tevent_loop_once(ev);
}
close(fd[1]);
state.fd = -1;
while (kill(pid_server, 0) == 0 || errno != ESRCH) {
sleep(1);
}
exit(0);
}
close(fd[1]);
ret = 0;
n = read(fd[0], &ret, sizeof(ret));
if (n == 0) {
fprintf(stderr, "client id %d read 0 bytes\n", id);
}
assert(n == 0 || n == sizeof(ret));
close(fd[0]);
*client_pid = pid;
return ret;
}
struct test5_server_state {
int num_clients;
};
static bool test5_connect(struct sock_client_context *client,
pid_t pid,
void *private_data)
{
struct test5_server_state *state =
(struct test5_server_state *)private_data;
if (state->num_clients == TEST5_VALID_CLIENTS) {
return false;
}
state->num_clients += 1;
assert(state->num_clients <= TEST5_VALID_CLIENTS);
return true;
}
static void test5_disconnect(struct sock_client_context *client,
void *private_data)
{
struct test5_server_state *state =
(struct test5_server_state *)private_data;
state->num_clients -= 1;
assert(state->num_clients >= 0);
}
struct test5_read_state {
struct test5_pkt reply;
};
static void test5_read_done(struct tevent_req *subreq);
static struct tevent_req *test5_read_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct sock_client_context *client,
uint8_t *buf, size_t buflen,
void *private_data)
{
struct test5_server_state *server_state =
(struct test5_server_state *)private_data;
struct tevent_req *req, *subreq;
struct test5_read_state *state;
struct test5_pkt *pkt;
req = tevent_req_create(mem_ctx, &state, struct test5_read_state);
assert(req != NULL);
assert(buflen == sizeof(struct test5_pkt));
pkt = (struct test5_pkt *)buf;
assert(pkt->data == 0xbaba);
state->reply.len = sizeof(struct test5_pkt);
state->reply.data = server_state->num_clients;
subreq = sock_socket_write_send(state, ev, client,
(uint8_t *)&state->reply,
state->reply.len);
assert(subreq != NULL);
tevent_req_set_callback(subreq, test5_read_done, req);
return req;
}
static void test5_read_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
int ret;
bool status;
status = sock_socket_write_recv(subreq, &ret);
TALLOC_FREE(subreq);
if (! status) {
tevent_req_error(req, ret);
return;
}
tevent_req_done(req);
}
static bool test5_read_recv(struct tevent_req *req, int *perr)
{
int ret;
if (tevent_req_is_unix_error(req, &ret)) {
if (perr != NULL) {
*perr = ret;
}
return false;
}
return true;
}
static struct sock_socket_funcs test5_client_funcs = {
.connect = test5_connect,
.disconnect = test5_disconnect,
.read_send = test5_read_send,
.read_recv = test5_read_recv,
};
struct test5_wait_state {
};
static struct tevent_req *test5_wait_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
void *private_data)
{
struct tevent_req *req;
struct test5_wait_state *state;
int fd = *(int *)private_data;
int ret = 1;
ssize_t nwritten;
nwritten = write(fd, &ret, sizeof(ret));
assert(nwritten == sizeof(ret));
close(fd);
req = tevent_req_create(mem_ctx, &state, struct test5_wait_state);
if (req == NULL) {
return NULL;
}
return req;
}
static bool test5_wait_recv(struct tevent_req *req, int *perr)
{
return true;
}
static struct sock_daemon_funcs test5_funcs = {
.wait_send = test5_wait_send,
.wait_recv = test5_wait_recv,
};
static void test5(TALLOC_CTX *mem_ctx, const char *pidfile,
const char *sockpath)
{
pid_t pid_server, pid;
int fd[2], ret, i;
ssize_t n;
pid_t client_pid[TEST5_MAX_CLIENTS];
pid = getpid();
ret = pipe(fd);
assert(ret == 0);
pid_server = fork();
assert(pid_server != -1);
if (pid_server == 0) {
struct tevent_context *ev;
struct sock_daemon_context *sockd;
struct test5_server_state state;
close(fd[0]);
ev = tevent_context_init(mem_ctx);
assert(ev != NULL);
ret = sock_daemon_setup(mem_ctx, "test5", "file:", "NOTICE",
&test5_funcs, &fd[1], &sockd);
assert(ret == 0);
state.num_clients = 0;
ret = sock_daemon_add_unix(sockd, sockpath,
&test5_client_funcs, &state);
assert(ret == 0);
ret = sock_daemon_run(ev, sockd, pidfile, false, false, pid);
assert(ret == EINTR);
exit(0);
}
close(fd[1]);
n = read(fd[0], &ret, sizeof(ret));
assert(n == sizeof(ret));
assert(ret == 1);
close(fd[0]);
for (i=0; i=0; i--) {
kill(client_pid[i], SIGKILL);
pid = wait(&ret);
assert(pid != -1);
}
ret = kill(pid_server, SIGTERM);
assert(ret == 0);
pid = waitpid(pid_server, &ret, 0);
assert(pid == pid_server);
assert(WEXITSTATUS(ret) == 0);
}
/*
* test6
*
* Start daemon, test client connects, requests, replies, disconnects
*/
struct test6_pkt {
uint32_t len;
uint32_t data;
};
struct test6_client_state {
bool done;
};
static void test6_client_callback(uint8_t *buf, size_t buflen,
void *private_data)
{
struct test6_client_state *state =
(struct test6_client_state *)private_data;
struct test6_pkt *pkt;
assert(buflen == sizeof(struct test6_pkt));
pkt = (struct test6_pkt *)buf;
assert(pkt->len == sizeof(struct test6_pkt));
assert(pkt->data == 0xffeeddcc);
state->done = true;
}
static void test6_client(const char *sockpath)
{
struct tevent_context *ev;
struct test6_client_state state;
struct sock_queue *queue;
struct test6_pkt pkt;
int conn, ret;
ev = tevent_context_init(NULL);
assert(ev != NULL);
conn = sock_connect(sockpath);
assert(conn != -1);
state.done = false;
queue = sock_queue_setup(ev, ev, conn,
test6_client_callback, &state);
assert(queue != NULL);
pkt.len = 8;
pkt.data = 0xaabbccdd;
ret = sock_queue_write(queue, (uint8_t *)&pkt,
sizeof(struct test6_pkt));
assert(ret == 0);
while (! state.done) {
tevent_loop_once(ev);
}
talloc_free(ev);
}
struct test6_server_state {
struct sock_daemon_context *sockd;
int fd, done;
};
struct test6_read_state {
struct test6_server_state *server_state;
struct test6_pkt reply;
};
static void test6_read_done(struct tevent_req *subreq);
static struct tevent_req *test6_read_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct sock_client_context *client,
uint8_t *buf, size_t buflen,
void *private_data)
{
struct test6_server_state *server_state =
(struct test6_server_state *)private_data;
struct tevent_req *req, *subreq;
struct test6_read_state *state;
struct test6_pkt *pkt;
req = tevent_req_create(mem_ctx, &state, struct test6_read_state);
assert(req != NULL);
state->server_state = server_state;
assert(buflen == sizeof(struct test6_pkt));
pkt = (struct test6_pkt *)buf;
assert(pkt->data == 0xaabbccdd);
state->reply.len = sizeof(struct test6_pkt);
state->reply.data = 0xffeeddcc;
subreq = sock_socket_write_send(state, ev, client,
(uint8_t *)&state->reply,
state->reply.len);
assert(subreq != NULL);
tevent_req_set_callback(subreq, test6_read_done, req);
return req;
}
static void test6_read_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
struct test6_read_state *state = tevent_req_data(
req, struct test6_read_state);
int ret;
bool status;
status = sock_socket_write_recv(subreq, &ret);
TALLOC_FREE(subreq);
if (! status) {
tevent_req_error(req, ret);
return;
}
state->server_state->done = 1;
tevent_req_done(req);
}
static bool test6_read_recv(struct tevent_req *req, int *perr)
{
int ret;
if (tevent_req_is_unix_error(req, &ret)) {
if (perr != NULL) {
*perr = ret;
}
return false;
}
return true;
}
static struct sock_socket_funcs test6_client_funcs = {
.read_send = test6_read_send,
.read_recv = test6_read_recv,
};
struct test6_wait_state {
struct test6_server_state *server_state;
};
static void test6_wait_done(struct tevent_req *subreq);
static struct tevent_req *test6_wait_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
void *private_data)
{
struct test6_server_state *server_state =
(struct test6_server_state *)private_data;
struct tevent_req *req, *subreq;
struct test6_wait_state *state;
ssize_t nwritten;
int ret = 1;
nwritten = write(server_state->fd, &ret, sizeof(ret));
assert(nwritten == sizeof(ret));
close(server_state->fd);
server_state->fd = -1;
req = tevent_req_create(mem_ctx, &state, struct test6_wait_state);
if (req == NULL) {
return NULL;
}
state->server_state = (struct test6_server_state *)private_data;
subreq = tevent_wakeup_send(state, ev,
tevent_timeval_current_ofs(10,0));
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
tevent_req_set_callback(subreq, test6_wait_done, req);
return req;
}
static void test6_wait_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
struct test6_wait_state *state = tevent_req_data(
req, struct test6_wait_state);
bool status;
status = tevent_wakeup_recv(subreq);
TALLOC_FREE(subreq);
if (! status) {
tevent_req_error(req, EIO);
return;
}
if (state->server_state->done == 0) {
tevent_req_error(req, EIO);
return;
}
tevent_req_done(req);
}
static bool test6_wait_recv(struct tevent_req *req, int *perr)
{
int ret;
if (tevent_req_is_unix_error(req, &ret)) {
if (perr != NULL) {
*perr = ret;
}
return false;
}
return true;
}
static struct sock_daemon_funcs test6_funcs = {
.wait_send = test6_wait_send,
.wait_recv = test6_wait_recv,
};
static void test6(TALLOC_CTX *mem_ctx, const char *pidfile,
const char *sockpath)
{
pid_t pid_server, pid;
int fd[2], ret;
ssize_t n;
pid = getpid();
ret = pipe(fd);
assert(ret == 0);
pid_server = fork();
assert(pid_server != -1);
if (pid_server == 0) {
struct tevent_context *ev;
struct sock_daemon_context *sockd;
struct test6_server_state server_state = { 0 };
close(fd[0]);
ev = tevent_context_init(mem_ctx);
assert(ev != NULL);
server_state.fd = fd[1];
ret = sock_daemon_setup(mem_ctx, "test6", "file:", "NOTICE",
&test6_funcs, &server_state,
&sockd);
assert(ret == 0);
server_state.sockd = sockd;
server_state.done = 0;
ret = sock_daemon_add_unix(sockd, sockpath,
&test6_client_funcs, &server_state);
assert(ret == 0);
ret = sock_daemon_run(ev, sockd, pidfile, false, false, pid);
assert(ret == 0);
exit(0);
}
close(fd[1]);
n = read(fd[0], &ret, sizeof(ret));
assert(n == sizeof(ret));
assert(ret == 1);
close(fd[0]);
test6_client(sockpath);
pid = waitpid(pid_server, &ret, 0);
assert(pid == pid_server);
assert(WEXITSTATUS(ret) == 0);
}
/*
* test7
*
* Start daemon twice, confirm PID file contention
*/
static void test7(TALLOC_CTX *mem_ctx, const char *pidfile,
const char *sockpath)
{
struct sock_daemon_funcs test7_funcs;
struct stat st;
int fd[2];
pid_t pid, pid2;
int ret;
struct tevent_context *ev;
struct sock_daemon_context *sockd;
ssize_t n;
/* Reuse test2 funcs for the startup synchronisation */
test7_funcs = (struct sock_daemon_funcs) {
.startup = test2_startup,
.reconfigure = test2_reconfigure,
.shutdown = test2_shutdown,
};
ret = pipe(fd);
assert(ret == 0);
pid = fork();
assert(pid != -1);
if (pid == 0) {
close(fd[0]);
ev = tevent_context_init(mem_ctx);
assert(ev != NULL);
ret = sock_daemon_setup(mem_ctx, "test7", "file:", "NOTICE",
&test7_funcs, &fd[1], &sockd);
assert(ret == 0);
ret = sock_daemon_run(ev, sockd, pidfile, false, false, -1);
assert(ret == EINTR);
exit(0);
}
close(fd[1]);
n = read(fd[0], &ret, sizeof(ret));
assert(n == sizeof(ret));
assert(ret == 1);
ret = stat(pidfile, &st);
assert(ret == 0);
assert(S_ISREG(st.st_mode));
ev = tevent_context_init(mem_ctx);
assert(ev != NULL);
ret = sock_daemon_setup(mem_ctx, "test7-parent", "file:", "NOTICE",
&test7_funcs, &fd[1], &sockd);
assert(ret == 0);
ret = sock_daemon_run(ev, sockd, pidfile, false, false, -1);
assert(ret == EEXIST);
ret = kill(pid, SIGTERM);
assert(ret == 0);
n = read(fd[0], &ret, sizeof(ret));
assert(n == sizeof(ret));
assert(ret == 3);
pid2 = waitpid(pid, &ret, 0);
assert(pid2 == pid);
assert(WEXITSTATUS(ret) == 0);
close(fd[0]);
}
/*
* test8
*
* Start daemon, confirm that create_session argument works as expected
*/
static void test8(TALLOC_CTX *mem_ctx, const char *pidfile,
const char *sockpath)
{
int fd[2];
pid_t pid, pid2, sid;
int ret;
struct tevent_context *ev;
struct sock_daemon_context *sockd;
ssize_t n;
ret = pipe(fd);
assert(ret == 0);
pid = fork();
assert(pid != -1);
if (pid == 0) {
/* Reuse test2 funcs for the startup synchronisation */
struct sock_daemon_funcs test8_funcs = {
.startup = test2_startup,
};
close(fd[0]);
ev = tevent_context_init(mem_ctx);
assert(ev != NULL);
ret = sock_daemon_setup(mem_ctx, "test8", "file:", "NOTICE",
&test8_funcs, &fd[1], &sockd);
assert(ret == 0);
ret = sock_daemon_run(ev, sockd, pidfile, false, false, -1);
assert(ret == EINTR);
exit(0);
}
close(fd[1]);
n = read(fd[0], &ret, sizeof(ret));
assert(n == sizeof(ret));
assert(ret == 1);
/* create_session false above, so pid != sid */
sid = getsid(pid);
assert(pid != sid);
ret = kill(pid, SIGTERM);
assert(ret == 0);
pid2 = waitpid(pid, &ret, 0);
assert(pid2 == pid);
assert(WEXITSTATUS(ret) == 0);
close(fd[0]);
ret = pipe(fd);
assert(ret == 0);
pid = fork();
assert(pid != -1);
if (pid == 0) {
/* Reuse test2 funcs for the startup synchronisation */
struct sock_daemon_funcs test8_funcs = {
.startup = test2_startup,
};
close(fd[0]);
ev = tevent_context_init(mem_ctx);
assert(ev != NULL);
ret = sock_daemon_setup(mem_ctx, "test8", "file:", "NOTICE",
&test8_funcs, &fd[1], &sockd);
assert(ret == 0);
ret = sock_daemon_run(ev, sockd, pidfile, false, true, -1);
assert(ret == EINTR);
exit(0);
}
close(fd[1]);
n = read(fd[0], &ret, sizeof(ret));
assert(n == sizeof(ret));
assert(ret == 1);
/* create_session true above, so pid == sid */
sid = getsid(pid);
assert(pid == sid);
ret = kill(pid, SIGTERM);
assert(ret == 0);
pid2 = waitpid(pid, &ret, 0);
assert(pid2 == pid);
assert(WEXITSTATUS(ret) == 0);
close(fd[0]);
}
/*
* test9
*
* Confirm that do_fork causes the daemon to be forked as a separate child
*/
static void test9(TALLOC_CTX *mem_ctx, const char *pidfile,
const char *sockpath)
{
int fd[2];
pid_t pid, pid2;
int ret;
struct tevent_context *ev;
struct sock_daemon_context *sockd;
ssize_t n;
int pidfile_fd;
char pidstr[20] = { 0 };
struct stat st;
ret = pipe(fd);
assert(ret == 0);
pid = fork();
assert(pid != -1);
if (pid == 0) {
/* Reuse test2 funcs for the startup synchronisation */
struct sock_daemon_funcs test9_funcs = {
.startup = test2_startup,
};
close(fd[0]);
ev = tevent_context_init(mem_ctx);
assert(ev != NULL);
ret = sock_daemon_setup(mem_ctx, "test9", "file:", "NOTICE",
&test9_funcs, &fd[1], &sockd);
assert(ret == 0);
ret = sock_daemon_run(ev, sockd, pidfile, false, false, -1);
assert(ret == EINTR);
exit(0);
}
close(fd[1]);
n = read(fd[0], &ret, sizeof(ret));
assert(n == sizeof(ret));
assert(ret == 1);
/* do_fork false above, so pid should be active */
ret = kill(pid, 0);
assert(ret == 0);
ret = kill(pid, SIGTERM);
assert(ret == 0);
pid2 = waitpid(pid, &ret, 0);
assert(pid2 == pid);
assert(WEXITSTATUS(ret) == 0);
close(fd[0]);
ret = pipe(fd);
assert(ret == 0);
pid = fork();
assert(pid != -1);
if (pid == 0) {
/* Reuse test2 funcs for the startup synchronisation */
struct sock_daemon_funcs test9_funcs = {
.startup = test2_startup,
.shutdown = test2_shutdown,
};
close(fd[0]);
ev = tevent_context_init(mem_ctx);
assert(ev != NULL);
ret = sock_daemon_setup(mem_ctx, "test9", "file:", "NOTICE",
&test9_funcs, &fd[1], &sockd);
assert(ret == 0);
ret = sock_daemon_run(ev, sockd, pidfile, true, false, -1);
assert(ret == EINTR);
exit(0);
}
close(fd[1]);
n = read(fd[0], &ret, sizeof(ret));
assert(n == sizeof(ret));
assert(ret == 1);
/* do_fork true above, so pid should have exited */
pid2 = waitpid(pid, &ret, 0);
assert(pid2 == pid);
assert(WEXITSTATUS(ret) == 0);
pidfile_fd = open(pidfile, O_RDONLY, 0644);
assert(pidfile_fd != -1);
n = read(pidfile_fd, pidstr, sizeof(pidstr)-1);
assert(n != -1);
pid2 = (pid_t)atoi(pidstr);
assert(pid != pid2);
close(pidfile_fd);
ret = kill(pid2, SIGTERM);
assert(ret == 0);
n = read(fd[0], &ret, sizeof(ret));
assert(n == sizeof(ret));
assert(ret == 3);
/*
* pid2 isn't our child, so can't call waitpid(). kill(pid2, 0)
* is unreliable - pid2 may have been recycled. Above indicates
* that the shutdown function was called, so just do 1 final
* check to see if pidfile has been removed.
*/
ret = stat(sockpath, &st);
assert(ret == -1);
close(fd[0]);
}
static void test10_shutdown(void *private_data)
{
int fd = *(int *)private_data;
int ret = 3;
ssize_t nwritten;
nwritten = write(fd, &ret, sizeof(ret));
assert(nwritten == sizeof(ret));
}
struct test10_wait_state {
};
static void test10_wait_done(struct tevent_req *subreq);
static struct tevent_req *test10_wait_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
void *private_data)
{
int fd = *(int *)private_data;
struct tevent_req *req, *subreq;
struct test10_wait_state *state;
size_t nwritten;
int ret = 1;
req = tevent_req_create(mem_ctx, &state, struct test10_wait_state);
if (req == NULL) {
return NULL;
}
subreq = tevent_wakeup_send(state, ev,
tevent_timeval_current_ofs(10, 0));
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
tevent_req_set_callback(subreq, test10_wait_done, req);
nwritten = write(fd, &ret, sizeof(ret));
assert(nwritten == sizeof(ret));
return req;
}
static void test10_wait_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
bool status;
status = tevent_wakeup_recv(subreq);
if (! status) {
tevent_req_error(req, EIO);
return;
}
tevent_req_done(req);
}
static bool test10_wait_recv(struct tevent_req *req, int *perr)
{
int ret;
if (tevent_req_is_unix_error(req, &ret)) {
if (perr != NULL) {
*perr = ret;
}
return false;
}
return true;
}
static struct sock_daemon_funcs test10_funcs = {
.shutdown = test10_shutdown,
.wait_send = test10_wait_send,
.wait_recv = test10_wait_recv,
};
/*
* test10
*
* Confirm that the daemon starts successfully if there is a stale socket
*/
static void test10(TALLOC_CTX *mem_ctx, const char *pidfile,
const char *sockpath)
{
struct stat st;
int fd[2];
pid_t pid, pid2;
int ret;
ssize_t n;
int pidfile_fd;
char pidstr[20] = { 0 };
ret = pipe(fd);
assert(ret == 0);
pid = fork();
assert(pid != -1);
if (pid == 0) {
struct tevent_context *ev;
struct sock_daemon_context *sockd;
close(fd[0]);
ev = tevent_context_init(mem_ctx);
assert(ev != NULL);
ret = sock_daemon_setup(mem_ctx, "test10", "file:", "NOTICE",
&test10_funcs, &fd[1], &sockd);
assert(ret == 0);
ret = sock_daemon_add_unix(sockd, sockpath,
&dummy_socket_funcs, NULL);
assert(ret == 0);
ret = sock_daemon_run(ev, sockd, pidfile, false, false, -1);
assert(ret == EINTR);
exit(0);
}
close(fd[1]);
n = read(fd[0], &ret, sizeof(ret));
assert(n == sizeof(ret));
assert(ret == 1);
/* KILL will leave PID file and socket behind */
ret = kill (pid, SIGKILL);
assert(ret == 0);
pid2 = waitpid(pid, &ret, 0);
assert(pid2 == pid);
assert(WEXITSTATUS(ret) == 0);
ret = stat(sockpath, &st);
assert(ret == 0);
close(fd[0]);
ret = pipe(fd);
assert(ret == 0);
pid = fork();
assert(pid != -1);
if (pid == 0) {
struct tevent_context *ev;
struct sock_daemon_context *sockd;
close(fd[0]);
ev = tevent_context_init(mem_ctx);
assert(ev != NULL);
ret = sock_daemon_setup(mem_ctx, "test10", "file:", "NOTICE",
&test10_funcs, &fd[1], &sockd);
assert(ret == 0);
ret = sock_daemon_add_unix(sockd, sockpath,
&dummy_socket_funcs, NULL);
assert(ret == 0);
ret = sock_daemon_run(ev, sockd, pidfile, false, false, -1);
assert(ret == EINTR);
exit(0);
}
close(fd[1]);
n = read(fd[0], &ret, sizeof(ret));
assert(n == sizeof(ret));
assert(ret == 1);
pidfile_fd = open(pidfile, O_RDONLY, 0644);
assert(pidfile_fd != -1);
n = read(pidfile_fd, pidstr, sizeof(pidstr)-1);
assert(n != -1);
pid2 = (pid_t)atoi(pidstr);
assert(pid == pid2);
close(pidfile_fd);
ret = kill(pid, SIGTERM);
assert(ret == 0);
n = read(fd[0], &ret, sizeof(ret));
assert(n == sizeof(ret));
assert(ret == 3);
pid2 = waitpid(pid, &ret, 0);
assert(pid2 == pid);
assert(WEXITSTATUS(ret) == 0);
close(fd[0]);
ret = stat(pidfile, &st);
assert(ret == -1);
ret = stat(sockpath, &st);
assert(ret == -1);
}
int main(int argc, const char **argv)
{
TALLOC_CTX *mem_ctx;
const char *pidfile, *sockpath;
int num;
if (argc != 4) {
fprintf(stderr, "%s \n", argv[0]);
exit(1);
}
pidfile = argv[1];
sockpath = argv[2];
num = atoi(argv[3]);
mem_ctx = talloc_new(NULL);
assert(mem_ctx != NULL);
switch (num) {
case 1:
test1(mem_ctx, pidfile, sockpath);
break;
case 2:
test2(mem_ctx, pidfile, sockpath);
break;
case 3:
test3(mem_ctx, pidfile, sockpath);
break;
case 4:
test4(mem_ctx, pidfile, sockpath);
break;
case 5:
test5(mem_ctx, pidfile, sockpath);
break;
case 6:
test6(mem_ctx, pidfile, sockpath);
break;
case 7:
test7(mem_ctx, pidfile, sockpath);
break;
case 8:
test8(mem_ctx, pidfile, sockpath);
break;
case 9:
test9(mem_ctx, pidfile, sockpath);
break;
case 10:
test10(mem_ctx, pidfile, sockpath);
break;
default:
fprintf(stderr, "Unknown test number %d\n", num);
}
return 0;
}