1
/* -*- mode: c; coding: utf-8; after-save-hook: (lambda () (let* ((find-build-directory (lambda (try-directory &optional base-directory) (let ((base-directory (or base-directory try-directory))) (cond ((equal try-directory "/") base-directory) ((file-readable-p (concat (file-name-as-directory try-directory) "Makefile")) try-directory) ((funcall find-build-directory (directory-file-name (file-name-directory try-directory)) base-directory)))))) (build-directory (funcall find-build-directory (buffer-file-name))) (local-build-directory (if (fboundp 'file-local-name) (file-local-name build-directory) (or (file-remote-p build-directory 'localname) build-directory))) (command (file-relative-name (file-name-sans-extension (buffer-file-name)) build-directory))) (pcase (progn (if (get-buffer "*Test*") (kill-buffer "*Test*")) (process-file-shell-command (let ((qbdir (shell-quote-argument local-build-directory)) (qcmd (shell-quote-argument command))) (format "cd %s && CFLAGS=-Werror make --silent %s && %s --test --verbose" qbdir qcmd qcmd)) nil "*Test*")) (0 (let ((w (get-buffer-window "*Test*"))) (if w (delete-window w)))) (_ (with-current-buffer "*Test*" (compilation-mode) (cd-absolute build-directory)) (display-buffer "*Test*" '(display-buffer-in-side-window)))))); -*- */
3
* Mandos password agent - Simple password agent to run Mandos client
5
* Copyright © 2019-2020 Teddy Hogeborn
6
* Copyright © 2019-2020 Björn Påhlsson
8
* This file is part of Mandos.
10
* Mandos is free software: you can redistribute it and/or modify it
11
* under the terms of the GNU General Public License as published by
12
* the Free Software Foundation, either version 3 of the License, or
13
* (at your option) any later version.
15
* Mandos is distributed in the hope that it will be useful, but
16
* WITHOUT ANY WARRANTY; without even the implied warranty of
17
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18
* General Public License for more details.
20
* You should have received a copy of the GNU General Public License
21
* along with Mandos. If not, see <http://www.gnu.org/licenses/>.
23
* Contact the authors at <mandos@recompile.se>.
27
#include <inttypes.h> /* uintmax_t, PRIuMAX, PRIdMAX,
28
intmax_t, uint32_t, SCNx32,
30
#include <stddef.h> /* size_t */
31
#include <sys/types.h> /* pid_t, uid_t, gid_t, getuid(),
33
#include <stdbool.h> /* bool, true, false */
34
#include <signal.h> /* struct sigaction, sigset_t,
35
sigemptyset(), sigaddset(),
36
SIGCHLD, pthread_sigmask(),
37
SIG_BLOCK, SIG_SETMASK, SA_RESTART,
38
SA_NOCLDSTOP, sigfillset(), kill(),
39
SIGTERM, sigdelset(), SIGKILL,
40
NSIG, sigismember(), SA_ONSTACK,
41
SIG_DFL, SIG_IGN, SIGINT, SIGQUIT,
42
SIGHUP, SIGSTOP, SIG_UNBLOCK */
43
#include <stdlib.h> /* EXIT_SUCCESS, EXIT_FAILURE,
44
malloc(), free(), strtoumax(),
45
realloc(), setenv(), calloc(),
46
mkdtemp(), mkostemp() */
47
#include <iso646.h> /* not, or, and, xor */
48
#include <error.h> /* error() */
49
#include <sysexits.h> /* EX_USAGE, EX_OSERR, EX_OSFILE */
50
#include <errno.h> /* errno, error_t, EACCES,
51
ENAMETOOLONG, ENOENT, ENOTDIR,
52
ENOMEM, EEXIST, ECHILD, EPERM,
53
EAGAIN, EINTR, ENOBUFS, EADDRINUSE,
54
ECONNREFUSED, ECONNRESET,
55
ETOOMANYREFS, EMSGSIZE, EBADF,
57
#include <string.h> /* strdup(), memcpy(),
58
explicit_bzero(), memset(),
59
strcmp(), strlen(), strncpy(),
60
memcmp(), basename() */
61
#include <argz.h> /* argz_create(), argz_count(),
62
argz_extract(), argz_next(),
64
#include <sys/epoll.h> /* epoll_create1(), EPOLL_CLOEXEC,
65
epoll_ctl(), EPOLL_CTL_ADD,
66
struct epoll_event, EPOLLIN,
69
#include <time.h> /* struct timespec, clock_gettime(),
71
#include <argp.h> /* struct argp_option, OPTION_HIDDEN,
72
OPTION_ALIAS, struct argp_state,
73
ARGP_ERR_UNKNOWN, ARGP_KEY_ARGS,
74
struct argp, argp_parse(),
76
#include <stdint.h> /* SIZE_MAX */
77
#include <unistd.h> /* uid_t, gid_t, close(), pipe2(),
78
fork(), _exit(), dup2(),
79
STDOUT_FILENO, setresgid(),
80
setresuid(), execv(), ssize_t,
81
read(), dup3(), getuid(), dup(),
82
STDERR_FILENO, pause(), write(),
83
rmdir(), unlink(), getpid() */
84
#include <sys/mman.h> /* munlock(), mlock() */
85
#include <fcntl.h> /* O_CLOEXEC, O_NONBLOCK, fcntl(),
86
F_GETFD, F_GETFL, FD_CLOEXEC,
87
open(), O_WRONLY, O_NOCTTY,
88
O_RDONLY, O_NOFOLLOW */
89
#include <sys/wait.h> /* waitpid(), WNOHANG, WIFEXITED(),
91
#include <limits.h> /* PIPE_BUF, NAME_MAX, INT_MAX */
92
#include <sys/inotify.h> /* inotify_init1(), IN_NONBLOCK,
93
IN_CLOEXEC, inotify_add_watch(),
94
IN_CLOSE_WRITE, IN_MOVED_TO,
95
IN_MOVED_FROM, IN_DELETE,
96
IN_EXCL_UNLINK, IN_ONLYDIR,
97
struct inotify_event */
98
#include <fnmatch.h> /* fnmatch(), FNM_FILE_NAME */
99
#include <stdio.h> /* asprintf(), FILE, stderr, fopen(),
100
fclose(), getline(), sscanf(),
101
feof(), ferror(), rename(),
102
fdopen(), fprintf(), fscanf() */
103
#include <glib.h> /* GKeyFile, g_key_file_free(), g_key_file_new(),
104
GError, g_key_file_load_from_file(),
105
G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
106
g_key_file_get_string(), guint64,
107
g_key_file_get_uint64(),
108
G_KEY_FILE_ERROR_KEY_NOT_FOUND, gconstpointer,
109
g_assert_true(), g_assert_nonnull(),
110
g_assert_null(), g_assert_false(),
111
g_assert_cmpint(), g_assert_cmpuint(),
112
g_test_skip(), g_assert_cmpstr(),
113
g_test_init(), g_test_add(), g_test_run(),
114
GOptionContext, g_option_context_new(),
115
g_option_context_set_help_enabled(), FALSE,
116
g_option_context_set_ignore_unknown_options(),
117
gboolean, GOptionEntry, G_OPTION_ARG_NONE,
118
g_option_context_add_main_entries(),
119
g_option_context_parse(),
120
g_option_context_free(), g_error() */
121
#include <sys/un.h> /* struct sockaddr_un, SUN_LEN */
122
#include <sys/socket.h> /* AF_LOCAL, socket(), PF_LOCAL,
123
SOCK_DGRAM, SOCK_NONBLOCK,
124
SOCK_CLOEXEC, connect(),
125
struct sockaddr, socklen_t,
126
shutdown(), SHUT_RD, send(),
127
MSG_NOSIGNAL, bind(), recv(),
129
#include <glob.h> /* globfree(), glob_t, glob(),
130
GLOB_ERR, GLOB_NOSORT, GLOB_MARK,
131
GLOB_ABORTED, GLOB_NOMATCH,
134
/* End of includes */
136
/* Start of declarations of private types and functions */
138
/* microseconds of CLOCK_MONOTONIC absolute time; 0 means unset */
139
typedef uintmax_t mono_microsecs;
141
/* "task_queue" - A queue of tasks to be run */
143
struct task_struct *tasks; /* Tasks in this queue */
144
size_t length; /* Number of tasks */
145
/* Memory allocated for "tasks", in bytes */
147
/* Time when this queue should be run, at the latest */
148
mono_microsecs next_run;
149
} __attribute__((designated_init)) task_queue;
151
/* "task_func" - A function type for task functions
153
I.e. functions for the code which runs when a task is run, all have
155
typedef void (task_func) (const struct task_struct,
157
__attribute__((nonnull));
159
/* "buffer" - A data buffer for a growing array of bytes
161
Used for the "password" variable */
166
} __attribute__((designated_init)) buffer;
168
/* "string_set" - A set type which can contain strings
170
Used by the "cancelled_filenames" variable */
172
char *argz; /* Do not access these except in */
173
size_t argz_len; /* the string_set_* functions */
174
} __attribute__((designated_init)) string_set;
176
/* "task_context" - local variables for tasks
178
This data structure distinguishes between different tasks which are
179
using the same function. This data structure is passed to every
180
task function when each task is run.
182
Note that not every task uses every struct member. */
183
typedef struct task_struct {
184
task_func *const func; /* The function run by this task */
185
char *const question_filename; /* The question file */
186
const pid_t pid; /* Mandos client process ID */
187
const int epoll_fd; /* The epoll set file descriptor */
188
bool *const quit_now; /* Set to true on fatal errors */
189
const int fd; /* General purpose file descriptor */
190
bool *const mandos_client_exited; /* Set true when client exits */
191
buffer *const password; /* As read from client process */
192
bool *const password_is_read; /* "password" is done growing */
193
char *filename; /* General purpose file name */
194
/* A set of strings of all the file names of questions which have
195
been cancelled for any reason; tasks pertaining to these question
196
files should not be run */
197
string_set *const cancelled_filenames;
198
const mono_microsecs notafter; /* "NotAfter" from question file */
199
/* Updated before each queue run; is compared with queue.next_run */
200
const mono_microsecs *const current_time;
201
} __attribute__((designated_init)) task_context;
203
/* Declare all our functions here so we can define them in any order
204
below. Note: test functions are *not* declared here, they are
205
declared in the test section. */
206
__attribute__((warn_unused_result))
207
static bool should_only_run_tests(int *, char **[]);
208
__attribute__((warn_unused_result, cold))
209
static bool run_tests(int, char *[]);
210
static void handle_sigchld(__attribute__((unused)) int sig){}
211
__attribute__((warn_unused_result, malloc))
212
task_queue *create_queue(void);
213
__attribute__((nonnull, warn_unused_result))
214
bool add_to_queue(task_queue *const, const task_context);
215
__attribute__((nonnull))
216
void cleanup_task(const task_context *const);
217
__attribute__((nonnull))
218
void cleanup_queue(task_queue *const *const);
219
__attribute__((pure, nonnull, warn_unused_result))
220
bool queue_has_question(const task_queue *const);
221
__attribute__((nonnull))
222
void cleanup_close(const int *const);
223
__attribute__((nonnull))
224
void cleanup_string(char *const *const);
225
__attribute__((nonnull))
226
void cleanup_buffer(buffer *const);
227
__attribute__((pure, nonnull, warn_unused_result))
228
bool string_set_contains(const string_set, const char *const);
229
__attribute__((nonnull, warn_unused_result))
230
bool string_set_add(string_set *const, const char *const);
231
__attribute__((nonnull))
232
void string_set_clear(string_set *);
233
void string_set_swap(string_set *const, string_set *const);
234
__attribute__((nonnull, warn_unused_result))
235
bool start_mandos_client(task_queue *const, const int, bool *const,
236
bool *const, buffer *const, bool *const,
237
const struct sigaction *const,
238
const sigset_t, const char *const,
239
const uid_t, const gid_t,
240
const char *const *const);
241
__attribute__((nonnull))
242
task_func wait_for_mandos_client_exit;
243
__attribute__((nonnull))
244
task_func read_mandos_client_output;
245
__attribute__((warn_unused_result))
246
bool add_inotify_dir_watch(task_queue *const, const int, bool *const,
247
buffer *const, const char *const,
248
string_set *, const mono_microsecs *const,
249
bool *const, bool *const);
250
__attribute__((nonnull))
251
task_func read_inotify_event;
252
__attribute__((nonnull))
253
task_func open_and_parse_question;
254
__attribute__((nonnull))
255
task_func cancel_old_question;
256
__attribute__((nonnull))
257
task_func connect_question_socket;
258
__attribute__((nonnull))
259
task_func send_password_to_socket;
260
__attribute__((warn_unused_result))
261
bool add_existing_questions(task_queue *const, const int,
262
buffer *const, string_set *,
263
const mono_microsecs *const,
264
bool *const, bool *const,
266
__attribute__((nonnull, warn_unused_result))
267
bool wait_for_event(const int, const mono_microsecs,
268
const mono_microsecs);
269
bool run_queue(task_queue **const, string_set *const, bool *const);
270
bool clear_all_fds_from_epoll_set(const int);
271
mono_microsecs get_current_time(void);
272
__attribute__((nonnull, warn_unused_result))
273
bool setup_signal_handler(struct sigaction *const);
274
__attribute__((nonnull))
275
bool restore_signal_handler(const struct sigaction *const);
276
__attribute__((nonnull, warn_unused_result))
277
bool block_sigchld(sigset_t *const);
278
__attribute__((nonnull))
279
bool restore_sigmask(const sigset_t *const);
280
__attribute__((nonnull))
281
bool parse_arguments(int, char *[], const bool, char **, char **,
282
uid_t *const , gid_t *const, char **, size_t *);
284
/* End of declarations of private types and functions */
286
/* Start of "main" section; this section LACKS TESTS!
288
Code here should be as simple as possible. */
290
/* These are required to be global by Argp */
291
const char *argp_program_version = "password-agent " VERSION;
292
const char *argp_program_bug_address = "<mandos@recompile.se>";
294
int main(int argc, char *argv[]){
296
/* If the --test option is passed, skip all normal operations and
297
instead only run the run_tests() function, which also does all
298
its own option parsing, so we don't have to do anything here. */
299
if(should_only_run_tests(&argc, &argv)){
300
if(run_tests(argc, argv)){
301
return EXIT_SUCCESS; /* All tests successful */
303
return EXIT_FAILURE; /* Some test(s) failed */
306
__attribute__((cleanup(cleanup_string)))
307
char *agent_directory = NULL;
309
__attribute__((cleanup(cleanup_string)))
310
char *helper_directory = NULL;
315
__attribute__((cleanup(cleanup_string)))
316
char *mandos_argz = NULL;
317
size_t mandos_argz_length = 0;
319
if(not parse_arguments(argc, argv, true, &agent_directory,
320
&helper_directory, &user, &group,
321
&mandos_argz, &mandos_argz_length)){
322
/* This should never happen, since "true" is passed as the third
323
argument to parse_arguments() above, which should make
324
argp_parse() call exit() if any parsing error occurs. */
325
error(EX_USAGE, errno, "Failed to parse arguments");
328
const char default_agent_directory[] = "/run/systemd/ask-password";
329
const char default_helper_directory[]
330
= "/lib/mandos/plugin-helpers";
331
const char *const default_argv[]
332
= {"/lib/mandos/plugins.d/mandos-client", NULL };
334
/* Set variables to default values if unset */
335
if(agent_directory == NULL){
336
agent_directory = strdup(default_agent_directory);
337
if(agent_directory == NULL){
338
error(EX_OSERR, errno, "Failed strdup()");
341
if(helper_directory == NULL){
342
helper_directory = strdup(default_helper_directory);
343
if(helper_directory == NULL){
344
error(EX_OSERR, errno, "Failed strdup()");
348
user = 65534; /* nobody */
351
group = 65534; /* nogroup */
353
/* If parse_opt did not create an argz vector, create one with
355
if(mandos_argz == NULL){
357
#pragma GCC diagnostic push
358
/* argz_create() takes a non-const argv for some unknown reason -
359
argz_create() isn't modifying the strings, just copying them.
360
Therefore, this cast to non-const should be safe. */
361
#pragma GCC diagnostic ignored "-Wcast-qual"
363
errno = argz_create((char *const *)default_argv, &mandos_argz,
364
&mandos_argz_length);
366
#pragma GCC diagnostic pop
369
error(EX_OSERR, errno, "Failed argz_create()");
372
/* Use argz vector to create a normal argv, usable by execv() */
374
char **mandos_argv = malloc((argz_count(mandos_argz,
376
+ 1) * sizeof(char *));
377
if(mandos_argv == NULL){
378
error_t saved_errno = errno;
380
error(EX_OSERR, saved_errno, "Failed malloc()");
382
argz_extract(mandos_argz, mandos_argz_length, mandos_argv);
384
sigset_t orig_sigmask;
385
if(not block_sigchld(&orig_sigmask)){
389
struct sigaction old_sigchld_action;
390
if(not setup_signal_handler(&old_sigchld_action)){
394
mono_microsecs current_time = 0;
396
bool mandos_client_exited = false;
397
bool quit_now = false;
398
__attribute__((cleanup(cleanup_close)))
399
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
401
error(EX_OSERR, errno, "Failed to create epoll set fd");
403
__attribute__((cleanup(cleanup_queue)))
404
task_queue *queue = create_queue();
406
error(EX_OSERR, errno, "Failed to create task queue");
409
__attribute__((cleanup(cleanup_buffer)))
410
buffer password = {};
411
bool password_is_read = false;
413
__attribute__((cleanup(string_set_clear)))
414
string_set cancelled_filenames = {};
416
/* Add tasks to queue */
417
if(not start_mandos_client(queue, epoll_fd, &mandos_client_exited,
418
&quit_now, &password, &password_is_read,
419
&old_sigchld_action, orig_sigmask,
420
helper_directory, user, group,
421
(const char *const *)mandos_argv)){
422
return EX_OSERR; /* Error has already been printed */
424
/* These variables were only for start_mandos_client() and are not
429
if(not add_inotify_dir_watch(queue, epoll_fd, &quit_now, &password,
430
agent_directory, &cancelled_filenames,
431
¤t_time, &mandos_client_exited,
433
switch(errno){ /* Error has already been printed */
443
if(not add_existing_questions(queue, epoll_fd, &password,
444
&cancelled_filenames, ¤t_time,
445
&mandos_client_exited,
446
&password_is_read, agent_directory)){
447
return EXIT_FAILURE; /* Error has already been printed */
452
current_time = get_current_time();
453
if(not wait_for_event(epoll_fd, queue->next_run, current_time)){
454
const error_t saved_errno = errno;
455
error(EXIT_FAILURE, saved_errno, "Failure while waiting for"
459
current_time = get_current_time();
460
if(not run_queue(&queue, &cancelled_filenames, &quit_now)){
461
const error_t saved_errno = errno;
462
error(EXIT_FAILURE, saved_errno, "Failure while running queue");
465
/* When no tasks about questions are left in the queue, break out
466
of the loop (and implicitly exit the program) */
467
} while(queue_has_question(queue));
469
restore_signal_handler(&old_sigchld_action);
470
restore_sigmask(&orig_sigmask);
475
__attribute__((warn_unused_result))
476
mono_microsecs get_current_time(void){
477
struct timespec currtime;
478
if(clock_gettime(CLOCK_MONOTONIC, &currtime) != 0){
479
error(0, errno, "Failed to get current time");
482
return ((mono_microsecs)currtime.tv_sec * 1000000) /* seconds */
483
+ ((mono_microsecs)currtime.tv_nsec / 1000); /* nanoseconds */
486
/* End of "main" section */
488
/* Start of regular code section; ALL this code has tests */
490
__attribute__((nonnull))
491
bool parse_arguments(int argc, char *argv[], const bool exit_failure,
492
char **agent_directory, char **helper_directory,
493
uid_t *const user, gid_t *const group,
494
char **mandos_argz, size_t *mandos_argz_length){
496
const struct argp_option options[] = {
497
{ .name="agent-directory",.key='d', .arg="DIRECTORY",
498
.doc="Systemd password agent directory" },
499
{ .name="helper-directory",.key=128, .arg="DIRECTORY",
500
.doc="Mandos Client password helper directory" },
501
{ .name="plugin-helper-dir", .key=129, /* From plugin-runner */
502
.flags=OPTION_HIDDEN | OPTION_ALIAS },
503
{ .name="user", .key='u', .arg="USERID",
504
.doc="User ID the Mandos Client will use as its unprivileged"
506
{ .name="userid", .key=130, /* From plugin--runner */
507
.flags=OPTION_HIDDEN | OPTION_ALIAS },
508
{ .name="group", .key='g', .arg="GROUPID",
509
.doc="Group ID the Mandos Client will use as its unprivileged"
511
{ .name="groupid", .key=131, /* From plugin--runner */
512
.flags=OPTION_HIDDEN | OPTION_ALIAS },
513
{ .name="test", .key=255, /* See should_only_run_tests() */
514
.doc="Skip normal operation, and only run self-tests. See"
515
" --test --help.", .group=10, },
519
__attribute__((nonnull(3)))
520
error_t parse_opt(int key, char *arg, struct argp_state *state){
523
case 'd': /* --agent-directory */
524
*agent_directory = strdup(arg);
526
case 128: /* --helper-directory */
527
case 129: /* --plugin-helper-dir */
528
*helper_directory = strdup(arg);
530
case 'u': /* --user */
531
case 130: /* --userid */
534
uintmax_t tmp_id = 0;
536
tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
537
if(errno != 0 or tmp == arg or *tmp != '\0'
538
or tmp_id != (uid_t)tmp_id or (uid_t)tmp_id == 0){
539
return ARGP_ERR_UNKNOWN;
541
*user = (uid_t)tmp_id;
545
case 'g': /* --group */
546
case 131: /* --groupid */
549
uintmax_t tmp_id = 0;
551
tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
552
if(errno != 0 or tmp == arg or *tmp != '\0'
553
or tmp_id != (gid_t)tmp_id or (gid_t)tmp_id == 0){
554
return ARGP_ERR_UNKNOWN;
556
*group = (gid_t)tmp_id;
561
/* Copy arguments into argz vector */
562
return argz_create(state->argv + state->next, mandos_argz,
565
return ARGP_ERR_UNKNOWN;
570
const struct argp argp = {
573
.args_doc="[MANDOS_CLIENT [OPTION...]]\n--test",
574
.doc = "Mandos password agent -- runs Mandos client as a"
575
" systemd password agent",
578
errno = argp_parse(&argp, argc, argv,
579
exit_failure ? 0 : ARGP_NO_EXIT, NULL, NULL);
584
__attribute__((nonnull, warn_unused_result))
585
bool block_sigchld(sigset_t *const orig_sigmask){
586
sigset_t sigchld_sigmask;
587
if(sigemptyset(&sigchld_sigmask) < 0){
588
error(0, errno, "Failed to empty signal set");
591
if(sigaddset(&sigchld_sigmask, SIGCHLD) < 0){
592
error(0, errno, "Failed to add SIGCHLD to signal set");
595
if(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask, orig_sigmask) != 0){
596
error(0, errno, "Failed to block SIGCHLD signal");
602
__attribute__((nonnull, warn_unused_result, const))
603
bool restore_sigmask(const sigset_t *const orig_sigmask){
604
if(pthread_sigmask(SIG_SETMASK, orig_sigmask, NULL) != 0){
605
error(0, errno, "Failed to restore blocked signals");
611
__attribute__((nonnull, warn_unused_result))
612
bool setup_signal_handler(struct sigaction *const old_sigchld_action){
613
struct sigaction sigchld_action = {
614
.sa_handler=handle_sigchld,
615
.sa_flags=SA_RESTART | SA_NOCLDSTOP,
617
/* Set all signals in "sa_mask" struct member; this makes all
618
signals automatically blocked during signal handler */
619
if(sigfillset(&sigchld_action.sa_mask) != 0){
620
error(0, errno, "Failed to do sigfillset()");
623
if(sigaction(SIGCHLD, &sigchld_action, old_sigchld_action) != 0){
624
error(0, errno, "Failed to set SIGCHLD signal handler");
630
__attribute__((nonnull, warn_unused_result))
631
bool restore_signal_handler(const struct sigaction *const
633
if(sigaction(SIGCHLD, old_sigchld_action, NULL) != 0){
634
error(0, errno, "Failed to restore signal handler");
640
__attribute__((warn_unused_result, malloc))
641
task_queue *create_queue(void){
642
task_queue *queue = malloc(sizeof(task_queue));
646
queue->allocated = 0;
652
__attribute__((nonnull, warn_unused_result))
653
bool add_to_queue(task_queue *const queue, const task_context task){
654
if((queue->length + 1) > (SIZE_MAX / sizeof(task_context))){
656
error(0, ENOMEM, "Failed to allocate %" PRIuMAX
657
" tasks for queue->tasks", (uintmax_t)(queue->length + 1));
661
const size_t needed_size = sizeof(task_context)*(queue->length + 1);
662
if(needed_size > (queue->allocated)){
663
task_context *const new_tasks = realloc(queue->tasks,
665
if(new_tasks == NULL){
666
error(0, errno, "Failed to allocate %" PRIuMAX
667
" bytes for queue->tasks", (uintmax_t)needed_size);
670
queue->tasks = new_tasks;
671
queue->allocated = needed_size;
673
/* Using memcpy here is necessary because doing */
674
/* queue->tasks[queue->length++] = task; */
675
/* would violate const-ness of task members */
676
memcpy(&(queue->tasks[queue->length++]), &task,
677
sizeof(task_context));
681
__attribute__((nonnull))
682
void cleanup_task(const task_context *const task){
683
const error_t saved_errno = errno;
684
/* free and close all task data */
685
free(task->question_filename);
686
if(task->filename != task->question_filename){
687
free(task->filename);
690
kill(task->pid, SIGTERM);
698
__attribute__((nonnull))
699
void free_queue(task_queue *const queue){
704
__attribute__((nonnull))
705
void cleanup_queue(task_queue *const *const queue){
709
for(size_t i = 0; i < (*queue)->length; i++){
710
const task_context *const task = ((*queue)->tasks)+i;
716
__attribute__((pure, nonnull, warn_unused_result))
717
bool queue_has_question(const task_queue *const queue){
718
for(size_t i=0; i < queue->length; i++){
719
if(queue->tasks[i].question_filename != NULL){
726
__attribute__((nonnull))
727
void cleanup_close(const int *const fd){
728
const error_t saved_errno = errno;
733
__attribute__((nonnull))
734
void cleanup_string(char *const *const ptr){
738
__attribute__((nonnull))
739
void cleanup_buffer(buffer *buf){
740
if(buf->allocated > 0){
741
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
742
explicit_bzero(buf->data, buf->allocated);
744
memset(buf->data, '\0', buf->allocated);
747
if(buf->data != NULL){
748
if(munlock(buf->data, buf->allocated) != 0){
749
error(0, errno, "Failed to unlock memory of old buffer");
758
__attribute__((pure, nonnull, warn_unused_result))
759
bool string_set_contains(const string_set set, const char *const str){
760
for(const char *s = set.argz; s != NULL and set.argz_len > 0;
761
s = argz_next(set.argz, set.argz_len, s)){
762
if(strcmp(s, str) == 0){
769
__attribute__((nonnull, warn_unused_result))
770
bool string_set_add(string_set *const set, const char *const str){
771
if(string_set_contains(*set, str)){
774
error_t error = argz_add(&set->argz, &set->argz_len, str);
782
__attribute__((nonnull))
783
void string_set_clear(string_set *set){
789
__attribute__((nonnull))
790
void string_set_swap(string_set *const set1, string_set *const set2){
791
/* Swap contents of two string sets */
793
char *const tmp_argz = set1->argz;
794
set1->argz = set2->argz;
795
set2->argz = tmp_argz;
798
const size_t tmp_argz_len = set1->argz_len;
799
set1->argz_len = set2->argz_len;
800
set2->argz_len = tmp_argz_len;
804
__attribute__((nonnull, warn_unused_result))
805
bool start_mandos_client(task_queue *const queue,
807
bool *const mandos_client_exited,
808
bool *const quit_now, buffer *const password,
809
bool *const password_is_read,
810
const struct sigaction *const
811
old_sigchld_action, const sigset_t sigmask,
812
const char *const helper_directory,
813
const uid_t user, const gid_t group,
814
const char *const *const argv){
816
if(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK) != 0){
817
error(0, errno, "Failed to pipe2(..., O_CLOEXEC | O_NONBLOCK)");
821
const pid_t pid = fork();
823
if(not restore_signal_handler(old_sigchld_action)){
826
if(not restore_sigmask(&sigmask)){
829
if(close(pipefds[0]) != 0){
830
error(0, errno, "Failed to close() parent pipe fd");
833
if(dup2(pipefds[1], STDOUT_FILENO) == -1){
834
error(0, errno, "Failed to dup2() pipe fd to stdout");
837
if(close(pipefds[1]) != 0){
838
error(0, errno, "Failed to close() old child pipe fd");
841
if(setenv("MANDOSPLUGINHELPERDIR", helper_directory, 1) != 0){
842
error(0, errno, "Failed to setenv(\"MANDOSPLUGINHELPERDIR\","
843
" \"%s\", 1)", helper_directory);
846
if(group != 0 and setresgid(group, 0, 0) == -1){
847
error(0, errno, "Failed to setresgid(-1, %" PRIuMAX ", %"
848
PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
851
if(user != 0 and setresuid(user, 0, 0) == -1){
852
error(0, errno, "Failed to setresuid(-1, %" PRIuMAX ", %"
853
PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
857
#pragma GCC diagnostic push
858
/* For historical reasons, the "argv" argument to execv() is not
859
const, but it is safe to override this. */
860
#pragma GCC diagnostic ignored "-Wcast-qual"
862
execv(argv[0], (char **)argv);
864
#pragma GCC diagnostic pop
866
error(0, errno, "execv(\"%s\", ...) failed", argv[0]);
872
error(0, errno, "Failed to fork()");
877
if(not add_to_queue(queue, (task_context){
878
.func=wait_for_mandos_client_exit,
880
.mandos_client_exited=mandos_client_exited,
883
error(0, errno, "Failed to add wait_for_mandos_client to queue");
888
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipefds[0],
889
&(struct epoll_event)
890
{ .events=EPOLLIN | EPOLLRDHUP });
891
if(ret != 0 and errno != EEXIST){
892
error(0, errno, "Failed to add file descriptor to epoll set");
897
return add_to_queue(queue, (task_context){
898
.func=read_mandos_client_output,
903
.password_is_read=password_is_read,
907
__attribute__((nonnull))
908
void wait_for_mandos_client_exit(const task_context task,
909
task_queue *const queue){
910
const pid_t pid = task.pid;
911
bool *const mandos_client_exited = task.mandos_client_exited;
912
bool *const quit_now = task.quit_now;
915
switch(waitpid(pid, &status, WNOHANG)){
916
case 0: /* Not exited yet */
917
if(not add_to_queue(queue, task)){
918
error(0, errno, "Failed to add myself to queue");
923
error(0, errno, "waitpid(%" PRIdMAX ") failed", (intmax_t)pid);
929
default: /* Has exited */
930
*mandos_client_exited = true;
931
if((not WIFEXITED(status))
932
or (WEXITSTATUS(status) != EXIT_SUCCESS)){
933
error(0, 0, "Mandos client failed or was killed");
939
__attribute__((nonnull))
940
void read_mandos_client_output(const task_context task,
941
task_queue *const queue){
942
buffer *const password = task.password;
943
bool *const quit_now = task.quit_now;
944
bool *const password_is_read = task.password_is_read;
945
const int fd = task.fd;
946
const int epoll_fd = task.epoll_fd;
948
const size_t new_potential_size = (password->length + PIPE_BUF);
949
if(password->allocated < new_potential_size){
950
char *const new_buffer = calloc(new_potential_size, 1);
951
if(new_buffer == NULL){
952
error(0, errno, "Failed to allocate %" PRIuMAX
953
" bytes for password", (uintmax_t)new_potential_size);
958
if(mlock(new_buffer, new_potential_size) != 0){
959
/* Warn but do not treat as fatal error */
960
if(errno != EPERM and errno != ENOMEM){
961
error(0, errno, "Failed to lock memory for password");
964
if(password->length > 0){
965
memcpy(new_buffer, password->data, password->length);
966
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
967
explicit_bzero(password->data, password->allocated);
969
memset(password->data, '\0', password->allocated);
972
if(password->data != NULL){
973
if(munlock(password->data, password->allocated) != 0){
974
error(0, errno, "Failed to unlock memory of old buffer");
976
free(password->data);
978
password->data = new_buffer;
979
password->allocated = new_potential_size;
982
const ssize_t read_length = read(fd, password->data
983
+ password->length, PIPE_BUF);
985
if(read_length == 0){ /* EOF */
986
*password_is_read = true;
990
if(read_length < 0 and errno != EAGAIN){ /* Actual error */
991
error(0, errno, "Failed to read password from Mandos client");
996
if(read_length > 0){ /* Data has been read */
997
password->length += (size_t)read_length;
1000
/* Either data was read, or EAGAIN was indicated, meaning no data
1003
/* Re-add the fd to the epoll set */
1004
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1005
&(struct epoll_event)
1006
{ .events=EPOLLIN | EPOLLRDHUP });
1007
if(ret != 0 and errno != EEXIST){
1008
error(0, errno, "Failed to re-add file descriptor to epoll set");
1014
/* Re-add myself to the queue */
1015
if(not add_to_queue(queue, task)){
1016
error(0, errno, "Failed to add myself to queue");
1022
__attribute__((nonnull, warn_unused_result))
1023
bool add_inotify_dir_watch(task_queue *const queue,
1024
const int epoll_fd, bool *const quit_now,
1025
buffer *const password,
1026
const char *const dir,
1027
string_set *cancelled_filenames,
1028
const mono_microsecs *const current_time,
1029
bool *const mandos_client_exited,
1030
bool *const password_is_read){
1031
const int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
1033
error(0, errno, "Failed to create inotify instance");
1037
if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO
1038
| IN_MOVED_FROM| IN_DELETE | IN_EXCL_UNLINK
1041
error(0, errno, "Failed to create inotify watch on %s", dir);
1045
/* Add the inotify fd to the epoll set */
1046
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1047
&(struct epoll_event)
1048
{ .events=EPOLLIN | EPOLLRDHUP });
1049
if(ret != 0 and errno != EEXIST){
1050
error(0, errno, "Failed to add file descriptor to epoll set");
1055
const task_context read_inotify_event_task = {
1056
.func=read_inotify_event,
1061
.filename=strdup(dir),
1062
.cancelled_filenames=cancelled_filenames,
1063
.current_time=current_time,
1064
.mandos_client_exited=mandos_client_exited,
1065
.password_is_read=password_is_read,
1067
if(read_inotify_event_task.filename == NULL){
1068
error(0, errno, "Failed to strdup(\"%s\")", dir);
1073
return add_to_queue(queue, read_inotify_event_task);
1076
__attribute__((nonnull))
1077
void read_inotify_event(const task_context task,
1078
task_queue *const queue){
1079
const int fd = task.fd;
1080
const int epoll_fd = task.epoll_fd;
1081
char *const filename = task.filename;
1082
bool *quit_now = task.quit_now;
1083
buffer *const password = task.password;
1084
string_set *const cancelled_filenames = task.cancelled_filenames;
1085
const mono_microsecs *const current_time = task.current_time;
1086
bool *const mandos_client_exited = task.mandos_client_exited;
1087
bool *const password_is_read = task.password_is_read;
1089
/* "sufficient to read at least one event." - inotify(7) */
1090
const size_t ievent_size = (sizeof(struct inotify_event)
1093
struct inotify_event event;
1094
char name_buffer[NAME_MAX + 1];
1096
struct inotify_event *const ievent = &ievent_buffer.event;
1098
const ssize_t read_length = read(fd, ievent, ievent_size);
1099
if(read_length == 0){ /* EOF */
1100
error(0, 0, "Got EOF from inotify fd for directory %s", filename);
1102
cleanup_task(&task);
1105
if(read_length < 0 and errno != EAGAIN){ /* Actual error */
1106
error(0, errno, "Failed to read from inotify fd for directory %s",
1109
cleanup_task(&task);
1112
if(read_length > 0 /* Data has been read */
1113
and fnmatch("ask.*", ievent->name, FNM_FILE_NAME) == 0){
1114
char *question_filename = NULL;
1115
const ssize_t question_filename_length
1116
= asprintf(&question_filename, "%s/%s", filename, ievent->name);
1117
if(question_filename_length < 0){
1118
error(0, errno, "Failed to create file name from directory name"
1119
" %s and file name %s", filename, ievent->name);
1121
if(ievent->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)){
1122
if(not add_to_queue(queue, (task_context){
1123
.func=open_and_parse_question,
1125
.question_filename=question_filename,
1126
.filename=question_filename,
1128
.cancelled_filenames=cancelled_filenames,
1129
.current_time=current_time,
1130
.mandos_client_exited=mandos_client_exited,
1131
.password_is_read=password_is_read,
1133
error(0, errno, "Failed to add open_and_parse_question task"
1134
" for file name %s to queue", filename);
1136
/* Force the added task (open_and_parse_question) to run
1138
queue->next_run = 1;
1140
} else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
1141
if(not string_set_add(cancelled_filenames,
1142
question_filename)){
1143
error(0, errno, "Could not add question %s to"
1144
" cancelled_questions", question_filename);
1146
free(question_filename);
1147
cleanup_task(&task);
1150
free(question_filename);
1155
/* Either data was read, or EAGAIN was indicated, meaning no data
1158
/* Re-add myself to the queue */
1159
if(not add_to_queue(queue, task)){
1160
error(0, errno, "Failed to re-add read_inotify_event(%s) to"
1161
" queue", filename);
1163
cleanup_task(&task);
1167
/* Re-add the fd to the epoll set */
1168
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1169
&(struct epoll_event)
1170
{ .events=EPOLLIN | EPOLLRDHUP });
1171
if(ret != 0 and errno != EEXIST){
1172
error(0, errno, "Failed to re-add inotify file descriptor %d for"
1173
" directory %s to epoll set", fd, filename);
1174
/* Force the added task (read_inotify_event) to run again, at most
1175
one second from now */
1176
if((queue->next_run == 0)
1177
or (queue->next_run > (*current_time + 1000000))){
1178
queue->next_run = *current_time + 1000000;
1183
__attribute__((nonnull))
1184
void open_and_parse_question(const task_context task,
1185
task_queue *const queue){
1186
__attribute__((cleanup(cleanup_string)))
1187
char *question_filename = task.question_filename;
1188
const int epoll_fd = task.epoll_fd;
1189
buffer *const password = task.password;
1190
string_set *const cancelled_filenames = task.cancelled_filenames;
1191
const mono_microsecs *const current_time = task.current_time;
1192
bool *const mandos_client_exited = task.mandos_client_exited;
1193
bool *const password_is_read = task.password_is_read;
1195
/* We use the GLib "Key-value file parser" functions to parse the
1196
question file. See <https://systemd.io/PASSWORD_AGENTS/> for
1197
specification of contents */
1198
__attribute__((nonnull))
1199
void cleanup_g_key_file(GKeyFile **key_file){
1200
if(*key_file != NULL){
1201
g_key_file_free(*key_file);
1205
__attribute__((cleanup(cleanup_g_key_file)))
1206
GKeyFile *key_file = g_key_file_new();
1207
if(key_file == NULL){
1208
error(0, errno, "Failed g_key_file_new() for \"%s\"",
1212
GError *glib_error = NULL;
1213
if(g_key_file_load_from_file(key_file, question_filename,
1214
G_KEY_FILE_NONE, &glib_error) != TRUE){
1215
/* If a file was removed, we should ignore it, so */
1216
/* only show error message if file actually existed */
1217
if(glib_error->code != G_FILE_ERROR_NOENT){
1218
error(0, 0, "Failed to load question data from file \"%s\": %s",
1219
question_filename, glib_error->message);
1224
__attribute__((cleanup(cleanup_string)))
1225
char *socket_name = g_key_file_get_string(key_file, "Ask",
1228
if(socket_name == NULL){
1229
error(0, 0, "Question file \"%s\" did not contain \"Socket\": %s",
1230
question_filename, glib_error->message);
1234
if(strlen(socket_name) == 0){
1235
error(0, 0, "Question file \"%s\" had empty \"Socket\" value",
1240
const guint64 pid = g_key_file_get_uint64(key_file, "Ask", "PID",
1242
if(glib_error != NULL){
1243
error(0, 0, "Question file \"%s\" contained bad \"PID\": %s",
1244
question_filename, glib_error->message);
1248
if((pid != (guint64)((pid_t)pid))
1249
or (kill((pid_t)pid, 0) != 0)){
1250
error(0, 0, "PID %" PRIuMAX " in question file \"%s\" is bad or"
1251
" does not exist", (uintmax_t)pid, question_filename);
1255
guint64 notafter = g_key_file_get_uint64(key_file, "Ask",
1256
"NotAfter", &glib_error);
1257
if(glib_error != NULL){
1258
if(glib_error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND){
1259
error(0, 0, "Question file \"%s\" contained bad \"NotAfter\":"
1260
" %s", question_filename, glib_error->message);
1265
if(queue->next_run == 0 or (queue->next_run > notafter)){
1266
queue->next_run = notafter;
1268
if(*current_time >= notafter){
1273
const task_context connect_question_socket_task = {
1274
.func=connect_question_socket,
1275
.question_filename=strdup(question_filename),
1278
.filename=strdup(socket_name),
1279
.cancelled_filenames=task.cancelled_filenames,
1280
.mandos_client_exited=mandos_client_exited,
1281
.password_is_read=password_is_read,
1282
.current_time=current_time,
1284
if(connect_question_socket_task.question_filename == NULL
1285
or connect_question_socket_task.filename == NULL
1286
or not add_to_queue(queue, connect_question_socket_task)){
1287
error(0, errno, "Failed to add connect_question_socket for socket"
1288
" %s (from \"%s\") to queue", socket_name,
1290
cleanup_task(&connect_question_socket_task);
1293
/* Force the added task (connect_question_socket) to run
1295
queue->next_run = 1;
1298
char *const dup_filename = strdup(question_filename);
1299
const task_context cancel_old_question_task = {
1300
.func=cancel_old_question,
1301
.question_filename=dup_filename,
1303
.filename=dup_filename,
1304
.cancelled_filenames=cancelled_filenames,
1305
.current_time=current_time,
1307
if(cancel_old_question_task.question_filename == NULL
1308
or not add_to_queue(queue, cancel_old_question_task)){
1309
error(0, errno, "Failed to add cancel_old_question for file "
1310
"\"%s\" to queue", question_filename);
1311
cleanup_task(&cancel_old_question_task);
1317
__attribute__((nonnull))
1318
void cancel_old_question(const task_context task,
1319
task_queue *const queue){
1320
char *const question_filename = task.question_filename;
1321
string_set *const cancelled_filenames = task.cancelled_filenames;
1322
const mono_microsecs notafter = task.notafter;
1323
const mono_microsecs *const current_time = task.current_time;
1325
if(*current_time >= notafter){
1326
if(not string_set_add(cancelled_filenames, question_filename)){
1327
error(0, errno, "Failed to cancel question for file %s",
1330
cleanup_task(&task);
1334
if(not add_to_queue(queue, task)){
1335
error(0, errno, "Failed to add cancel_old_question for file "
1336
"%s to queue", question_filename);
1337
cleanup_task(&task);
1341
if((queue->next_run == 0) or (queue->next_run > notafter)){
1342
queue->next_run = notafter;
1346
__attribute__((nonnull))
1347
void connect_question_socket(const task_context task,
1348
task_queue *const queue){
1349
char *const question_filename = task.question_filename;
1350
char *const filename = task.filename;
1351
const int epoll_fd = task.epoll_fd;
1352
buffer *const password = task.password;
1353
string_set *const cancelled_filenames = task.cancelled_filenames;
1354
bool *const mandos_client_exited = task.mandos_client_exited;
1355
bool *const password_is_read = task.password_is_read;
1356
const mono_microsecs *const current_time = task.current_time;
1358
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
1360
if(sizeof(sock_name.sun_path) <= strlen(filename)){
1361
error(0, 0, "Socket filename is larger than"
1362
" sizeof(sockaddr_un.sun_path); %" PRIuMAX ": \"%s\"",
1363
(uintmax_t)sizeof(sock_name.sun_path), filename);
1364
if(not string_set_add(cancelled_filenames, question_filename)){
1365
error(0, errno, "Failed to cancel question for file %s",
1368
cleanup_task(&task);
1372
const int fd = socket(PF_LOCAL, SOCK_DGRAM
1373
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
1376
"Failed to create socket(PF_LOCAL, SOCK_DGRAM, 0)");
1377
if(not add_to_queue(queue, task)){
1378
error(0, errno, "Failed to add connect_question_socket for file"
1379
" \"%s\" and socket \"%s\" to queue", question_filename,
1381
cleanup_task(&task);
1383
/* Force the added task (connect_question_socket) to run
1385
queue->next_run = 1;
1390
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
1391
if(connect(fd, (struct sockaddr *)&sock_name,
1392
(socklen_t)SUN_LEN(&sock_name)) != 0){
1393
error(0, errno, "Failed to connect socket to \"%s\"", filename);
1394
if(not add_to_queue(queue, task)){
1395
error(0, errno, "Failed to add connect_question_socket for file"
1396
" \"%s\" and socket \"%s\" to queue", question_filename,
1398
cleanup_task(&task);
1400
/* Force the added task (connect_question_socket) to run again,
1401
at most one second from now */
1402
if((queue->next_run == 0)
1403
or (queue->next_run > (*current_time + 1000000))){
1404
queue->next_run = *current_time + 1000000;
1410
/* Not necessary, but we can try, and merely warn on failure */
1411
if(shutdown(fd, SHUT_RD) != 0){
1412
error(0, errno, "Failed to shutdown reading from socket \"%s\"",
1416
/* Add the fd to the epoll set */
1417
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1418
&(struct epoll_event){ .events=EPOLLOUT })
1420
error(0, errno, "Failed to add inotify file descriptor %d for"
1421
" socket %s to epoll set", fd, filename);
1422
if(not add_to_queue(queue, task)){
1423
error(0, errno, "Failed to add connect_question_socket for file"
1424
" \"%s\" and socket \"%s\" to queue", question_filename,
1426
cleanup_task(&task);
1428
/* Force the added task (connect_question_socket) to run again,
1429
at most one second from now */
1430
if((queue->next_run == 0)
1431
or (queue->next_run > (*current_time + 1000000))){
1432
queue->next_run = *current_time + 1000000;
1438
/* add task send_password_to_socket to queue */
1439
const task_context send_password_to_socket_task = {
1440
.func=send_password_to_socket,
1441
.question_filename=question_filename,
1446
.cancelled_filenames=cancelled_filenames,
1447
.mandos_client_exited=mandos_client_exited,
1448
.password_is_read=password_is_read,
1449
.current_time=current_time,
1452
if(not add_to_queue(queue, send_password_to_socket_task)){
1453
error(0, errno, "Failed to add send_password_to_socket for"
1454
" file \"%s\" and socket \"%s\" to queue",
1455
question_filename, filename);
1456
cleanup_task(&send_password_to_socket_task);
1460
__attribute__((nonnull))
1461
void send_password_to_socket(const task_context task,
1462
task_queue *const queue){
1463
char *const question_filename=task.question_filename;
1464
char *const filename=task.filename;
1465
const int epoll_fd=task.epoll_fd;
1466
const int fd=task.fd;
1467
buffer *const password=task.password;
1468
string_set *const cancelled_filenames=task.cancelled_filenames;
1469
bool *const mandos_client_exited = task.mandos_client_exited;
1470
bool *const password_is_read = task.password_is_read;
1471
const mono_microsecs *const current_time = task.current_time;
1473
if(*mandos_client_exited and *password_is_read){
1475
const size_t send_buffer_length = password->length + 2;
1476
char *send_buffer = malloc(send_buffer_length);
1477
if(send_buffer == NULL){
1478
error(0, errno, "Failed to allocate send_buffer");
1480
if(mlock(send_buffer, send_buffer_length) != 0){
1481
/* Warn but do not treat as fatal error */
1482
if(errno != EPERM and errno != ENOMEM){
1483
error(0, errno, "Failed to lock memory for password"
1487
/* “[…] send a single datagram to the socket consisting of the
1488
password string either prefixed with "+" or with "-"
1489
depending on whether the password entry was successful or
1490
not. You may but don't have to include a final NUL byte in
1493
— <https://systemd.io/PASSWORD_AGENTS/> (Tue, 15 Sep 2020
1496
send_buffer[0] = '+'; /* Prefix with "+" */
1497
/* Always add an extra NUL */
1498
send_buffer[password->length + 1] = '\0';
1499
if(password->length > 0){
1500
memcpy(send_buffer + 1, password->data, password->length);
1503
ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1505
const error_t saved_errno = (ssret < 0) ? errno : 0;
1506
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1507
explicit_bzero(send_buffer, send_buffer_length);
1509
memset(send_buffer, '\0', send_buffer_length);
1511
if(munlock(send_buffer, send_buffer_length) != 0){
1512
error(0, errno, "Failed to unlock memory of send buffer");
1515
if(ssret < 0 or ssret < (ssize_t)send_buffer_length){
1516
switch(saved_errno){
1529
error(0, saved_errno, "Password of size %" PRIuMAX
1530
" is too big", (uintmax_t)password->length);
1534
__attribute__((fallthrough));
1537
if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1538
error(0, 0, "Password only partially sent to socket %s: %"
1539
PRIuMAX " out of %" PRIuMAX " bytes sent", filename,
1540
(uintmax_t)ssret, (uintmax_t)send_buffer_length);
1545
__attribute__((fallthrough));
1548
error(0, saved_errno, "Failed to send() to socket %s",
1550
if(not string_set_add(cancelled_filenames,
1551
question_filename)){
1552
error(0, errno, "Failed to cancel question for file %s",
1555
cleanup_task(&task);
1560
cleanup_task(&task);
1566
/* We failed or are not ready yet; retry later */
1568
if(not add_to_queue(queue, task)){
1569
error(0, errno, "Failed to add send_password_to_socket for"
1570
" file %s and socket %s to queue", question_filename,
1572
cleanup_task(&task);
1575
/* Add the fd to the epoll set */
1576
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1577
&(struct epoll_event){ .events=EPOLLOUT })
1579
error(0, errno, "Failed to add socket file descriptor %d for"
1580
" socket %s to epoll set", fd, filename);
1581
/* Force the added task (send_password_to_socket) to run again, at
1582
most one second from now */
1583
if((queue->next_run == 0)
1584
or (queue->next_run > (*current_time + 1000000))){
1585
queue->next_run = *current_time + 1000000;
1590
__attribute__((warn_unused_result))
1591
bool add_existing_questions(task_queue *const queue,
1593
buffer *const password,
1594
string_set *cancelled_filenames,
1595
const mono_microsecs *const current_time,
1596
bool *const mandos_client_exited,
1597
bool *const password_is_read,
1598
const char *const dirname){
1599
__attribute__((cleanup(cleanup_string)))
1600
char *dir_pattern = NULL;
1601
const int ret = asprintf(&dir_pattern, "%s/ask.*", dirname);
1602
if(ret < 0 or dir_pattern == NULL){
1603
error(0, errno, "Could not create glob pattern for directory %s",
1607
__attribute__((cleanup(globfree)))
1608
glob_t question_filenames = {};
1609
switch(glob(dir_pattern, GLOB_ERR | GLOB_NOSORT | GLOB_MARK,
1610
NULL, &question_filenames)){
1613
error(0, errno, "Failed to open directory %s", dirname);
1616
error(0, errno, "There are no question files in %s", dirname);
1619
error(0, errno, "Could not allocate memory for question file"
1620
" names in %s", dirname);
1624
__attribute__((fallthrough));
1627
for(size_t i = 0; i < question_filenames.gl_pathc; i++){
1628
char *const question_filename = strdup(question_filenames
1630
const task_context task = {
1631
.func=open_and_parse_question,
1633
.question_filename=question_filename,
1634
.filename=question_filename,
1636
.cancelled_filenames=cancelled_filenames,
1637
.current_time=current_time,
1638
.mandos_client_exited=mandos_client_exited,
1639
.password_is_read=password_is_read,
1642
if(question_filename == NULL
1643
or not add_to_queue(queue, task)){
1644
error(0, errno, "Failed to add open_and_parse_question for"
1645
" file %s to queue",
1646
question_filenames.gl_pathv[i]);
1647
free(question_filename);
1649
queue->next_run = 1;
1656
__attribute__((nonnull, warn_unused_result))
1657
bool wait_for_event(const int epoll_fd,
1658
const mono_microsecs queue_next_run,
1659
const mono_microsecs current_time){
1660
__attribute__((const))
1661
int milliseconds_to_wait(const mono_microsecs currtime,
1662
const mono_microsecs nextrun){
1663
if(currtime >= nextrun){
1666
const uintmax_t wait_time_ms = (nextrun - currtime) / 1000;
1667
if(wait_time_ms > (uintmax_t)INT_MAX){
1670
return (int)wait_time_ms;
1673
const int wait_time_ms = milliseconds_to_wait(current_time,
1676
/* Prepare unblocking of SIGCHLD during epoll_pwait */
1677
sigset_t temporary_unblocked_sigmask;
1678
/* Get current signal mask */
1679
if(pthread_sigmask(-1, NULL, &temporary_unblocked_sigmask) != 0){
1682
/* Remove SIGCHLD from the signal mask */
1683
if(sigdelset(&temporary_unblocked_sigmask, SIGCHLD) != 0){
1686
struct epoll_event events[8]; /* Ignored */
1687
int ret = epoll_pwait(epoll_fd, events,
1688
sizeof(events) / sizeof(struct epoll_event),
1689
queue_next_run == 0 ? -1 : (int)wait_time_ms,
1690
&temporary_unblocked_sigmask);
1691
if(ret < 0 and errno != EINTR){
1692
error(0, errno, "Failed epoll_pwait(epfd=%d, ..., timeout=%d,"
1694
queue_next_run == 0 ? -1 : (int)wait_time_ms);
1697
return clear_all_fds_from_epoll_set(epoll_fd);
1700
bool clear_all_fds_from_epoll_set(const int epoll_fd){
1701
/* Create a new empty epoll set */
1702
__attribute__((cleanup(cleanup_close)))
1703
const int new_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
1704
if(new_epoll_fd < 0){
1707
/* dup3() the new epoll set fd over the old one, replacing it */
1708
if(dup3(new_epoll_fd, epoll_fd, O_CLOEXEC) < 0){
1714
__attribute__((nonnull, warn_unused_result))
1715
bool run_queue(task_queue **const queue,
1716
string_set *const cancelled_filenames,
1717
bool *const quit_now){
1719
task_queue *new_queue = create_queue();
1720
if(new_queue == NULL){
1724
__attribute__((cleanup(string_set_clear)))
1725
string_set old_cancelled_filenames = {};
1726
string_set_swap(cancelled_filenames, &old_cancelled_filenames);
1728
/* Declare i outside the for loop, since we might need i after the
1729
loop in case we aborted in the middle */
1731
for(i=0; i < (*queue)->length and not *quit_now; i++){
1732
task_context *const task = &((*queue)->tasks[i]);
1733
const char *const question_filename = task->question_filename;
1734
/* Skip any task referencing a cancelled question filename */
1735
if(question_filename != NULL
1736
and string_set_contains(old_cancelled_filenames,
1737
question_filename)){
1741
task->func(*task, new_queue);
1745
/* we might be in the middle of the queue, so clean up any
1746
remaining tasks in the current queue */
1747
for(; i < (*queue)->length; i++){
1748
cleanup_task(&((*queue)->tasks[i]));
1762
/* End of regular code section */
1764
/* Start of tests section; here are the tests for the above code */
1766
/* This "fixture" data structure is used by the test setup and
1767
teardown functions */
1769
struct sigaction orig_sigaction;
1770
sigset_t orig_sigmask;
1773
static void test_setup(test_fixture *fixture,
1774
__attribute__((unused))
1775
gconstpointer user_data){
1776
g_assert_true(setup_signal_handler(&fixture->orig_sigaction));
1777
g_assert_true(block_sigchld(&fixture->orig_sigmask));
1780
static void test_teardown(test_fixture *fixture,
1781
__attribute__((unused))
1782
gconstpointer user_data){
1783
g_assert_true(restore_signal_handler(&fixture->orig_sigaction));
1784
g_assert_true(restore_sigmask(&fixture->orig_sigmask));
1787
/* Utility function used by tests to search queue for matching task */
1788
__attribute__((pure, nonnull, warn_unused_result))
1789
static task_context *find_matching_task(const task_queue *const queue,
1790
const task_context task){
1791
/* The argument "task" structure is a pattern to match; 0 in any
1792
member means any value matches, otherwise the value must match.
1793
The filename strings are compared by strcmp(), not by pointer. */
1794
for(size_t i = 0; i < queue->length; i++){
1795
task_context *const current_task = queue->tasks+i;
1796
/* Check all members of task_context, if set to a non-zero value.
1797
If a member does not match, continue to next task in queue */
1799
/* task_func *const func */
1800
if(task.func != NULL and current_task->func != task.func){
1803
/* char *const question_filename; */
1804
if(task.question_filename != NULL
1805
and (current_task->question_filename == NULL
1806
or strcmp(current_task->question_filename,
1807
task.question_filename) != 0)){
1810
/* const pid_t pid; */
1811
if(task.pid != 0 and current_task->pid != task.pid){
1814
/* const int epoll_fd; */
1815
if(task.epoll_fd != 0
1816
and current_task->epoll_fd != task.epoll_fd){
1819
/* bool *const quit_now; */
1820
if(task.quit_now != NULL
1821
and current_task->quit_now != task.quit_now){
1825
if(task.fd != 0 and current_task->fd != task.fd){
1828
/* bool *const mandos_client_exited; */
1829
if(task.mandos_client_exited != NULL
1830
and current_task->mandos_client_exited
1831
!= task.mandos_client_exited){
1834
/* buffer *const password; */
1835
if(task.password != NULL
1836
and current_task->password != task.password){
1839
/* bool *const password_is_read; */
1840
if(task.password_is_read != NULL
1841
and current_task->password_is_read != task.password_is_read){
1844
/* char *filename; */
1845
if(task.filename != NULL
1846
and (current_task->filename == NULL
1847
or strcmp(current_task->filename, task.filename) != 0)){
1850
/* string_set *const cancelled_filenames; */
1851
if(task.cancelled_filenames != NULL
1852
and current_task->cancelled_filenames
1853
!= task.cancelled_filenames){
1856
/* const mono_microsecs notafter; */
1857
if(task.notafter != 0
1858
and current_task->notafter != task.notafter){
1861
/* const mono_microsecs *const current_time; */
1862
if(task.current_time != NULL
1863
and current_task->current_time != task.current_time){
1866
/* Current task matches all members; return it */
1867
return current_task;
1869
/* No task in queue matches passed pattern task */
1873
static void test_create_queue(__attribute__((unused))
1874
test_fixture *fixture,
1875
__attribute__((unused))
1876
gconstpointer user_data){
1877
__attribute__((cleanup(cleanup_queue)))
1878
task_queue *const queue = create_queue();
1879
g_assert_nonnull(queue);
1880
g_assert_null(queue->tasks);
1881
g_assert_true(queue->length == 0);
1882
g_assert_true(queue->next_run == 0);
1885
static task_func dummy_func;
1887
static void test_add_to_queue(__attribute__((unused))
1888
test_fixture *fixture,
1889
__attribute__((unused))
1890
gconstpointer user_data){
1891
__attribute__((cleanup(cleanup_queue)))
1892
task_queue *queue = create_queue();
1893
g_assert_nonnull(queue);
1895
g_assert_true(add_to_queue(queue,
1896
(task_context){ .func=dummy_func }));
1897
g_assert_true(queue->length == 1);
1898
g_assert_nonnull(queue->tasks);
1899
g_assert_true(queue->tasks[0].func == dummy_func);
1902
static void test_add_to_queue_overflow(__attribute__((unused))
1903
test_fixture *fixture,
1904
__attribute__((unused))
1905
gconstpointer user_data){
1906
__attribute__((cleanup(cleanup_queue)))
1907
task_queue *queue = create_queue();
1908
g_assert_nonnull(queue);
1909
g_assert_true(queue->length == 0);
1910
queue->length = SIZE_MAX / sizeof(task_context); /* fake max size */
1912
FILE *real_stderr = stderr;
1913
FILE *devnull = fopen("/dev/null", "we");
1914
g_assert_nonnull(devnull);
1916
const bool ret = add_to_queue(queue,
1917
(task_context){ .func=dummy_func });
1918
g_assert_true(errno == ENOMEM);
1919
g_assert_false(ret);
1920
stderr = real_stderr;
1921
g_assert_cmpint(fclose(devnull), ==, 0);
1922
queue->length = 0; /* Restore real size */
1925
static void dummy_func(__attribute__((unused))
1926
const task_context task,
1927
__attribute__((unused))
1928
task_queue *const queue){
1931
static void test_queue_has_question_empty(__attribute__((unused))
1932
test_fixture *fixture,
1933
__attribute__((unused))
1934
gconstpointer user_data){
1935
__attribute__((cleanup(cleanup_queue)))
1936
task_queue *queue = create_queue();
1937
g_assert_nonnull(queue);
1938
g_assert_false(queue_has_question(queue));
1941
static void test_queue_has_question_false(__attribute__((unused))
1942
test_fixture *fixture,
1943
__attribute__((unused))
1944
gconstpointer user_data){
1945
__attribute__((cleanup(cleanup_queue)))
1946
task_queue *queue = create_queue();
1947
g_assert_nonnull(queue);
1948
g_assert_true(add_to_queue(queue,
1949
(task_context){ .func=dummy_func }));
1950
g_assert_false(queue_has_question(queue));
1953
static void test_queue_has_question_true(__attribute__((unused))
1954
test_fixture *fixture,
1955
__attribute__((unused))
1956
gconstpointer user_data){
1957
__attribute__((cleanup(cleanup_queue)))
1958
task_queue *queue = create_queue();
1959
g_assert_nonnull(queue);
1960
char *const question_filename
1961
= strdup("/nonexistent/question_filename");
1962
g_assert_nonnull(question_filename);
1963
task_context task = {
1965
.question_filename=question_filename,
1967
g_assert_true(add_to_queue(queue, task));
1968
g_assert_true(queue_has_question(queue));
1971
static void test_queue_has_question_false2(__attribute__((unused))
1972
test_fixture *fixture,
1973
__attribute__((unused))
1974
gconstpointer user_data){
1975
__attribute__((cleanup(cleanup_queue)))
1976
task_queue *queue = create_queue();
1977
g_assert_nonnull(queue);
1978
task_context task = { .func=dummy_func };
1979
g_assert_true(add_to_queue(queue, task));
1980
g_assert_true(add_to_queue(queue, task));
1981
g_assert_cmpint((int)queue->length, ==, 2);
1982
g_assert_false(queue_has_question(queue));
1985
static void test_queue_has_question_true2(__attribute__((unused))
1986
test_fixture *fixture,
1987
__attribute__((unused))
1988
gconstpointer user_data){
1989
__attribute__((cleanup(cleanup_queue)))
1990
task_queue *queue = create_queue();
1991
g_assert_nonnull(queue);
1992
task_context task1 = { .func=dummy_func };
1993
g_assert_true(add_to_queue(queue, task1));
1994
char *const question_filename
1995
= strdup("/nonexistent/question_filename");
1996
g_assert_nonnull(question_filename);
1997
task_context task2 = {
1999
.question_filename=question_filename,
2001
g_assert_true(add_to_queue(queue, task2));
2002
g_assert_cmpint((int)queue->length, ==, 2);
2003
g_assert_true(queue_has_question(queue));
2006
static void test_cleanup_buffer(__attribute__((unused))
2007
test_fixture *fixture,
2008
__attribute__((unused))
2009
gconstpointer user_data){
2012
const size_t buffersize = 10;
2014
buf.data = malloc(buffersize);
2015
g_assert_nonnull(buf.data);
2016
if(mlock(buf.data, buffersize) != 0){
2017
g_assert_true(errno == EPERM or errno == ENOMEM);
2020
cleanup_buffer(&buf);
2021
g_assert_null(buf.data);
2025
void test_string_set_new_set_contains_nothing(__attribute__((unused))
2026
test_fixture *fixture,
2027
__attribute__((unused))
2030
__attribute__((cleanup(string_set_clear)))
2031
string_set set = {};
2032
g_assert_false(string_set_contains(set, "")); /* Empty string */
2033
g_assert_false(string_set_contains(set, "test_string"));
2037
test_string_set_with_added_string_contains_it(__attribute__((unused))
2038
test_fixture *fixture,
2039
__attribute__((unused))
2042
__attribute__((cleanup(string_set_clear)))
2043
string_set set = {};
2044
g_assert_true(string_set_add(&set, "test_string"));
2045
g_assert_true(string_set_contains(set, "test_string"));
2049
test_string_set_cleared_does_not_contain_str(__attribute__((unused))
2050
test_fixture *fixture,
2051
__attribute__((unused))
2052
gconstpointer user_data){
2053
__attribute__((cleanup(string_set_clear)))
2054
string_set set = {};
2055
g_assert_true(string_set_add(&set, "test_string"));
2056
string_set_clear(&set);
2057
g_assert_false(string_set_contains(set, "test_string"));
2061
void test_string_set_swap_one_with_empty(__attribute__((unused))
2062
test_fixture *fixture,
2063
__attribute__((unused))
2064
gconstpointer user_data){
2065
__attribute__((cleanup(string_set_clear)))
2066
string_set set1 = {};
2067
__attribute__((cleanup(string_set_clear)))
2068
string_set set2 = {};
2069
g_assert_true(string_set_add(&set1, "test_string1"));
2070
string_set_swap(&set1, &set2);
2071
g_assert_false(string_set_contains(set1, "test_string1"));
2072
g_assert_true(string_set_contains(set2, "test_string1"));
2076
void test_string_set_swap_empty_with_one(__attribute__((unused))
2077
test_fixture *fixture,
2078
__attribute__((unused))
2079
gconstpointer user_data){
2080
__attribute__((cleanup(string_set_clear)))
2081
string_set set1 = {};
2082
__attribute__((cleanup(string_set_clear)))
2083
string_set set2 = {};
2084
g_assert_true(string_set_add(&set2, "test_string2"));
2085
string_set_swap(&set1, &set2);
2086
g_assert_true(string_set_contains(set1, "test_string2"));
2087
g_assert_false(string_set_contains(set2, "test_string2"));
2090
static void test_string_set_swap_one_with_one(__attribute__((unused))
2091
test_fixture *fixture,
2092
__attribute__((unused))
2095
__attribute__((cleanup(string_set_clear)))
2096
string_set set1 = {};
2097
__attribute__((cleanup(string_set_clear)))
2098
string_set set2 = {};
2099
g_assert_true(string_set_add(&set1, "test_string1"));
2100
g_assert_true(string_set_add(&set2, "test_string2"));
2101
string_set_swap(&set1, &set2);
2102
g_assert_false(string_set_contains(set1, "test_string1"));
2103
g_assert_true(string_set_contains(set1, "test_string2"));
2104
g_assert_false(string_set_contains(set2, "test_string2"));
2105
g_assert_true(string_set_contains(set2, "test_string1"));
2108
static bool fd_has_cloexec_and_nonblock(const int);
2110
static bool epoll_set_contains(int, int, uint32_t);
2112
static void test_start_mandos_client(test_fixture *fixture,
2113
__attribute__((unused))
2114
gconstpointer user_data){
2116
bool mandos_client_exited = false;
2117
bool quit_now = false;
2118
__attribute__((cleanup(cleanup_close)))
2119
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2120
g_assert_cmpint(epoll_fd, >=, 0);
2121
__attribute__((cleanup(cleanup_queue)))
2122
task_queue *queue = create_queue();
2123
g_assert_nonnull(queue);
2124
buffer password = {};
2125
bool password_is_read = false;
2126
const char helper_directory[] = "/nonexistent";
2127
const char *const argv[] = { "/bin/true", NULL };
2129
g_assert_true(start_mandos_client(queue, epoll_fd,
2130
&mandos_client_exited, &quit_now,
2131
&password, &password_is_read,
2132
&fixture->orig_sigaction,
2133
fixture->orig_sigmask,
2134
helper_directory, 0, 0, argv));
2136
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
2138
const task_context *const added_wait_task
2139
= find_matching_task(queue, (task_context){
2140
.func=wait_for_mandos_client_exit,
2141
.mandos_client_exited=&mandos_client_exited,
2142
.quit_now=&quit_now,
2144
g_assert_nonnull(added_wait_task);
2145
g_assert_cmpint(added_wait_task->pid, >, 0);
2146
g_assert_cmpint(kill(added_wait_task->pid, SIGKILL), ==, 0);
2147
waitpid(added_wait_task->pid, NULL, 0);
2149
const task_context *const added_read_task
2150
= find_matching_task(queue, (task_context){
2151
.func=read_mandos_client_output,
2153
.password=&password,
2154
.password_is_read=&password_is_read,
2155
.quit_now=&quit_now,
2157
g_assert_nonnull(added_read_task);
2158
g_assert_cmpint(added_read_task->fd, >, 2);
2159
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
2160
g_assert_true(epoll_set_contains(epoll_fd, added_read_task->fd,
2161
EPOLLIN | EPOLLRDHUP));
2164
static bool fd_has_cloexec_and_nonblock(const int fd){
2165
const int socket_fd_flags = fcntl(fd, F_GETFD, 0);
2166
const int socket_file_flags = fcntl(fd, F_GETFL, 0);
2167
return ((socket_fd_flags >= 0)
2168
and (socket_fd_flags & FD_CLOEXEC)
2169
and (socket_file_flags >= 0)
2170
and (socket_file_flags & O_NONBLOCK));
2173
__attribute__((const))
2174
bool is_privileged(void){
2175
uid_t user = getuid() + 1;
2176
if(user == 0){ /* Overflow check */
2179
gid_t group = getuid() + 1;
2180
if(group == 0){ /* Overflow check */
2183
const pid_t pid = fork();
2184
if(pid == 0){ /* Child */
2185
if(setresgid((uid_t)-1, group, group) == -1){
2187
error(EXIT_FAILURE, errno, "Failed to setresgid(-1, %" PRIuMAX
2188
", %" PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
2192
if(setresuid((uid_t)-1, user, user) == -1){
2194
error(EXIT_FAILURE, errno, "Failed to setresuid(-1, %" PRIuMAX
2195
", %" PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
2202
error(EXIT_FAILURE, errno, "Failed to fork()");
2206
waitpid(pid, &status, 0);
2207
if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2213
static bool epoll_set_contains(int epoll_fd, int fd, uint32_t events){
2214
/* Only scan for events in this eventmask */
2215
const uint32_t eventmask = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
2216
__attribute__((cleanup(cleanup_string)))
2217
char *fdinfo_name = NULL;
2218
int ret = asprintf(&fdinfo_name, "/proc/self/fdinfo/%d", epoll_fd);
2219
g_assert_cmpint(ret, >, 0);
2220
g_assert_nonnull(fdinfo_name);
2222
FILE *fdinfo = fopen(fdinfo_name, "r");
2223
g_assert_nonnull(fdinfo);
2224
uint32_t reported_events;
2229
if(getline(&line.data, &line.allocated, fdinfo) < 0){
2232
/* See proc(5) for format of /proc/PID/fdinfo/FD for epoll fd's */
2233
if(sscanf(line.data, "tfd: %d events: %" SCNx32 " ",
2234
&found_fd, &reported_events) == 2){
2239
} while(not feof(fdinfo) and not ferror(fdinfo));
2240
g_assert_cmpint(fclose(fdinfo), ==, 0);
2247
/* Don't check events if none are given */
2250
return (reported_events & eventmask) == (events & eventmask);
2253
static void test_start_mandos_client_execv(test_fixture *fixture,
2254
__attribute__((unused))
2255
gconstpointer user_data){
2256
bool mandos_client_exited = false;
2257
bool quit_now = false;
2258
__attribute__((cleanup(cleanup_close)))
2259
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2260
g_assert_cmpint(epoll_fd, >=, 0);
2261
__attribute__((cleanup(cleanup_queue)))
2262
task_queue *queue = create_queue();
2263
g_assert_nonnull(queue);
2264
__attribute__((cleanup(cleanup_buffer)))
2265
buffer password = {};
2266
const char helper_directory[] = "/nonexistent";
2267
/* Can't execv("/", ...), so this should fail */
2268
const char *const argv[] = { "/", NULL };
2271
__attribute__((cleanup(cleanup_close)))
2272
const int devnull_fd = open("/dev/null",
2273
O_WRONLY | O_CLOEXEC | O_NOCTTY);
2274
g_assert_cmpint(devnull_fd, >=, 0);
2275
__attribute__((cleanup(cleanup_close)))
2276
const int real_stderr_fd = dup(STDERR_FILENO);
2277
g_assert_cmpint(real_stderr_fd, >=, 0);
2278
dup2(devnull_fd, STDERR_FILENO);
2280
const bool success = start_mandos_client(queue, epoll_fd,
2281
&mandos_client_exited,
2285
&fixture->orig_sigaction,
2286
fixture->orig_sigmask,
2287
helper_directory, 0, 0,
2289
dup2(real_stderr_fd, STDERR_FILENO);
2290
g_assert_true(success);
2292
g_assert_cmpuint((unsigned int)queue->length, ==, 2);
2294
struct timespec starttime, currtime;
2295
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2297
queue->next_run = 0;
2298
string_set cancelled_filenames = {};
2301
__attribute__((cleanup(cleanup_close)))
2302
const int devnull_fd = open("/dev/null",
2303
O_WRONLY | O_CLOEXEC | O_NOCTTY);
2304
g_assert_cmpint(devnull_fd, >=, 0);
2305
__attribute__((cleanup(cleanup_close)))
2306
const int real_stderr_fd = dup(STDERR_FILENO);
2307
g_assert_cmpint(real_stderr_fd, >=, 0);
2308
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2309
dup2(devnull_fd, STDERR_FILENO);
2310
const bool success = run_queue(&queue, &cancelled_filenames,
2312
dup2(real_stderr_fd, STDERR_FILENO);
2317
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2318
} while(((queue->length) > 0)
2320
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2322
g_assert_true(quit_now);
2323
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2324
g_assert_true(mandos_client_exited);
2327
static void test_start_mandos_client_suid_euid(test_fixture *fixture,
2328
__attribute__((unused))
2331
if(not is_privileged()){
2332
g_test_skip("Not privileged");
2336
bool mandos_client_exited = false;
2337
bool quit_now = false;
2338
__attribute__((cleanup(cleanup_close)))
2339
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2340
g_assert_cmpint(epoll_fd, >=, 0);
2341
__attribute__((cleanup(cleanup_queue)))
2342
task_queue *queue = create_queue();
2343
g_assert_nonnull(queue);
2344
__attribute__((cleanup(cleanup_buffer)))
2345
buffer password = {};
2346
bool password_is_read = false;
2347
const char helper_directory[] = "/nonexistent";
2348
const char *const argv[] = { "/usr/bin/id", "--user", NULL };
2352
const bool success = start_mandos_client(queue, epoll_fd,
2353
&mandos_client_exited,
2354
&quit_now, &password,
2356
&fixture->orig_sigaction,
2357
fixture->orig_sigmask,
2358
helper_directory, user,
2360
g_assert_true(success);
2361
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2363
struct timespec starttime, currtime;
2364
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2366
queue->next_run = 0;
2367
string_set cancelled_filenames = {};
2368
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2369
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2370
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2371
} while(((queue->length) > 0)
2373
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2375
g_assert_false(quit_now);
2376
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2377
g_assert_true(mandos_client_exited);
2379
g_assert_true(password_is_read);
2380
g_assert_nonnull(password.data);
2383
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2385
g_assert_true((uid_t)id == id);
2387
g_assert_cmpuint((unsigned int)id, ==, 0);
2390
static void test_start_mandos_client_suid_egid(test_fixture *fixture,
2391
__attribute__((unused))
2394
if(not is_privileged()){
2395
g_test_skip("Not privileged");
2399
bool mandos_client_exited = false;
2400
bool quit_now = false;
2401
__attribute__((cleanup(cleanup_close)))
2402
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2403
g_assert_cmpint(epoll_fd, >=, 0);
2404
__attribute__((cleanup(cleanup_queue)))
2405
task_queue *queue = create_queue();
2406
g_assert_nonnull(queue);
2407
__attribute__((cleanup(cleanup_buffer)))
2408
buffer password = {};
2409
bool password_is_read = false;
2410
const char helper_directory[] = "/nonexistent";
2411
const char *const argv[] = { "/usr/bin/id", "--group", NULL };
2415
const bool success = start_mandos_client(queue, epoll_fd,
2416
&mandos_client_exited,
2417
&quit_now, &password,
2419
&fixture->orig_sigaction,
2420
fixture->orig_sigmask,
2421
helper_directory, user,
2423
g_assert_true(success);
2424
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2426
struct timespec starttime, currtime;
2427
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2429
queue->next_run = 0;
2430
string_set cancelled_filenames = {};
2431
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2432
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2433
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2434
} while(((queue->length) > 0)
2436
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2438
g_assert_false(quit_now);
2439
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2440
g_assert_true(mandos_client_exited);
2442
g_assert_true(password_is_read);
2443
g_assert_nonnull(password.data);
2446
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2448
g_assert_true((gid_t)id == id);
2450
g_assert_cmpuint((unsigned int)id, ==, 0);
2453
static void test_start_mandos_client_suid_ruid(test_fixture *fixture,
2454
__attribute__((unused))
2457
if(not is_privileged()){
2458
g_test_skip("Not privileged");
2462
bool mandos_client_exited = false;
2463
bool quit_now = false;
2464
__attribute__((cleanup(cleanup_close)))
2465
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2466
g_assert_cmpint(epoll_fd, >=, 0);
2467
__attribute__((cleanup(cleanup_queue)))
2468
task_queue *queue = create_queue();
2469
g_assert_nonnull(queue);
2470
__attribute__((cleanup(cleanup_buffer)))
2471
buffer password = {};
2472
bool password_is_read = false;
2473
const char helper_directory[] = "/nonexistent";
2474
const char *const argv[] = { "/usr/bin/id", "--user", "--real",
2479
const bool success = start_mandos_client(queue, epoll_fd,
2480
&mandos_client_exited,
2481
&quit_now, &password,
2483
&fixture->orig_sigaction,
2484
fixture->orig_sigmask,
2485
helper_directory, user,
2487
g_assert_true(success);
2488
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2490
struct timespec starttime, currtime;
2491
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2493
queue->next_run = 0;
2494
string_set cancelled_filenames = {};
2495
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2496
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2497
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2498
} while(((queue->length) > 0)
2500
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2502
g_assert_false(quit_now);
2503
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2504
g_assert_true(mandos_client_exited);
2506
g_assert_true(password_is_read);
2507
g_assert_nonnull(password.data);
2510
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2512
g_assert_true((uid_t)id == id);
2514
g_assert_cmpuint((unsigned int)id, ==, user);
2517
static void test_start_mandos_client_suid_rgid(test_fixture *fixture,
2518
__attribute__((unused))
2521
if(not is_privileged()){
2522
g_test_skip("Not privileged");
2526
bool mandos_client_exited = false;
2527
bool quit_now = false;
2528
__attribute__((cleanup(cleanup_close)))
2529
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2530
g_assert_cmpint(epoll_fd, >=, 0);
2531
__attribute__((cleanup(cleanup_queue)))
2532
task_queue *queue = create_queue();
2533
g_assert_nonnull(queue);
2534
__attribute__((cleanup(cleanup_buffer)))
2535
buffer password = {};
2536
bool password_is_read = false;
2537
const char helper_directory[] = "/nonexistent";
2538
const char *const argv[] = { "/usr/bin/id", "--group", "--real",
2543
const bool success = start_mandos_client(queue, epoll_fd,
2544
&mandos_client_exited,
2545
&quit_now, &password,
2547
&fixture->orig_sigaction,
2548
fixture->orig_sigmask,
2549
helper_directory, user,
2551
g_assert_true(success);
2552
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2554
struct timespec starttime, currtime;
2555
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2557
queue->next_run = 0;
2558
string_set cancelled_filenames = {};
2559
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2560
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2561
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2562
} while(((queue->length) > 0)
2564
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2566
g_assert_false(quit_now);
2567
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2568
g_assert_true(mandos_client_exited);
2570
g_assert_true(password_is_read);
2571
g_assert_nonnull(password.data);
2574
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2576
g_assert_true((gid_t)id == id);
2578
g_assert_cmpuint((unsigned int)id, ==, group);
2581
static void test_start_mandos_client_read(test_fixture *fixture,
2582
__attribute__((unused))
2583
gconstpointer user_data){
2584
bool mandos_client_exited = false;
2585
bool quit_now = false;
2586
__attribute__((cleanup(cleanup_close)))
2587
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2588
g_assert_cmpint(epoll_fd, >=, 0);
2589
__attribute__((cleanup(cleanup_queue)))
2590
task_queue *queue = create_queue();
2591
g_assert_nonnull(queue);
2592
__attribute__((cleanup(cleanup_buffer)))
2593
buffer password = {};
2594
bool password_is_read = false;
2595
const char dummy_test_password[] = "dummy test password";
2596
const char helper_directory[] = "/nonexistent";
2597
const char *const argv[] = { "/bin/echo", "-n", dummy_test_password,
2600
const bool success = start_mandos_client(queue, epoll_fd,
2601
&mandos_client_exited,
2602
&quit_now, &password,
2604
&fixture->orig_sigaction,
2605
fixture->orig_sigmask,
2606
helper_directory, 0, 0,
2608
g_assert_true(success);
2609
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2611
struct timespec starttime, currtime;
2612
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2614
queue->next_run = 0;
2615
string_set cancelled_filenames = {};
2616
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2617
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2618
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2619
} while(((queue->length) > 0)
2621
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2623
g_assert_false(quit_now);
2624
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2625
g_assert_true(mandos_client_exited);
2627
g_assert_true(password_is_read);
2628
g_assert_cmpint((int)password.length, ==,
2629
sizeof(dummy_test_password)-1);
2630
g_assert_nonnull(password.data);
2631
g_assert_cmpint(memcmp(dummy_test_password, password.data,
2632
sizeof(dummy_test_password)-1), ==, 0);
2636
void test_start_mandos_client_helper_directory(test_fixture *fixture,
2637
__attribute__((unused))
2640
bool mandos_client_exited = false;
2641
bool quit_now = false;
2642
__attribute__((cleanup(cleanup_close)))
2643
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2644
g_assert_cmpint(epoll_fd, >=, 0);
2645
__attribute__((cleanup(cleanup_queue)))
2646
task_queue *queue = create_queue();
2647
g_assert_nonnull(queue);
2648
__attribute__((cleanup(cleanup_buffer)))
2649
buffer password = {};
2650
bool password_is_read = false;
2651
const char helper_directory[] = "/nonexistent";
2652
const char *const argv[] = { "/bin/sh", "-c",
2653
"echo -n ${MANDOSPLUGINHELPERDIR}", NULL };
2655
const bool success = start_mandos_client(queue, epoll_fd,
2656
&mandos_client_exited,
2657
&quit_now, &password,
2659
&fixture->orig_sigaction,
2660
fixture->orig_sigmask,
2661
helper_directory, 0, 0,
2663
g_assert_true(success);
2664
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2666
struct timespec starttime, currtime;
2667
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2669
queue->next_run = 0;
2670
string_set cancelled_filenames = {};
2671
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2672
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2673
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2674
} while(((queue->length) > 0)
2676
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2678
g_assert_false(quit_now);
2679
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2680
g_assert_true(mandos_client_exited);
2682
g_assert_true(password_is_read);
2683
g_assert_cmpint((int)password.length, ==,
2684
sizeof(helper_directory)-1);
2685
g_assert_nonnull(password.data);
2686
g_assert_cmpint(memcmp(helper_directory, password.data,
2687
sizeof(helper_directory)-1), ==, 0);
2690
__attribute__((nonnull, warn_unused_result))
2691
static bool proc_status_sigblk_to_sigset(const char *const,
2694
static void test_start_mandos_client_sigmask(test_fixture *fixture,
2695
__attribute__((unused))
2696
gconstpointer user_data){
2697
bool mandos_client_exited = false;
2698
bool quit_now = false;
2699
__attribute__((cleanup(cleanup_close)))
2700
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2701
g_assert_cmpint(epoll_fd, >=, 0);
2702
__attribute__((cleanup(cleanup_queue)))
2703
task_queue *queue = create_queue();
2704
g_assert_nonnull(queue);
2705
__attribute__((cleanup(cleanup_buffer)))
2706
buffer password = {};
2707
bool password_is_read = false;
2708
const char helper_directory[] = "/nonexistent";
2709
/* see proc(5) for format of /proc/self/status */
2710
const char *const argv[] = { "/usr/bin/awk",
2711
"$1==\"SigBlk:\"{ print $2 }", "/proc/self/status", NULL };
2713
g_assert_true(start_mandos_client(queue, epoll_fd,
2714
&mandos_client_exited, &quit_now,
2715
&password, &password_is_read,
2716
&fixture->orig_sigaction,
2717
fixture->orig_sigmask,
2718
helper_directory, 0, 0, argv));
2720
struct timespec starttime, currtime;
2721
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2723
queue->next_run = 0;
2724
string_set cancelled_filenames = {};
2725
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2726
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2727
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2728
} while((not (mandos_client_exited and password_is_read))
2730
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2731
g_assert_true(mandos_client_exited);
2732
g_assert_true(password_is_read);
2734
sigset_t parsed_sigmask;
2735
g_assert_true(proc_status_sigblk_to_sigset(password.data,
2738
for(int signum = 1; signum < NSIG; signum++){
2739
const bool has_signal = sigismember(&parsed_sigmask, signum);
2740
if(sigismember(&fixture->orig_sigmask, signum)){
2741
g_assert_true(has_signal);
2743
g_assert_false(has_signal);
2748
__attribute__((nonnull, warn_unused_result))
2749
static bool proc_status_sigblk_to_sigset(const char *const sigblk,
2750
sigset_t *const sigmask){
2751
/* parse /proc/PID/status SigBlk value and convert to a sigset_t */
2752
uintmax_t scanned_sigmask;
2753
if(sscanf(sigblk, "%" SCNxMAX " ", &scanned_sigmask) != 1){
2756
if(sigemptyset(sigmask) != 0){
2759
for(int signum = 1; signum < NSIG; signum++){
2760
if(scanned_sigmask & ((uintmax_t)1 << (signum-1))){
2761
if(sigaddset(sigmask, signum) != 0){
2769
static void run_task_with_stderr_to_dev_null(const task_context task,
2770
task_queue *const queue);
2773
void test_wait_for_mandos_client_exit_badpid(__attribute__((unused))
2774
test_fixture *fixture,
2775
__attribute__((unused))
2776
gconstpointer user_data){
2778
bool mandos_client_exited = false;
2779
bool quit_now = false;
2781
__attribute__((cleanup(cleanup_queue)))
2782
task_queue *queue = create_queue();
2783
g_assert_nonnull(queue);
2784
const task_context task = {
2785
.func=wait_for_mandos_client_exit,
2787
.mandos_client_exited=&mandos_client_exited,
2788
.quit_now=&quit_now,
2790
run_task_with_stderr_to_dev_null(task, queue);
2792
g_assert_false(mandos_client_exited);
2793
g_assert_true(quit_now);
2794
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2797
static void run_task_with_stderr_to_dev_null(const task_context task,
2798
task_queue *const queue){
2799
FILE *real_stderr = stderr;
2800
FILE *devnull = fopen("/dev/null", "we");
2801
g_assert_nonnull(devnull);
2804
task.func(task, queue);
2805
stderr = real_stderr;
2807
g_assert_cmpint(fclose(devnull), ==, 0);
2811
void test_wait_for_mandos_client_exit_noexit(test_fixture *fixture,
2812
__attribute__((unused))
2813
gconstpointer user_data){
2814
bool mandos_client_exited = false;
2815
bool quit_now = false;
2817
pid_t create_eternal_process(void){
2818
const pid_t pid = fork();
2819
if(pid == 0){ /* Child */
2820
if(not restore_signal_handler(&fixture->orig_sigaction)){
2821
_exit(EXIT_FAILURE);
2823
if(not restore_sigmask(&fixture->orig_sigmask)){
2824
_exit(EXIT_FAILURE);
2832
pid_t pid = create_eternal_process();
2833
g_assert_true(pid != -1);
2835
__attribute__((cleanup(cleanup_queue)))
2836
task_queue *queue = create_queue();
2837
g_assert_nonnull(queue);
2838
const task_context task = {
2839
.func=wait_for_mandos_client_exit,
2841
.mandos_client_exited=&mandos_client_exited,
2842
.quit_now=&quit_now,
2844
task.func(task, queue);
2846
g_assert_false(mandos_client_exited);
2847
g_assert_false(quit_now);
2848
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
2850
g_assert_nonnull(find_matching_task(queue, (task_context){
2851
.func=wait_for_mandos_client_exit,
2853
.mandos_client_exited=&mandos_client_exited,
2854
.quit_now=&quit_now,
2859
void test_wait_for_mandos_client_exit_success(test_fixture *fixture,
2860
__attribute__((unused))
2863
bool mandos_client_exited = false;
2864
bool quit_now = false;
2866
pid_t create_successful_process(void){
2867
const pid_t pid = fork();
2868
if(pid == 0){ /* Child */
2869
if(not restore_signal_handler(&fixture->orig_sigaction)){
2870
_exit(EXIT_FAILURE);
2872
if(not restore_sigmask(&fixture->orig_sigmask)){
2873
_exit(EXIT_FAILURE);
2879
const pid_t pid = create_successful_process();
2880
g_assert_true(pid != -1);
2882
__attribute__((cleanup(cleanup_queue)))
2883
task_queue *queue = create_queue();
2884
g_assert_nonnull(queue);
2885
const task_context initial_task = {
2886
.func=wait_for_mandos_client_exit,
2888
.mandos_client_exited=&mandos_client_exited,
2889
.quit_now=&quit_now,
2891
g_assert_true(add_to_queue(queue, initial_task));
2893
struct timespec starttime, currtime;
2894
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2895
__attribute__((cleanup(cleanup_close)))
2896
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2898
queue->next_run = 0;
2899
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2900
g_assert_true(run_queue(&queue, (string_set[]){{}}, &quit_now));
2901
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2902
} while((not mandos_client_exited)
2904
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2906
g_assert_true(mandos_client_exited);
2907
g_assert_false(quit_now);
2908
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2912
void test_wait_for_mandos_client_exit_failure(test_fixture *fixture,
2913
__attribute__((unused))
2916
bool mandos_client_exited = false;
2917
bool quit_now = false;
2919
pid_t create_failing_process(void){
2920
const pid_t pid = fork();
2921
if(pid == 0){ /* Child */
2922
if(not restore_signal_handler(&fixture->orig_sigaction)){
2923
_exit(EXIT_FAILURE);
2925
if(not restore_sigmask(&fixture->orig_sigmask)){
2926
_exit(EXIT_FAILURE);
2932
const pid_t pid = create_failing_process();
2933
g_assert_true(pid != -1);
2935
__attribute__((cleanup(string_set_clear)))
2936
string_set cancelled_filenames = {};
2937
__attribute__((cleanup(cleanup_close)))
2938
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2939
g_assert_cmpint(epoll_fd, >=, 0);
2940
__attribute__((cleanup(cleanup_queue)))
2941
task_queue *queue = create_queue();
2942
g_assert_nonnull(queue);
2943
g_assert_true(add_to_queue(queue, (task_context){
2944
.func=wait_for_mandos_client_exit,
2946
.mandos_client_exited=&mandos_client_exited,
2947
.quit_now=&quit_now,
2950
g_assert_true(sigismember(&fixture->orig_sigmask, SIGCHLD) == 0);
2952
__attribute__((cleanup(cleanup_close)))
2953
const int devnull_fd = open("/dev/null",
2954
O_WRONLY | O_CLOEXEC | O_NOCTTY);
2955
g_assert_cmpint(devnull_fd, >=, 0);
2956
__attribute__((cleanup(cleanup_close)))
2957
const int real_stderr_fd = dup(STDERR_FILENO);
2958
g_assert_cmpint(real_stderr_fd, >=, 0);
2960
struct timespec starttime, currtime;
2961
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2963
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2964
dup2(devnull_fd, STDERR_FILENO);
2965
const bool success = run_queue(&queue, &cancelled_filenames,
2967
dup2(real_stderr_fd, STDERR_FILENO);
2972
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2973
} while((not mandos_client_exited)
2975
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2977
g_assert_true(quit_now);
2978
g_assert_true(mandos_client_exited);
2979
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2983
void test_wait_for_mandos_client_exit_killed(test_fixture *fixture,
2984
__attribute__((unused))
2985
gconstpointer user_data){
2986
bool mandos_client_exited = false;
2987
bool quit_now = false;
2989
pid_t create_killed_process(void){
2990
const pid_t pid = fork();
2991
if(pid == 0){ /* Child */
2992
if(not restore_signal_handler(&fixture->orig_sigaction)){
2993
_exit(EXIT_FAILURE);
2995
if(not restore_sigmask(&fixture->orig_sigmask)){
2996
_exit(EXIT_FAILURE);
3005
const pid_t pid = create_killed_process();
3006
g_assert_true(pid != -1);
3008
__attribute__((cleanup(string_set_clear)))
3009
string_set cancelled_filenames = {};
3010
__attribute__((cleanup(cleanup_close)))
3011
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3012
g_assert_cmpint(epoll_fd, >=, 0);
3013
__attribute__((cleanup(cleanup_queue)))
3014
task_queue *queue = create_queue();
3015
g_assert_nonnull(queue);
3016
g_assert_true(add_to_queue(queue, (task_context){
3017
.func=wait_for_mandos_client_exit,
3019
.mandos_client_exited=&mandos_client_exited,
3020
.quit_now=&quit_now,
3023
__attribute__((cleanup(cleanup_close)))
3024
const int devnull_fd = open("/dev/null",
3025
O_WRONLY | O_CLOEXEC, O_NOCTTY);
3026
g_assert_cmpint(devnull_fd, >=, 0);
3027
__attribute__((cleanup(cleanup_close)))
3028
const int real_stderr_fd = dup(STDERR_FILENO);
3029
g_assert_cmpint(real_stderr_fd, >=, 0);
3031
struct timespec starttime, currtime;
3032
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
3034
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
3035
dup2(devnull_fd, STDERR_FILENO);
3036
const bool success = run_queue(&queue, &cancelled_filenames,
3038
dup2(real_stderr_fd, STDERR_FILENO);
3043
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
3044
} while((not mandos_client_exited)
3046
and ((currtime.tv_sec - starttime.tv_sec) < 10));
3048
g_assert_true(mandos_client_exited);
3049
g_assert_true(quit_now);
3050
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3053
static bool epoll_set_does_not_contain(int, int);
3056
void test_read_mandos_client_output_readerror(__attribute__((unused))
3057
test_fixture *fixture,
3058
__attribute__((unused))
3061
__attribute__((cleanup(cleanup_close)))
3062
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3063
g_assert_cmpint(epoll_fd, >=, 0);
3065
__attribute__((cleanup(cleanup_buffer)))
3066
buffer password = {};
3068
/* Reading /proc/self/mem from offset 0 will always give EIO */
3069
const int fd = open("/proc/self/mem",
3070
O_RDONLY | O_CLOEXEC | O_NOCTTY);
3072
bool password_is_read = false;
3073
bool quit_now = false;
3074
__attribute__((cleanup(cleanup_queue)))
3075
task_queue *queue = create_queue();
3076
g_assert_nonnull(queue);
3078
task_context task = {
3079
.func=read_mandos_client_output,
3082
.password=&password,
3083
.password_is_read=&password_is_read,
3084
.quit_now=&quit_now,
3086
run_task_with_stderr_to_dev_null(task, queue);
3087
g_assert_false(password_is_read);
3088
g_assert_cmpint((int)password.length, ==, 0);
3089
g_assert_true(quit_now);
3090
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3092
g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
3094
g_assert_cmpint(close(fd), ==, -1);
3097
static bool epoll_set_does_not_contain(int epoll_fd, int fd){
3098
return not epoll_set_contains(epoll_fd, fd, 0);
3102
void test_read_mandos_client_output_nodata(__attribute__((unused))
3103
test_fixture *fixture,
3104
__attribute__((unused))
3105
gconstpointer user_data){
3106
__attribute__((cleanup(cleanup_close)))
3107
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3108
g_assert_cmpint(epoll_fd, >=, 0);
3111
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3113
__attribute__((cleanup(cleanup_buffer)))
3114
buffer password = {};
3116
bool password_is_read = false;
3117
bool quit_now = false;
3118
__attribute__((cleanup(cleanup_queue)))
3119
task_queue *queue = create_queue();
3120
g_assert_nonnull(queue);
3122
task_context task = {
3123
.func=read_mandos_client_output,
3126
.password=&password,
3127
.password_is_read=&password_is_read,
3128
.quit_now=&quit_now,
3130
task.func(task, queue);
3131
g_assert_false(password_is_read);
3132
g_assert_cmpint((int)password.length, ==, 0);
3133
g_assert_false(quit_now);
3134
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3136
g_assert_nonnull(find_matching_task(queue, (task_context){
3137
.func=read_mandos_client_output,
3140
.password=&password,
3141
.password_is_read=&password_is_read,
3142
.quit_now=&quit_now,
3145
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3146
EPOLLIN | EPOLLRDHUP));
3148
g_assert_cmpint(close(pipefds[1]), ==, 0);
3151
static void test_read_mandos_client_output_eof(__attribute__((unused))
3152
test_fixture *fixture,
3153
__attribute__((unused))
3156
__attribute__((cleanup(cleanup_close)))
3157
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3158
g_assert_cmpint(epoll_fd, >=, 0);
3161
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3162
g_assert_cmpint(close(pipefds[1]), ==, 0);
3164
__attribute__((cleanup(cleanup_buffer)))
3165
buffer password = {};
3167
bool password_is_read = false;
3168
bool quit_now = false;
3169
__attribute__((cleanup(cleanup_queue)))
3170
task_queue *queue = create_queue();
3171
g_assert_nonnull(queue);
3173
task_context task = {
3174
.func=read_mandos_client_output,
3177
.password=&password,
3178
.password_is_read=&password_is_read,
3179
.quit_now=&quit_now,
3181
task.func(task, queue);
3182
g_assert_true(password_is_read);
3183
g_assert_cmpint((int)password.length, ==, 0);
3184
g_assert_false(quit_now);
3185
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3187
g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
3189
g_assert_cmpint(close(pipefds[0]), ==, -1);
3193
void test_read_mandos_client_output_once(__attribute__((unused))
3194
test_fixture *fixture,
3195
__attribute__((unused))
3196
gconstpointer user_data){
3197
__attribute__((cleanup(cleanup_close)))
3198
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3199
g_assert_cmpint(epoll_fd, >=, 0);
3202
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3204
const char dummy_test_password[] = "dummy test password";
3205
/* Start with a pre-allocated buffer */
3206
__attribute__((cleanup(cleanup_buffer)))
3208
.data=malloc(sizeof(dummy_test_password)),
3210
.allocated=sizeof(dummy_test_password),
3212
g_assert_nonnull(password.data);
3213
if(mlock(password.data, password.allocated) != 0){
3214
g_assert_true(errno == EPERM or errno == ENOMEM);
3217
bool password_is_read = false;
3218
bool quit_now = false;
3219
__attribute__((cleanup(cleanup_queue)))
3220
task_queue *queue = create_queue();
3221
g_assert_nonnull(queue);
3223
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3224
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3225
sizeof(dummy_test_password)),
3226
==, (int)sizeof(dummy_test_password));
3228
task_context task = {
3229
.func=read_mandos_client_output,
3232
.password=&password,
3233
.password_is_read=&password_is_read,
3234
.quit_now=&quit_now,
3236
task.func(task, queue);
3238
g_assert_false(password_is_read);
3239
g_assert_cmpint((int)password.length, ==,
3240
(int)sizeof(dummy_test_password));
3241
g_assert_nonnull(password.data);
3242
g_assert_cmpint(memcmp(password.data, dummy_test_password,
3243
sizeof(dummy_test_password)), ==, 0);
3245
g_assert_false(quit_now);
3246
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3248
g_assert_nonnull(find_matching_task(queue, (task_context){
3249
.func=read_mandos_client_output,
3252
.password=&password,
3253
.password_is_read=&password_is_read,
3254
.quit_now=&quit_now,
3257
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3258
EPOLLIN | EPOLLRDHUP));
3260
g_assert_cmpint(close(pipefds[1]), ==, 0);
3264
void test_read_mandos_client_output_malloc(__attribute__((unused))
3265
test_fixture *fixture,
3266
__attribute__((unused))
3267
gconstpointer user_data){
3268
__attribute__((cleanup(cleanup_close)))
3269
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3270
g_assert_cmpint(epoll_fd, >=, 0);
3273
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3275
const char dummy_test_password[] = "dummy test password";
3276
/* Start with an empty buffer */
3277
__attribute__((cleanup(cleanup_buffer)))
3278
buffer password = {};
3280
bool password_is_read = false;
3281
bool quit_now = false;
3282
__attribute__((cleanup(cleanup_queue)))
3283
task_queue *queue = create_queue();
3284
g_assert_nonnull(queue);
3286
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3287
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3288
sizeof(dummy_test_password)),
3289
==, (int)sizeof(dummy_test_password));
3291
task_context task = {
3292
.func=read_mandos_client_output,
3295
.password=&password,
3296
.password_is_read=&password_is_read,
3297
.quit_now=&quit_now,
3299
task.func(task, queue);
3301
g_assert_false(password_is_read);
3302
g_assert_cmpint((int)password.length, ==,
3303
(int)sizeof(dummy_test_password));
3304
g_assert_nonnull(password.data);
3305
g_assert_cmpint(memcmp(password.data, dummy_test_password,
3306
sizeof(dummy_test_password)), ==, 0);
3308
g_assert_false(quit_now);
3309
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3311
g_assert_nonnull(find_matching_task(queue, (task_context){
3312
.func=read_mandos_client_output,
3315
.password=&password,
3316
.password_is_read=&password_is_read,
3317
.quit_now=&quit_now,
3320
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3321
EPOLLIN | EPOLLRDHUP));
3323
g_assert_cmpint(close(pipefds[1]), ==, 0);
3327
void test_read_mandos_client_output_append(__attribute__((unused))
3328
test_fixture *fixture,
3329
__attribute__((unused))
3330
gconstpointer user_data){
3331
__attribute__((cleanup(cleanup_close)))
3332
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3333
g_assert_cmpint(epoll_fd, >=, 0);
3336
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3338
const char dummy_test_password[] = "dummy test password";
3339
__attribute__((cleanup(cleanup_buffer)))
3341
.data=malloc(PIPE_BUF),
3343
.allocated=PIPE_BUF,
3345
g_assert_nonnull(password.data);
3346
if(mlock(password.data, password.allocated) != 0){
3347
g_assert_true(errno == EPERM or errno == ENOMEM);
3350
memset(password.data, 'x', PIPE_BUF);
3351
char password_expected[PIPE_BUF];
3352
memcpy(password_expected, password.data, PIPE_BUF);
3354
bool password_is_read = false;
3355
bool quit_now = false;
3356
__attribute__((cleanup(cleanup_queue)))
3357
task_queue *queue = create_queue();
3358
g_assert_nonnull(queue);
3360
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3361
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3362
sizeof(dummy_test_password)),
3363
==, (int)sizeof(dummy_test_password));
3365
task_context task = {
3366
.func=read_mandos_client_output,
3369
.password=&password,
3370
.password_is_read=&password_is_read,
3371
.quit_now=&quit_now,
3373
task.func(task, queue);
3375
g_assert_false(password_is_read);
3376
g_assert_cmpint((int)password.length, ==,
3377
PIPE_BUF + sizeof(dummy_test_password));
3378
g_assert_nonnull(password.data);
3379
g_assert_cmpint(memcmp(password_expected, password.data, PIPE_BUF),
3381
g_assert_cmpint(memcmp(password.data + PIPE_BUF,
3382
dummy_test_password,
3383
sizeof(dummy_test_password)), ==, 0);
3384
g_assert_false(quit_now);
3385
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3387
g_assert_nonnull(find_matching_task(queue, (task_context){
3388
.func=read_mandos_client_output,
3391
.password=&password,
3392
.password_is_read=&password_is_read,
3393
.quit_now=&quit_now,
3396
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3397
EPOLLIN | EPOLLRDHUP));
3400
static char *make_temporary_directory(void);
3402
static void test_add_inotify_dir_watch(__attribute__((unused))
3403
test_fixture *fixture,
3404
__attribute__((unused))
3405
gconstpointer user_data){
3406
__attribute__((cleanup(cleanup_close)))
3407
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3408
g_assert_cmpint(epoll_fd, >=, 0);
3409
__attribute__((cleanup(cleanup_queue)))
3410
task_queue *queue = create_queue();
3411
g_assert_nonnull(queue);
3412
__attribute__((cleanup(string_set_clear)))
3413
string_set cancelled_filenames = {};
3414
const mono_microsecs current_time = 0;
3416
bool quit_now = false;
3417
buffer password = {};
3418
bool mandos_client_exited = false;
3419
bool password_is_read = false;
3421
__attribute__((cleanup(cleanup_string)))
3422
char *tempdir = make_temporary_directory();
3423
g_assert_nonnull(tempdir);
3425
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3427
&cancelled_filenames,
3429
&mandos_client_exited,
3430
&password_is_read));
3432
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3434
const task_context *const added_read_task
3435
= find_matching_task(queue, (task_context){
3436
.func=read_inotify_event,
3438
.quit_now=&quit_now,
3439
.password=&password,
3441
.cancelled_filenames=&cancelled_filenames,
3442
.current_time=¤t_time,
3443
.mandos_client_exited=&mandos_client_exited,
3444
.password_is_read=&password_is_read,
3446
g_assert_nonnull(added_read_task);
3448
g_assert_cmpint(added_read_task->fd, >, 2);
3449
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3450
g_assert_true(epoll_set_contains(added_read_task->epoll_fd,
3451
added_read_task->fd,
3452
EPOLLIN | EPOLLRDHUP));
3454
g_assert_cmpint(rmdir(tempdir), ==, 0);
3457
static char *make_temporary_directory(void){
3458
char *name = strdup("/tmp/mandosXXXXXX");
3459
g_assert_nonnull(name);
3460
char *result = mkdtemp(name);
3467
static void test_add_inotify_dir_watch_fail(__attribute__((unused))
3468
test_fixture *fixture,
3469
__attribute__((unused))
3470
gconstpointer user_data){
3471
__attribute__((cleanup(cleanup_close)))
3472
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3473
g_assert_cmpint(epoll_fd, >=, 0);
3474
__attribute__((cleanup(cleanup_queue)))
3475
task_queue *queue = create_queue();
3476
g_assert_nonnull(queue);
3477
__attribute__((cleanup(string_set_clear)))
3478
string_set cancelled_filenames = {};
3479
const mono_microsecs current_time = 0;
3481
bool quit_now = false;
3482
buffer password = {};
3483
bool mandos_client_exited = false;
3484
bool password_is_read = false;
3486
const char nonexistent_dir[] = "/nonexistent";
3488
FILE *real_stderr = stderr;
3489
FILE *devnull = fopen("/dev/null", "we");
3490
g_assert_nonnull(devnull);
3492
g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3493
&password, nonexistent_dir,
3494
&cancelled_filenames,
3496
&mandos_client_exited,
3497
&password_is_read));
3498
stderr = real_stderr;
3499
g_assert_cmpint(fclose(devnull), ==, 0);
3501
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3504
static void test_add_inotify_dir_watch_nondir(__attribute__((unused))
3505
test_fixture *fixture,
3506
__attribute__((unused))
3509
__attribute__((cleanup(cleanup_close)))
3510
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3511
g_assert_cmpint(epoll_fd, >=, 0);
3512
__attribute__((cleanup(cleanup_queue)))
3513
task_queue *queue = create_queue();
3514
g_assert_nonnull(queue);
3515
__attribute__((cleanup(string_set_clear)))
3516
string_set cancelled_filenames = {};
3517
const mono_microsecs current_time = 0;
3519
bool quit_now = false;
3520
buffer password = {};
3521
bool mandos_client_exited = false;
3522
bool password_is_read = false;
3524
const char not_a_directory[] = "/dev/tty";
3526
FILE *real_stderr = stderr;
3527
FILE *devnull = fopen("/dev/null", "we");
3528
g_assert_nonnull(devnull);
3530
g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3531
&password, not_a_directory,
3532
&cancelled_filenames,
3534
&mandos_client_exited,
3535
&password_is_read));
3536
stderr = real_stderr;
3537
g_assert_cmpint(fclose(devnull), ==, 0);
3539
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3542
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3543
test_fixture *fixture,
3544
__attribute__((unused))
3547
__attribute__((cleanup(cleanup_close)))
3548
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3549
g_assert_cmpint(epoll_fd, >=, 0);
3550
__attribute__((cleanup(cleanup_queue)))
3551
task_queue *queue = create_queue();
3552
g_assert_nonnull(queue);
3553
__attribute__((cleanup(string_set_clear)))
3554
string_set cancelled_filenames = {};
3555
const mono_microsecs current_time = 0;
3557
bool quit_now = false;
3558
buffer password = {};
3559
bool mandos_client_exited = false;
3560
bool password_is_read = false;
3562
__attribute__((cleanup(cleanup_string)))
3563
char *tempdir = make_temporary_directory();
3564
g_assert_nonnull(tempdir);
3566
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3568
&cancelled_filenames,
3570
&mandos_client_exited,
3571
&password_is_read));
3573
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3575
const task_context *const added_read_task
3576
= find_matching_task(queue,
3577
(task_context){ .func=read_inotify_event });
3578
g_assert_nonnull(added_read_task);
3580
g_assert_cmpint(added_read_task->fd, >, 2);
3581
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3583
/* "sufficient to read at least one event." - inotify(7) */
3584
const size_t ievent_size = (sizeof(struct inotify_event)
3586
struct inotify_event *ievent = malloc(ievent_size);
3587
g_assert_nonnull(ievent);
3589
g_assert_cmpint(read(added_read_task->fd, ievent, ievent_size), ==,
3591
g_assert_cmpint(errno, ==, EAGAIN);
3595
g_assert_cmpint(rmdir(tempdir), ==, 0);
3598
static char *make_temporary_file_in_directory(const char
3602
void test_add_inotify_dir_watch_IN_CLOSE_WRITE(__attribute__((unused))
3603
test_fixture *fixture,
3604
__attribute__((unused))
3607
__attribute__((cleanup(cleanup_close)))
3608
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3609
g_assert_cmpint(epoll_fd, >=, 0);
3610
__attribute__((cleanup(cleanup_queue)))
3611
task_queue *queue = create_queue();
3612
g_assert_nonnull(queue);
3613
__attribute__((cleanup(string_set_clear)))
3614
string_set cancelled_filenames = {};
3615
const mono_microsecs current_time = 0;
3617
bool quit_now = false;
3618
buffer password = {};
3619
bool mandos_client_exited = false;
3620
bool password_is_read = false;
3622
__attribute__((cleanup(cleanup_string)))
3623
char *tempdir = make_temporary_directory();
3624
g_assert_nonnull(tempdir);
3626
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3628
&cancelled_filenames,
3630
&mandos_client_exited,
3631
&password_is_read));
3633
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3635
const task_context *const added_read_task
3636
= find_matching_task(queue,
3637
(task_context){ .func=read_inotify_event });
3638
g_assert_nonnull(added_read_task);
3640
g_assert_cmpint(added_read_task->fd, >, 2);
3641
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3643
__attribute__((cleanup(cleanup_string)))
3644
char *filename = make_temporary_file_in_directory(tempdir);
3645
g_assert_nonnull(filename);
3647
/* "sufficient to read at least one event." - inotify(7) */
3648
const size_t ievent_size = (sizeof(struct inotify_event)
3650
struct inotify_event *ievent = malloc(ievent_size);
3651
g_assert_nonnull(ievent);
3653
ssize_t read_size = 0;
3654
read_size = read(added_read_task->fd, ievent, ievent_size);
3656
g_assert_cmpint((int)read_size, >, 0);
3657
g_assert_true(ievent->mask & IN_CLOSE_WRITE);
3658
g_assert_cmpstr(ievent->name, ==, basename(filename));
3662
g_assert_cmpint(unlink(filename), ==, 0);
3663
g_assert_cmpint(rmdir(tempdir), ==, 0);
3666
static char *make_temporary_prefixed_file_in_directory(const char
3670
char *filename = NULL;
3671
g_assert_cmpint(asprintf(&filename, "%s/%sXXXXXX", dir, prefix),
3673
g_assert_nonnull(filename);
3674
const int fd = mkostemp(filename, O_CLOEXEC);
3675
g_assert_cmpint(fd, >=, 0);
3676
g_assert_cmpint(close(fd), ==, 0);
3680
static char *make_temporary_file_in_directory(const char
3682
return make_temporary_prefixed_file_in_directory("temp", dir);
3686
void test_add_inotify_dir_watch_IN_MOVED_TO(__attribute__((unused))
3687
test_fixture *fixture,
3688
__attribute__((unused))
3689
gconstpointer user_data){
3690
__attribute__((cleanup(cleanup_close)))
3691
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3692
g_assert_cmpint(epoll_fd, >=, 0);
3693
__attribute__((cleanup(cleanup_queue)))
3694
task_queue *queue = create_queue();
3695
g_assert_nonnull(queue);
3696
__attribute__((cleanup(string_set_clear)))
3697
string_set cancelled_filenames = {};
3698
const mono_microsecs current_time = 0;
3700
bool quit_now = false;
3701
buffer password = {};
3702
bool mandos_client_exited = false;
3703
bool password_is_read = false;
3705
__attribute__((cleanup(cleanup_string)))
3706
char *watchdir = make_temporary_directory();
3707
g_assert_nonnull(watchdir);
3709
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3710
&password, watchdir,
3711
&cancelled_filenames,
3713
&mandos_client_exited,
3714
&password_is_read));
3716
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3718
const task_context *const added_read_task
3719
= find_matching_task(queue,
3720
(task_context){ .func=read_inotify_event });
3721
g_assert_nonnull(added_read_task);
3723
g_assert_cmpint(added_read_task->fd, >, 2);
3724
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3726
char *sourcedir = make_temporary_directory();
3727
g_assert_nonnull(sourcedir);
3729
__attribute__((cleanup(cleanup_string)))
3730
char *filename = make_temporary_file_in_directory(sourcedir);
3731
g_assert_nonnull(filename);
3733
__attribute__((cleanup(cleanup_string)))
3734
char *targetfilename = NULL;
3735
g_assert_cmpint(asprintf(&targetfilename, "%s/%s", watchdir,
3736
basename(filename)), >, 0);
3737
g_assert_nonnull(targetfilename);
3739
g_assert_cmpint(rename(filename, targetfilename), ==, 0);
3740
g_assert_cmpint(rmdir(sourcedir), ==, 0);
3743
/* "sufficient to read at least one event." - inotify(7) */
3744
const size_t ievent_size = (sizeof(struct inotify_event)
3746
struct inotify_event *ievent = malloc(ievent_size);
3747
g_assert_nonnull(ievent);
3749
ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3751
g_assert_cmpint((int)read_size, >, 0);
3752
g_assert_true(ievent->mask & IN_MOVED_TO);
3753
g_assert_cmpstr(ievent->name, ==, basename(targetfilename));
3757
g_assert_cmpint(unlink(targetfilename), ==, 0);
3758
g_assert_cmpint(rmdir(watchdir), ==, 0);
3762
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
3763
test_fixture *fixture,
3764
__attribute__((unused))
3767
__attribute__((cleanup(cleanup_close)))
3768
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3769
g_assert_cmpint(epoll_fd, >=, 0);
3770
__attribute__((cleanup(cleanup_queue)))
3771
task_queue *queue = create_queue();
3772
g_assert_nonnull(queue);
3773
__attribute__((cleanup(string_set_clear)))
3774
string_set cancelled_filenames = {};
3775
const mono_microsecs current_time = 0;
3777
bool quit_now = false;
3778
buffer password = {};
3779
bool mandos_client_exited = false;
3780
bool password_is_read = false;
3782
__attribute__((cleanup(cleanup_string)))
3783
char *tempdir = make_temporary_directory();
3784
g_assert_nonnull(tempdir);
3786
__attribute__((cleanup(cleanup_string)))
3787
char *tempfilename = make_temporary_file_in_directory(tempdir);
3788
g_assert_nonnull(tempfilename);
3790
__attribute__((cleanup(cleanup_string)))
3791
char *targetdir = make_temporary_directory();
3792
g_assert_nonnull(targetdir);
3794
__attribute__((cleanup(cleanup_string)))
3795
char *targetfilename = NULL;
3796
g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
3797
basename(tempfilename)), >, 0);
3798
g_assert_nonnull(targetfilename);
3800
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3802
&cancelled_filenames,
3804
&mandos_client_exited,
3805
&password_is_read));
3807
g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
3809
const task_context *const added_read_task
3810
= find_matching_task(queue,
3811
(task_context){ .func=read_inotify_event });
3812
g_assert_nonnull(added_read_task);
3814
/* "sufficient to read at least one event." - inotify(7) */
3815
const size_t ievent_size = (sizeof(struct inotify_event)
3817
struct inotify_event *ievent = malloc(ievent_size);
3818
g_assert_nonnull(ievent);
3820
ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3822
g_assert_cmpint((int)read_size, >, 0);
3823
g_assert_true(ievent->mask & IN_MOVED_FROM);
3824
g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
3828
g_assert_cmpint(unlink(targetfilename), ==, 0);
3829
g_assert_cmpint(rmdir(targetdir), ==, 0);
3830
g_assert_cmpint(rmdir(tempdir), ==, 0);
3834
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3835
test_fixture *fixture,
3836
__attribute__((unused))
3837
gconstpointer user_data){
3838
__attribute__((cleanup(cleanup_close)))
3839
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3840
g_assert_cmpint(epoll_fd, >=, 0);
3841
__attribute__((cleanup(cleanup_queue)))
3842
task_queue *queue = create_queue();
3843
g_assert_nonnull(queue);
3844
__attribute__((cleanup(string_set_clear)))
3845
string_set cancelled_filenames = {};
3846
const mono_microsecs current_time = 0;
3848
bool quit_now = false;
3849
buffer password = {};
3850
bool mandos_client_exited = false;
3851
bool password_is_read = false;
3853
__attribute__((cleanup(cleanup_string)))
3854
char *tempdir = make_temporary_directory();
3855
g_assert_nonnull(tempdir);
3857
__attribute__((cleanup(cleanup_string)))
3858
char *tempfile = make_temporary_file_in_directory(tempdir);
3859
g_assert_nonnull(tempfile);
3861
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3863
&cancelled_filenames,
3865
&mandos_client_exited,
3866
&password_is_read));
3867
g_assert_cmpint(unlink(tempfile), ==, 0);
3869
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3871
const task_context *const added_read_task
3872
= find_matching_task(queue,
3873
(task_context){ .func=read_inotify_event });
3874
g_assert_nonnull(added_read_task);
3876
g_assert_cmpint(added_read_task->fd, >, 2);
3877
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3879
/* "sufficient to read at least one event." - inotify(7) */
3880
const size_t ievent_size = (sizeof(struct inotify_event)
3882
struct inotify_event *ievent = malloc(ievent_size);
3883
g_assert_nonnull(ievent);
3885
ssize_t read_size = 0;
3886
read_size = read(added_read_task->fd, ievent, ievent_size);
3888
g_assert_cmpint((int)read_size, >, 0);
3889
g_assert_true(ievent->mask & IN_DELETE);
3890
g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3894
g_assert_cmpint(rmdir(tempdir), ==, 0);
3898
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
3899
test_fixture *fixture,
3900
__attribute__((unused))
3903
__attribute__((cleanup(cleanup_close)))
3904
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3905
g_assert_cmpint(epoll_fd, >=, 0);
3906
__attribute__((cleanup(cleanup_queue)))
3907
task_queue *queue = create_queue();
3908
g_assert_nonnull(queue);
3909
__attribute__((cleanup(string_set_clear)))
3910
string_set cancelled_filenames = {};
3911
const mono_microsecs current_time = 0;
3913
bool quit_now = false;
3914
buffer password = {};
3915
bool mandos_client_exited = false;
3916
bool password_is_read = false;
3918
__attribute__((cleanup(cleanup_string)))
3919
char *tempdir = make_temporary_directory();
3920
g_assert_nonnull(tempdir);
3922
__attribute__((cleanup(cleanup_string)))
3923
char *tempfile = make_temporary_file_in_directory(tempdir);
3924
g_assert_nonnull(tempfile);
3925
int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
3927
g_assert_cmpint(tempfile_fd, >, 2);
3929
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3931
&cancelled_filenames,
3933
&mandos_client_exited,
3934
&password_is_read));
3935
g_assert_cmpint(unlink(tempfile), ==, 0);
3937
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3939
const task_context *const added_read_task
3940
= find_matching_task(queue,
3941
(task_context){ .func=read_inotify_event });
3942
g_assert_nonnull(added_read_task);
3944
g_assert_cmpint(added_read_task->fd, >, 2);
3945
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3947
/* "sufficient to read at least one event." - inotify(7) */
3948
const size_t ievent_size = (sizeof(struct inotify_event)
3950
struct inotify_event *ievent = malloc(ievent_size);
3951
g_assert_nonnull(ievent);
3953
ssize_t read_size = 0;
3954
read_size = read(added_read_task->fd, ievent, ievent_size);
3956
g_assert_cmpint((int)read_size, >, 0);
3957
g_assert_true(ievent->mask & IN_DELETE);
3958
g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3960
g_assert_cmpint(close(tempfile_fd), ==, 0);
3962
/* IN_EXCL_UNLINK should make the closing of the previously unlinked
3963
file not appear as an ievent, so we should not see it now. */
3964
read_size = read(added_read_task->fd, ievent, ievent_size);
3965
g_assert_cmpint((int)read_size, ==, -1);
3966
g_assert_true(errno == EAGAIN);
3970
g_assert_cmpint(rmdir(tempdir), ==, 0);
3973
static void test_read_inotify_event_readerror(__attribute__((unused))
3974
test_fixture *fixture,
3975
__attribute__((unused))
3978
__attribute__((cleanup(cleanup_close)))
3979
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3980
g_assert_cmpint(epoll_fd, >=, 0);
3981
const mono_microsecs current_time = 0;
3983
/* Reading /proc/self/mem from offset 0 will always result in EIO */
3984
const int fd = open("/proc/self/mem",
3985
O_RDONLY | O_CLOEXEC | O_NOCTTY);
3987
bool quit_now = false;
3988
__attribute__((cleanup(cleanup_queue)))
3989
task_queue *queue = create_queue();
3990
g_assert_nonnull(queue);
3992
task_context task = {
3993
.func=read_inotify_event,
3996
.quit_now=&quit_now,
3997
.filename=strdup("/nonexistent"),
3998
.cancelled_filenames = &(string_set){},
4000
.current_time=¤t_time,
4002
g_assert_nonnull(task.filename);
4003
run_task_with_stderr_to_dev_null(task, queue);
4004
g_assert_true(quit_now);
4005
g_assert_true(queue->next_run == 0);
4006
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4008
g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
4010
g_assert_cmpint(close(fd), ==, -1);
4013
static void test_read_inotify_event_bad_epoll(__attribute__((unused))
4014
test_fixture *fixture,
4015
__attribute__((unused))
4018
const mono_microsecs current_time = 17;
4021
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4022
const int epoll_fd = pipefds[0]; /* This will obviously fail */
4024
bool quit_now = false;
4025
buffer password = {};
4026
bool mandos_client_exited = false;
4027
bool password_is_read = false;
4028
__attribute__((cleanup(cleanup_queue)))
4029
task_queue *queue = create_queue();
4030
g_assert_nonnull(queue);
4032
task_context task = {
4033
.func=read_inotify_event,
4036
.quit_now=&quit_now,
4037
.password=&password,
4038
.filename=strdup("/nonexistent"),
4039
.cancelled_filenames = &(string_set){},
4041
.current_time=¤t_time,
4042
.mandos_client_exited=&mandos_client_exited,
4043
.password_is_read=&password_is_read,
4045
g_assert_nonnull(task.filename);
4046
run_task_with_stderr_to_dev_null(task, queue);
4048
g_assert_nonnull(find_matching_task(queue, task));
4049
g_assert_true(queue->next_run == 1000000 + current_time);
4051
g_assert_cmpint(close(pipefds[0]), ==, 0);
4052
g_assert_cmpint(close(pipefds[1]), ==, 0);
4055
static void test_read_inotify_event_nodata(__attribute__((unused))
4056
test_fixture *fixture,
4057
__attribute__((unused))
4058
gconstpointer user_data){
4059
__attribute__((cleanup(cleanup_close)))
4060
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4061
g_assert_cmpint(epoll_fd, >=, 0);
4062
const mono_microsecs current_time = 0;
4065
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4067
bool quit_now = false;
4068
buffer password = {};
4069
bool mandos_client_exited = false;
4070
bool password_is_read = false;
4071
__attribute__((cleanup(cleanup_queue)))
4072
task_queue *queue = create_queue();
4073
g_assert_nonnull(queue);
4075
task_context task = {
4076
.func=read_inotify_event,
4079
.quit_now=&quit_now,
4080
.password=&password,
4081
.filename=strdup("/nonexistent"),
4082
.cancelled_filenames = &(string_set){},
4084
.current_time=¤t_time,
4085
.mandos_client_exited=&mandos_client_exited,
4086
.password_is_read=&password_is_read,
4088
g_assert_nonnull(task.filename);
4089
task.func(task, queue);
4090
g_assert_false(quit_now);
4091
g_assert_true(queue->next_run == 0);
4092
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4094
g_assert_nonnull(find_matching_task(queue, (task_context){
4095
.func=read_inotify_event,
4098
.quit_now=&quit_now,
4099
.password=&password,
4100
.filename=task.filename,
4101
.cancelled_filenames=task.cancelled_filenames,
4102
.current_time=¤t_time,
4103
.mandos_client_exited=&mandos_client_exited,
4104
.password_is_read=&password_is_read,
4107
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4108
EPOLLIN | EPOLLRDHUP));
4110
g_assert_cmpint(close(pipefds[1]), ==, 0);
4113
static void test_read_inotify_event_eof(__attribute__((unused))
4114
test_fixture *fixture,
4115
__attribute__((unused))
4116
gconstpointer user_data){
4117
__attribute__((cleanup(cleanup_close)))
4118
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4119
g_assert_cmpint(epoll_fd, >=, 0);
4120
const mono_microsecs current_time = 0;
4123
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4124
g_assert_cmpint(close(pipefds[1]), ==, 0);
4126
bool quit_now = false;
4127
buffer password = {};
4128
__attribute__((cleanup(cleanup_queue)))
4129
task_queue *queue = create_queue();
4130
g_assert_nonnull(queue);
4132
task_context task = {
4133
.func=read_inotify_event,
4136
.quit_now=&quit_now,
4137
.password=&password,
4138
.filename=strdup("/nonexistent"),
4139
.cancelled_filenames = &(string_set){},
4141
.current_time=¤t_time,
4143
run_task_with_stderr_to_dev_null(task, queue);
4144
g_assert_true(quit_now);
4145
g_assert_true(queue->next_run == 0);
4146
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4148
g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
4150
g_assert_cmpint(close(pipefds[0]), ==, -1);
4154
void test_read_inotify_event_IN_CLOSE_WRITE(__attribute__((unused))
4155
test_fixture *fixture,
4156
__attribute__((unused))
4157
gconstpointer user_data){
4158
__attribute__((cleanup(cleanup_close)))
4159
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4160
g_assert_cmpint(epoll_fd, >=, 0);
4161
const mono_microsecs current_time = 0;
4164
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4166
/* "sufficient to read at least one event." - inotify(7) */
4167
const size_t ievent_max_size = (sizeof(struct inotify_event)
4169
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4171
struct inotify_event event;
4172
char name_buffer[NAME_MAX + 1];
4174
struct inotify_event *const ievent = &ievent_buffer.event;
4176
const char dummy_file_name[] = "ask.dummy_file_name";
4177
ievent->mask = IN_CLOSE_WRITE;
4178
ievent->len = sizeof(dummy_file_name);
4179
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4180
const size_t ievent_size = (sizeof(struct inotify_event)
4181
+ sizeof(dummy_file_name));
4182
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4184
g_assert_cmpint(close(pipefds[1]), ==, 0);
4186
bool quit_now = false;
4187
buffer password = {};
4188
bool mandos_client_exited = false;
4189
bool password_is_read = false;
4190
__attribute__((cleanup(cleanup_queue)))
4191
task_queue *queue = create_queue();
4192
g_assert_nonnull(queue);
4194
task_context task = {
4195
.func=read_inotify_event,
4198
.quit_now=&quit_now,
4199
.password=&password,
4200
.filename=strdup("/nonexistent"),
4201
.cancelled_filenames = &(string_set){},
4203
.current_time=¤t_time,
4204
.mandos_client_exited=&mandos_client_exited,
4205
.password_is_read=&password_is_read,
4207
task.func(task, queue);
4208
g_assert_false(quit_now);
4209
g_assert_true(queue->next_run != 0);
4210
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4212
g_assert_nonnull(find_matching_task(queue, (task_context){
4213
.func=read_inotify_event,
4216
.quit_now=&quit_now,
4217
.password=&password,
4218
.filename=task.filename,
4219
.cancelled_filenames=task.cancelled_filenames,
4220
.current_time=¤t_time,
4221
.mandos_client_exited=&mandos_client_exited,
4222
.password_is_read=&password_is_read,
4225
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4226
EPOLLIN | EPOLLRDHUP));
4228
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4230
__attribute__((cleanup(cleanup_string)))
4231
char *filename = NULL;
4232
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4233
dummy_file_name), >, 0);
4234
g_assert_nonnull(filename);
4235
g_assert_nonnull(find_matching_task(queue, (task_context){
4236
.func=open_and_parse_question,
4239
.question_filename=filename,
4240
.password=&password,
4241
.cancelled_filenames=task.cancelled_filenames,
4242
.current_time=¤t_time,
4243
.mandos_client_exited=&mandos_client_exited,
4244
.password_is_read=&password_is_read,
4249
void test_read_inotify_event_IN_MOVED_TO(__attribute__((unused))
4250
test_fixture *fixture,
4251
__attribute__((unused))
4252
gconstpointer user_data){
4253
__attribute__((cleanup(cleanup_close)))
4254
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4255
g_assert_cmpint(epoll_fd, >=, 0);
4256
const mono_microsecs current_time = 0;
4259
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4261
/* "sufficient to read at least one event." - inotify(7) */
4262
const size_t ievent_max_size = (sizeof(struct inotify_event)
4264
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4266
struct inotify_event event;
4267
char name_buffer[NAME_MAX + 1];
4269
struct inotify_event *const ievent = &ievent_buffer.event;
4271
const char dummy_file_name[] = "ask.dummy_file_name";
4272
ievent->mask = IN_MOVED_TO;
4273
ievent->len = sizeof(dummy_file_name);
4274
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4275
const size_t ievent_size = (sizeof(struct inotify_event)
4276
+ sizeof(dummy_file_name));
4277
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4279
g_assert_cmpint(close(pipefds[1]), ==, 0);
4281
bool quit_now = false;
4282
buffer password = {};
4283
bool mandos_client_exited = false;
4284
bool password_is_read = false;
4285
__attribute__((cleanup(cleanup_queue)))
4286
task_queue *queue = create_queue();
4287
g_assert_nonnull(queue);
4289
task_context task = {
4290
.func=read_inotify_event,
4293
.quit_now=&quit_now,
4294
.password=&password,
4295
.filename=strdup("/nonexistent"),
4296
.cancelled_filenames = &(string_set){},
4298
.current_time=¤t_time,
4299
.mandos_client_exited=&mandos_client_exited,
4300
.password_is_read=&password_is_read,
4302
task.func(task, queue);
4303
g_assert_false(quit_now);
4304
g_assert_true(queue->next_run != 0);
4305
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4307
g_assert_nonnull(find_matching_task(queue, (task_context){
4308
.func=read_inotify_event,
4311
.quit_now=&quit_now,
4312
.password=&password,
4313
.filename=task.filename,
4314
.cancelled_filenames=task.cancelled_filenames,
4315
.current_time=¤t_time,
4316
.mandos_client_exited=&mandos_client_exited,
4317
.password_is_read=&password_is_read,
4320
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4321
EPOLLIN | EPOLLRDHUP));
4323
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4325
__attribute__((cleanup(cleanup_string)))
4326
char *filename = NULL;
4327
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4328
dummy_file_name), >, 0);
4329
g_assert_nonnull(filename);
4330
g_assert_nonnull(find_matching_task(queue, (task_context){
4331
.func=open_and_parse_question,
4334
.question_filename=filename,
4335
.password=&password,
4336
.cancelled_filenames=task.cancelled_filenames,
4337
.current_time=¤t_time,
4338
.mandos_client_exited=&mandos_client_exited,
4339
.password_is_read=&password_is_read,
4344
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
4345
test_fixture *fixture,
4346
__attribute__((unused))
4347
gconstpointer user_data){
4348
__attribute__((cleanup(cleanup_close)))
4349
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4350
g_assert_cmpint(epoll_fd, >=, 0);
4351
__attribute__((cleanup(string_set_clear)))
4352
string_set cancelled_filenames = {};
4353
const mono_microsecs current_time = 0;
4356
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4358
/* "sufficient to read at least one event." - inotify(7) */
4359
const size_t ievent_max_size = (sizeof(struct inotify_event)
4361
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4363
struct inotify_event event;
4364
char name_buffer[NAME_MAX + 1];
4366
struct inotify_event *const ievent = &ievent_buffer.event;
4368
const char dummy_file_name[] = "ask.dummy_file_name";
4369
ievent->mask = IN_MOVED_FROM;
4370
ievent->len = sizeof(dummy_file_name);
4371
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4372
const size_t ievent_size = (sizeof(struct inotify_event)
4373
+ sizeof(dummy_file_name));
4374
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4376
g_assert_cmpint(close(pipefds[1]), ==, 0);
4378
bool quit_now = false;
4379
buffer password = {};
4380
bool mandos_client_exited = false;
4381
bool password_is_read = false;
4382
__attribute__((cleanup(cleanup_queue)))
4383
task_queue *queue = create_queue();
4384
g_assert_nonnull(queue);
4386
task_context task = {
4387
.func=read_inotify_event,
4390
.quit_now=&quit_now,
4391
.password=&password,
4392
.filename=strdup("/nonexistent"),
4393
.cancelled_filenames=&cancelled_filenames,
4394
.current_time=¤t_time,
4395
.mandos_client_exited=&mandos_client_exited,
4396
.password_is_read=&password_is_read,
4398
task.func(task, queue);
4399
g_assert_false(quit_now);
4400
g_assert_true(queue->next_run == 0);
4401
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4403
g_assert_nonnull(find_matching_task(queue, (task_context){
4404
.func=read_inotify_event,
4407
.quit_now=&quit_now,
4408
.password=&password,
4409
.filename=task.filename,
4410
.cancelled_filenames=&cancelled_filenames,
4411
.current_time=¤t_time,
4412
.mandos_client_exited=&mandos_client_exited,
4413
.password_is_read=&password_is_read,
4416
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4417
EPOLLIN | EPOLLRDHUP));
4419
__attribute__((cleanup(cleanup_string)))
4420
char *filename = NULL;
4421
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4422
dummy_file_name), >, 0);
4423
g_assert_nonnull(filename);
4424
g_assert_true(string_set_contains(*task.cancelled_filenames,
4428
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4429
test_fixture *fixture,
4430
__attribute__((unused))
4433
__attribute__((cleanup(cleanup_close)))
4434
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4435
g_assert_cmpint(epoll_fd, >=, 0);
4436
__attribute__((cleanup(string_set_clear)))
4437
string_set cancelled_filenames = {};
4438
const mono_microsecs current_time = 0;
4441
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4443
/* "sufficient to read at least one event." - inotify(7) */
4444
const size_t ievent_max_size = (sizeof(struct inotify_event)
4446
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4448
struct inotify_event event;
4449
char name_buffer[NAME_MAX + 1];
4451
struct inotify_event *const ievent = &ievent_buffer.event;
4453
const char dummy_file_name[] = "ask.dummy_file_name";
4454
ievent->mask = IN_DELETE;
4455
ievent->len = sizeof(dummy_file_name);
4456
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4457
const size_t ievent_size = (sizeof(struct inotify_event)
4458
+ sizeof(dummy_file_name));
4459
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4461
g_assert_cmpint(close(pipefds[1]), ==, 0);
4463
bool quit_now = false;
4464
buffer password = {};
4465
bool mandos_client_exited = false;
4466
bool password_is_read = false;
4467
__attribute__((cleanup(cleanup_queue)))
4468
task_queue *queue = create_queue();
4469
g_assert_nonnull(queue);
4471
task_context task = {
4472
.func=read_inotify_event,
4475
.quit_now=&quit_now,
4476
.password=&password,
4477
.filename=strdup("/nonexistent"),
4478
.cancelled_filenames=&cancelled_filenames,
4479
.current_time=¤t_time,
4480
.mandos_client_exited=&mandos_client_exited,
4481
.password_is_read=&password_is_read,
4483
task.func(task, queue);
4484
g_assert_false(quit_now);
4485
g_assert_true(queue->next_run == 0);
4486
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4488
g_assert_nonnull(find_matching_task(queue, (task_context){
4489
.func=read_inotify_event,
4492
.quit_now=&quit_now,
4493
.password=&password,
4494
.filename=task.filename,
4495
.cancelled_filenames=&cancelled_filenames,
4496
.current_time=¤t_time,
4497
.mandos_client_exited=&mandos_client_exited,
4498
.password_is_read=&password_is_read,
4501
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4502
EPOLLIN | EPOLLRDHUP));
4504
__attribute__((cleanup(cleanup_string)))
4505
char *filename = NULL;
4506
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4507
dummy_file_name), >, 0);
4508
g_assert_nonnull(filename);
4509
g_assert_true(string_set_contains(*task.cancelled_filenames,
4514
test_read_inotify_event_IN_CLOSE_WRITE_badname(__attribute__((unused))
4515
test_fixture *fixture,
4516
__attribute__((unused))
4519
__attribute__((cleanup(cleanup_close)))
4520
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4521
g_assert_cmpint(epoll_fd, >=, 0);
4522
const mono_microsecs current_time = 0;
4525
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4527
/* "sufficient to read at least one event." - inotify(7) */
4528
const size_t ievent_max_size = (sizeof(struct inotify_event)
4530
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4532
struct inotify_event event;
4533
char name_buffer[NAME_MAX + 1];
4535
struct inotify_event *const ievent = &ievent_buffer.event;
4537
const char dummy_file_name[] = "ignored.dummy_file_name";
4538
ievent->mask = IN_CLOSE_WRITE;
4539
ievent->len = sizeof(dummy_file_name);
4540
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4541
const size_t ievent_size = (sizeof(struct inotify_event)
4542
+ sizeof(dummy_file_name));
4543
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4545
g_assert_cmpint(close(pipefds[1]), ==, 0);
4547
bool quit_now = false;
4548
buffer password = {};
4549
bool mandos_client_exited = false;
4550
bool password_is_read = false;
4551
__attribute__((cleanup(cleanup_queue)))
4552
task_queue *queue = create_queue();
4553
g_assert_nonnull(queue);
4555
task_context task = {
4556
.func=read_inotify_event,
4559
.quit_now=&quit_now,
4560
.password=&password,
4561
.filename=strdup("/nonexistent"),
4562
.cancelled_filenames = &(string_set){},
4564
.current_time=¤t_time,
4565
.mandos_client_exited=&mandos_client_exited,
4566
.password_is_read=&password_is_read,
4568
task.func(task, queue);
4569
g_assert_false(quit_now);
4570
g_assert_true(queue->next_run == 0);
4571
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4573
g_assert_nonnull(find_matching_task(queue, (task_context){
4574
.func=read_inotify_event,
4577
.quit_now=&quit_now,
4578
.password=&password,
4579
.filename=task.filename,
4580
.cancelled_filenames=task.cancelled_filenames,
4581
.current_time=¤t_time,
4582
.mandos_client_exited=&mandos_client_exited,
4583
.password_is_read=&password_is_read,
4586
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4587
EPOLLIN | EPOLLRDHUP));
4591
test_read_inotify_event_IN_MOVED_TO_badname(__attribute__((unused))
4592
test_fixture *fixture,
4593
__attribute__((unused))
4594
gconstpointer user_data){
4595
__attribute__((cleanup(cleanup_close)))
4596
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4597
g_assert_cmpint(epoll_fd, >=, 0);
4598
const mono_microsecs current_time = 0;
4601
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4603
/* "sufficient to read at least one event." - inotify(7) */
4604
const size_t ievent_max_size = (sizeof(struct inotify_event)
4606
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4608
struct inotify_event event;
4609
char name_buffer[NAME_MAX + 1];
4611
struct inotify_event *const ievent = &ievent_buffer.event;
4613
const char dummy_file_name[] = "ignored.dummy_file_name";
4614
ievent->mask = IN_MOVED_TO;
4615
ievent->len = sizeof(dummy_file_name);
4616
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4617
const size_t ievent_size = (sizeof(struct inotify_event)
4618
+ sizeof(dummy_file_name));
4619
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4621
g_assert_cmpint(close(pipefds[1]), ==, 0);
4623
bool quit_now = false;
4624
buffer password = {};
4625
bool mandos_client_exited = false;
4626
bool password_is_read = false;
4627
__attribute__((cleanup(cleanup_queue)))
4628
task_queue *queue = create_queue();
4629
g_assert_nonnull(queue);
4631
task_context task = {
4632
.func=read_inotify_event,
4635
.quit_now=&quit_now,
4636
.password=&password,
4637
.filename=strdup("/nonexistent"),
4638
.cancelled_filenames = &(string_set){},
4640
.current_time=¤t_time,
4641
.mandos_client_exited=&mandos_client_exited,
4642
.password_is_read=&password_is_read,
4644
task.func(task, queue);
4645
g_assert_false(quit_now);
4646
g_assert_true(queue->next_run == 0);
4647
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4649
g_assert_nonnull(find_matching_task(queue, (task_context){
4650
.func=read_inotify_event,
4653
.quit_now=&quit_now,
4654
.password=&password,
4655
.filename=task.filename,
4656
.cancelled_filenames=task.cancelled_filenames,
4657
.current_time=¤t_time,
4658
.mandos_client_exited=&mandos_client_exited,
4659
.password_is_read=&password_is_read,
4662
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4663
EPOLLIN | EPOLLRDHUP));
4667
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
4668
test_fixture *fixture,
4669
__attribute__((unused))
4672
__attribute__((cleanup(cleanup_close)))
4673
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4674
g_assert_cmpint(epoll_fd, >=, 0);
4675
__attribute__((cleanup(string_set_clear)))
4676
string_set cancelled_filenames = {};
4677
const mono_microsecs current_time = 0;
4680
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4682
/* "sufficient to read at least one event." - inotify(7) */
4683
const size_t ievent_max_size = (sizeof(struct inotify_event)
4685
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4687
struct inotify_event event;
4688
char name_buffer[NAME_MAX + 1];
4690
struct inotify_event *const ievent = &ievent_buffer.event;
4692
const char dummy_file_name[] = "ignored.dummy_file_name";
4693
ievent->mask = IN_MOVED_FROM;
4694
ievent->len = sizeof(dummy_file_name);
4695
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4696
const size_t ievent_size = (sizeof(struct inotify_event)
4697
+ sizeof(dummy_file_name));
4698
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4700
g_assert_cmpint(close(pipefds[1]), ==, 0);
4702
bool quit_now = false;
4703
buffer password = {};
4704
bool mandos_client_exited = false;
4705
bool password_is_read = false;
4706
__attribute__((cleanup(cleanup_queue)))
4707
task_queue *queue = create_queue();
4708
g_assert_nonnull(queue);
4710
task_context task = {
4711
.func=read_inotify_event,
4714
.quit_now=&quit_now,
4715
.password=&password,
4716
.filename=strdup("/nonexistent"),
4717
.cancelled_filenames=&cancelled_filenames,
4718
.current_time=¤t_time,
4719
.mandos_client_exited=&mandos_client_exited,
4720
.password_is_read=&password_is_read,
4722
task.func(task, queue);
4723
g_assert_false(quit_now);
4724
g_assert_true(queue->next_run == 0);
4725
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4727
g_assert_nonnull(find_matching_task(queue, (task_context){
4728
.func=read_inotify_event,
4731
.quit_now=&quit_now,
4732
.password=&password,
4733
.filename=task.filename,
4734
.cancelled_filenames=&cancelled_filenames,
4735
.current_time=¤t_time,
4736
.mandos_client_exited=&mandos_client_exited,
4737
.password_is_read=&password_is_read,
4740
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4741
EPOLLIN | EPOLLRDHUP));
4743
__attribute__((cleanup(cleanup_string)))
4744
char *filename = NULL;
4745
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4746
dummy_file_name), >, 0);
4747
g_assert_nonnull(filename);
4748
g_assert_false(string_set_contains(cancelled_filenames, filename));
4752
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4753
test_fixture *fixture,
4754
__attribute__((unused))
4757
__attribute__((cleanup(cleanup_close)))
4758
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4759
g_assert_cmpint(epoll_fd, >=, 0);
4760
__attribute__((cleanup(string_set_clear)))
4761
string_set cancelled_filenames = {};
4762
const mono_microsecs current_time = 0;
4765
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4767
/* "sufficient to read at least one event." - inotify(7) */
4768
const size_t ievent_max_size = (sizeof(struct inotify_event)
4770
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4772
struct inotify_event event;
4773
char name_buffer[NAME_MAX + 1];
4775
struct inotify_event *const ievent = &ievent_buffer.event;
4777
const char dummy_file_name[] = "ignored.dummy_file_name";
4778
ievent->mask = IN_DELETE;
4779
ievent->len = sizeof(dummy_file_name);
4780
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4781
const size_t ievent_size = (sizeof(struct inotify_event)
4782
+ sizeof(dummy_file_name));
4783
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4785
g_assert_cmpint(close(pipefds[1]), ==, 0);
4787
bool quit_now = false;
4788
buffer password = {};
4789
bool mandos_client_exited = false;
4790
bool password_is_read = false;
4791
__attribute__((cleanup(cleanup_queue)))
4792
task_queue *queue = create_queue();
4793
g_assert_nonnull(queue);
4795
task_context task = {
4796
.func=read_inotify_event,
4799
.quit_now=&quit_now,
4800
.password=&password,
4801
.filename=strdup("/nonexistent"),
4802
.cancelled_filenames=&cancelled_filenames,
4803
.current_time=¤t_time,
4804
.mandos_client_exited=&mandos_client_exited,
4805
.password_is_read=&password_is_read,
4807
task.func(task, queue);
4808
g_assert_false(quit_now);
4809
g_assert_true(queue->next_run == 0);
4810
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4812
g_assert_nonnull(find_matching_task(queue, (task_context){
4813
.func=read_inotify_event,
4816
.quit_now=&quit_now,
4817
.password=&password,
4818
.filename=task.filename,
4819
.cancelled_filenames=&cancelled_filenames,
4820
.current_time=¤t_time,
4821
.mandos_client_exited=&mandos_client_exited,
4822
.password_is_read=&password_is_read,
4825
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4826
EPOLLIN | EPOLLRDHUP));
4828
__attribute__((cleanup(cleanup_string)))
4829
char *filename = NULL;
4830
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4831
dummy_file_name), >, 0);
4832
g_assert_nonnull(filename);
4833
g_assert_false(string_set_contains(cancelled_filenames, filename));
4837
void test_open_and_parse_question_ENOENT(__attribute__((unused))
4838
test_fixture *fixture,
4839
__attribute__((unused))
4840
gconstpointer user_data){
4841
__attribute__((cleanup(cleanup_close)))
4842
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4843
g_assert_cmpint(epoll_fd, >=, 0);
4844
__attribute__((cleanup(string_set_clear)))
4845
string_set cancelled_filenames = {};
4846
bool mandos_client_exited = false;
4847
bool password_is_read = false;
4848
__attribute__((cleanup(cleanup_queue)))
4849
task_queue *queue = create_queue();
4850
g_assert_nonnull(queue);
4852
char *const filename = strdup("/nonexistent");
4853
g_assert_nonnull(filename);
4854
task_context task = {
4855
.func=open_and_parse_question,
4856
.question_filename=filename,
4858
.password=(buffer[]){{}},
4860
.cancelled_filenames=&cancelled_filenames,
4861
.current_time=(mono_microsecs[]){0},
4862
.mandos_client_exited=&mandos_client_exited,
4863
.password_is_read=&password_is_read,
4865
task.func(task, queue);
4866
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4869
static void test_open_and_parse_question_EIO(__attribute__((unused))
4870
test_fixture *fixture,
4871
__attribute__((unused))
4872
gconstpointer user_data){
4873
__attribute__((cleanup(cleanup_close)))
4874
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4875
g_assert_cmpint(epoll_fd, >=, 0);
4876
__attribute__((cleanup(string_set_clear)))
4877
string_set cancelled_filenames = {};
4878
buffer password = {};
4879
bool mandos_client_exited = false;
4880
bool password_is_read = false;
4881
__attribute__((cleanup(cleanup_queue)))
4882
task_queue *queue = create_queue();
4883
g_assert_nonnull(queue);
4884
const mono_microsecs current_time = 0;
4886
char *filename = strdup("/proc/self/mem");
4887
task_context task = {
4888
.func=open_and_parse_question,
4889
.question_filename=filename,
4891
.password=&password,
4893
.cancelled_filenames=&cancelled_filenames,
4894
.current_time=¤t_time,
4895
.mandos_client_exited=&mandos_client_exited,
4896
.password_is_read=&password_is_read,
4898
run_task_with_stderr_to_dev_null(task, queue);
4899
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4903
test_open_and_parse_question_parse_error(__attribute__((unused))
4904
test_fixture *fixture,
4905
__attribute__((unused))
4906
gconstpointer user_data){
4907
__attribute__((cleanup(cleanup_close)))
4908
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4909
g_assert_cmpint(epoll_fd, >=, 0);
4910
__attribute__((cleanup(string_set_clear)))
4911
string_set cancelled_filenames = {};
4912
__attribute__((cleanup(cleanup_queue)))
4913
task_queue *queue = create_queue();
4914
g_assert_nonnull(queue);
4916
__attribute__((cleanup(cleanup_string)))
4917
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4918
g_assert_nonnull(tempfilename);
4919
int tempfile = mkostemp(tempfilename, O_CLOEXEC);
4920
g_assert_cmpint(tempfile, >, 0);
4921
const char bad_data[] = "this is bad syntax\n";
4922
g_assert_cmpint(write(tempfile, bad_data, sizeof(bad_data)),
4923
==, sizeof(bad_data));
4924
g_assert_cmpint(close(tempfile), ==, 0);
4926
char *const filename = strdup(tempfilename);
4927
g_assert_nonnull(filename);
4928
task_context task = {
4929
.func=open_and_parse_question,
4930
.question_filename=filename,
4932
.password=(buffer[]){{}},
4934
.cancelled_filenames=&cancelled_filenames,
4935
.current_time=(mono_microsecs[]){0},
4936
.mandos_client_exited=(bool[]){false},
4937
.password_is_read=(bool[]){false},
4939
run_task_with_stderr_to_dev_null(task, queue);
4941
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4943
g_assert_cmpint(unlink(tempfilename), ==, 0);
4947
void test_open_and_parse_question_nosocket(__attribute__((unused))
4948
test_fixture *fixture,
4949
__attribute__((unused))
4950
gconstpointer user_data){
4951
__attribute__((cleanup(cleanup_close)))
4952
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4953
g_assert_cmpint(epoll_fd, >=, 0);
4954
__attribute__((cleanup(string_set_clear)))
4955
string_set cancelled_filenames = {};
4956
__attribute__((cleanup(cleanup_queue)))
4957
task_queue *queue = create_queue();
4958
g_assert_nonnull(queue);
4960
__attribute__((cleanup(cleanup_string)))
4961
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4962
g_assert_nonnull(tempfilename);
4963
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4964
g_assert_cmpint(questionfile, >, 0);
4965
FILE *qf = fdopen(questionfile, "w");
4966
g_assert_cmpint(fprintf(qf, "[Ask]\nPID=1\n"), >, 0);
4967
g_assert_cmpint(fclose(qf), ==, 0);
4969
char *const filename = strdup(tempfilename);
4970
g_assert_nonnull(filename);
4971
task_context task = {
4972
.func=open_and_parse_question,
4973
.question_filename=filename,
4975
.password=(buffer[]){{}},
4977
.cancelled_filenames=&cancelled_filenames,
4978
.current_time=(mono_microsecs[]){0},
4979
.mandos_client_exited=(bool[]){false},
4980
.password_is_read=(bool[]){false},
4982
run_task_with_stderr_to_dev_null(task, queue);
4983
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4985
g_assert_cmpint(unlink(tempfilename), ==, 0);
4989
void test_open_and_parse_question_badsocket(__attribute__((unused))
4990
test_fixture *fixture,
4991
__attribute__((unused))
4992
gconstpointer user_data){
4993
__attribute__((cleanup(cleanup_close)))
4994
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4995
g_assert_cmpint(epoll_fd, >=, 0);
4996
__attribute__((cleanup(string_set_clear)))
4997
string_set cancelled_filenames = {};
4998
__attribute__((cleanup(cleanup_queue)))
4999
task_queue *queue = create_queue();
5000
g_assert_nonnull(queue);
5002
__attribute__((cleanup(cleanup_string)))
5003
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5004
g_assert_nonnull(tempfilename);
5005
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5006
g_assert_cmpint(questionfile, >, 0);
5007
FILE *qf = fdopen(questionfile, "w");
5008
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=\nPID=1\n"), >, 0);
5009
g_assert_cmpint(fclose(qf), ==, 0);
5011
char *const filename = strdup(tempfilename);
5012
g_assert_nonnull(filename);
5013
task_context task = {
5014
.func=open_and_parse_question,
5015
.question_filename=filename,
5017
.password=(buffer[]){{}},
5019
.cancelled_filenames=&cancelled_filenames,
5020
.current_time=(mono_microsecs[]){0},
5021
.mandos_client_exited=(bool[]){false},
5022
.password_is_read=(bool[]){false},
5024
run_task_with_stderr_to_dev_null(task, queue);
5025
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5027
g_assert_cmpint(unlink(tempfilename), ==, 0);
5031
void test_open_and_parse_question_nopid(__attribute__((unused))
5032
test_fixture *fixture,
5033
__attribute__((unused))
5034
gconstpointer user_data){
5035
__attribute__((cleanup(cleanup_close)))
5036
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5037
g_assert_cmpint(epoll_fd, >=, 0);
5038
__attribute__((cleanup(string_set_clear)))
5039
string_set cancelled_filenames = {};
5040
__attribute__((cleanup(cleanup_queue)))
5041
task_queue *queue = create_queue();
5042
g_assert_nonnull(queue);
5044
__attribute__((cleanup(cleanup_string)))
5045
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5046
g_assert_nonnull(tempfilename);
5047
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5048
g_assert_cmpint(questionfile, >, 0);
5049
FILE *qf = fdopen(questionfile, "w");
5050
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\n"), >, 0);
5051
g_assert_cmpint(fclose(qf), ==, 0);
5053
char *const filename = strdup(tempfilename);
5054
g_assert_nonnull(filename);
5055
task_context task = {
5056
.func=open_and_parse_question,
5057
.question_filename=filename,
5059
.password=(buffer[]){{}},
5061
.cancelled_filenames=&cancelled_filenames,
5062
.current_time=(mono_microsecs[]){0},
5063
.mandos_client_exited=(bool[]){false},
5064
.password_is_read=(bool[]){false},
5066
run_task_with_stderr_to_dev_null(task, queue);
5067
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5069
g_assert_cmpint(unlink(tempfilename), ==, 0);
5073
void test_open_and_parse_question_badpid(__attribute__((unused))
5074
test_fixture *fixture,
5075
__attribute__((unused))
5076
gconstpointer user_data){
5077
__attribute__((cleanup(cleanup_close)))
5078
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5079
g_assert_cmpint(epoll_fd, >=, 0);
5080
__attribute__((cleanup(string_set_clear)))
5081
string_set cancelled_filenames = {};
5082
__attribute__((cleanup(cleanup_queue)))
5083
task_queue *queue = create_queue();
5084
g_assert_nonnull(queue);
5086
__attribute__((cleanup(cleanup_string)))
5087
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5088
g_assert_nonnull(tempfilename);
5089
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5090
g_assert_cmpint(questionfile, >, 0);
5091
FILE *qf = fdopen(questionfile, "w");
5092
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=\n"),
5094
g_assert_cmpint(fclose(qf), ==, 0);
5096
char *const filename = strdup(tempfilename);
5097
g_assert_nonnull(filename);
5098
task_context task = {
5099
.func=open_and_parse_question,
5100
.question_filename=filename,
5102
.password=(buffer[]){{}},
5104
.cancelled_filenames=&cancelled_filenames,
5105
.current_time=(mono_microsecs[]){0},
5106
.mandos_client_exited=(bool[]){false},
5107
.password_is_read=(bool[]){false},
5109
run_task_with_stderr_to_dev_null(task, queue);
5110
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5112
g_assert_cmpint(unlink(tempfilename), ==, 0);
5116
test_open_and_parse_question_noexist_pid(__attribute__((unused))
5117
test_fixture *fixture,
5118
__attribute__((unused))
5119
gconstpointer user_data){
5120
__attribute__((cleanup(cleanup_close)))
5121
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5122
g_assert_cmpint(epoll_fd, >=, 0);
5123
__attribute__((cleanup(string_set_clear)))
5124
string_set cancelled_filenames = {};
5125
buffer password = {};
5126
bool mandos_client_exited = false;
5127
bool password_is_read = false;
5128
__attribute__((cleanup(cleanup_queue)))
5129
task_queue *queue = create_queue();
5130
g_assert_nonnull(queue);
5131
const mono_microsecs current_time = 0;
5133
/* Find value of sysctl kernel.pid_max */
5134
uintmax_t pid_max = 0;
5135
FILE *sysctl_pid_max = fopen("/proc/sys/kernel/pid_max", "r");
5136
g_assert_nonnull(sysctl_pid_max);
5137
g_assert_cmpint(fscanf(sysctl_pid_max, "%" PRIuMAX, &pid_max),
5139
g_assert_cmpint(fclose(sysctl_pid_max), ==, 0);
5141
pid_t nonexisting_pid = ((pid_t)pid_max)+1;
5142
g_assert_true(nonexisting_pid > 0); /* Overflow check */
5144
__attribute__((cleanup(cleanup_string)))
5145
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5146
g_assert_nonnull(tempfilename);
5147
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5148
g_assert_cmpint(questionfile, >, 0);
5149
FILE *qf = fdopen(questionfile, "w");
5150
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5151
PRIuMAX"\n", (uintmax_t)nonexisting_pid),
5153
g_assert_cmpint(fclose(qf), ==, 0);
5155
char *const question_filename = strdup(tempfilename);
5156
g_assert_nonnull(question_filename);
5157
task_context task = {
5158
.func=open_and_parse_question,
5159
.question_filename=question_filename,
5161
.password=&password,
5162
.filename=question_filename,
5163
.cancelled_filenames=&cancelled_filenames,
5164
.current_time=¤t_time,
5165
.mandos_client_exited=&mandos_client_exited,
5166
.password_is_read=&password_is_read,
5168
run_task_with_stderr_to_dev_null(task, queue);
5169
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5171
g_assert_cmpint(unlink(tempfilename), ==, 0);
5175
test_open_and_parse_question_no_notafter(__attribute__((unused))
5176
test_fixture *fixture,
5177
__attribute__((unused))
5178
gconstpointer user_data){
5179
__attribute__((cleanup(cleanup_close)))
5180
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5181
g_assert_cmpint(epoll_fd, >=, 0);
5182
__attribute__((cleanup(string_set_clear)))
5183
string_set cancelled_filenames = {};
5184
buffer password = {};
5185
bool mandos_client_exited = false;
5186
bool password_is_read = false;
5187
__attribute__((cleanup(cleanup_queue)))
5188
task_queue *queue = create_queue();
5189
g_assert_nonnull(queue);
5190
const mono_microsecs current_time = 0;
5192
__attribute__((cleanup(cleanup_string)))
5193
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5194
g_assert_nonnull(tempfilename);
5195
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5196
g_assert_cmpint(questionfile, >, 0);
5197
FILE *qf = fdopen(questionfile, "w");
5198
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5199
PRIuMAX "\n", (uintmax_t)getpid()), >, 0);
5200
g_assert_cmpint(fclose(qf), ==, 0);
5202
char *const filename = strdup(tempfilename);
5203
g_assert_nonnull(filename);
5204
task_context task = {
5205
.func=open_and_parse_question,
5206
.question_filename=filename,
5208
.password=&password,
5210
.cancelled_filenames=&cancelled_filenames,
5211
.current_time=¤t_time,
5212
.mandos_client_exited=&mandos_client_exited,
5213
.password_is_read=&password_is_read,
5215
task.func(task, queue);
5216
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5218
__attribute__((cleanup(cleanup_string)))
5219
char *socket_filename = strdup("/nonexistent");
5220
g_assert_nonnull(socket_filename);
5221
g_assert_nonnull(find_matching_task(queue, (task_context){
5222
.func=connect_question_socket,
5223
.question_filename=tempfilename,
5224
.filename=socket_filename,
5226
.password=&password,
5227
.current_time=¤t_time,
5228
.mandos_client_exited=&mandos_client_exited,
5229
.password_is_read=&password_is_read,
5232
g_assert_true(queue->next_run != 0);
5234
g_assert_cmpint(unlink(tempfilename), ==, 0);
5238
test_open_and_parse_question_bad_notafter(__attribute__((unused))
5239
test_fixture *fixture,
5240
__attribute__((unused))
5241
gconstpointer user_data){
5242
__attribute__((cleanup(cleanup_close)))
5243
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5244
g_assert_cmpint(epoll_fd, >=, 0);
5245
__attribute__((cleanup(string_set_clear)))
5246
string_set cancelled_filenames = {};
5247
buffer password = {};
5248
bool mandos_client_exited = false;
5249
bool password_is_read = false;
5250
__attribute__((cleanup(cleanup_queue)))
5251
task_queue *queue = create_queue();
5252
g_assert_nonnull(queue);
5253
const mono_microsecs current_time = 0;
5255
__attribute__((cleanup(cleanup_string)))
5256
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5257
g_assert_nonnull(tempfilename);
5258
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5259
g_assert_cmpint(questionfile, >, 0);
5260
FILE *qf = fdopen(questionfile, "w");
5261
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5262
PRIuMAX "\nNotAfter=\n",
5263
(uintmax_t)getpid()), >, 0);
5264
g_assert_cmpint(fclose(qf), ==, 0);
5266
char *const filename = strdup(tempfilename);
5267
g_assert_nonnull(filename);
5268
task_context task = {
5269
.func=open_and_parse_question,
5270
.question_filename=filename,
5272
.password=&password,
5274
.cancelled_filenames=&cancelled_filenames,
5275
.current_time=¤t_time,
5276
.mandos_client_exited=&mandos_client_exited,
5277
.password_is_read=&password_is_read,
5279
run_task_with_stderr_to_dev_null(task, queue);
5280
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5282
__attribute__((cleanup(cleanup_string)))
5283
char *socket_filename = strdup("/nonexistent");
5284
g_assert_nonnull(find_matching_task(queue, (task_context){
5285
.func=connect_question_socket,
5286
.question_filename=tempfilename,
5287
.filename=socket_filename,
5289
.password=&password,
5290
.current_time=¤t_time,
5291
.mandos_client_exited=&mandos_client_exited,
5292
.password_is_read=&password_is_read,
5294
g_assert_true(queue->next_run != 0);
5296
g_assert_cmpint(unlink(tempfilename), ==, 0);
5300
void assert_open_and_parse_question_with_notafter(const mono_microsecs
5302
const mono_microsecs
5304
const mono_microsecs
5306
__attribute__((cleanup(cleanup_close)))
5307
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5308
g_assert_cmpint(epoll_fd, >=, 0);
5309
__attribute__((cleanup(string_set_clear)))
5310
string_set cancelled_filenames = {};
5311
buffer password = {};
5312
bool mandos_client_exited = false;
5313
bool password_is_read = false;
5314
__attribute__((cleanup(cleanup_queue)))
5315
task_queue *queue = create_queue();
5316
g_assert_nonnull(queue);
5317
queue->next_run = next_queue_run;
5319
__attribute__((cleanup(cleanup_string)))
5320
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5321
g_assert_nonnull(tempfilename);
5322
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5323
g_assert_cmpint(questionfile, >, 0);
5324
FILE *qf = fdopen(questionfile, "w");
5325
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5326
PRIuMAX "\nNotAfter=%" PRIuMAX "\n",
5327
(uintmax_t)getpid(), notafter), >, 0);
5328
g_assert_cmpint(fclose(qf), ==, 0);
5330
char *const filename = strdup(tempfilename);
5331
g_assert_nonnull(filename);
5332
task_context task = {
5333
.func=open_and_parse_question,
5334
.question_filename=filename,
5336
.password=&password,
5338
.cancelled_filenames=&cancelled_filenames,
5339
.current_time=¤t_time,
5340
.mandos_client_exited=&mandos_client_exited,
5341
.password_is_read=&password_is_read,
5343
task.func(task, queue);
5345
if(queue->length >= 1){
5346
__attribute__((cleanup(cleanup_string)))
5347
char *socket_filename = strdup("/nonexistent");
5348
g_assert_nonnull(find_matching_task(queue, (task_context){
5349
.func=connect_question_socket,
5350
.filename=socket_filename,
5352
.password=&password,
5353
.current_time=¤t_time,
5354
.cancelled_filenames=&cancelled_filenames,
5355
.mandos_client_exited=&mandos_client_exited,
5356
.password_is_read=&password_is_read,
5358
g_assert_true(queue->next_run != 0);
5362
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5363
} else if(current_time >= notafter) {
5364
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5366
g_assert_nonnull(find_matching_task(queue, (task_context){
5367
.func=cancel_old_question,
5368
.question_filename=tempfilename,
5369
.filename=tempfilename,
5371
.cancelled_filenames=&cancelled_filenames,
5372
.current_time=¤t_time,
5375
g_assert_true(queue->next_run == 1);
5377
g_assert_cmpint(unlink(tempfilename), ==, 0);
5381
test_open_and_parse_question_notafter_0(__attribute__((unused))
5382
test_fixture *fixture,
5383
__attribute__((unused))
5384
gconstpointer user_data){
5385
/* current_time, notafter, next_queue_run */
5386
assert_open_and_parse_question_with_notafter(0, 0, 0);
5390
test_open_and_parse_question_notafter_1(__attribute__((unused))
5391
test_fixture *fixture,
5392
__attribute__((unused))
5393
gconstpointer user_data){
5394
/* current_time, notafter, next_queue_run */
5395
assert_open_and_parse_question_with_notafter(0, 1, 0);
5399
test_open_and_parse_question_notafter_1_1(__attribute__((unused))
5400
test_fixture *fixture,
5401
__attribute__((unused))
5402
gconstpointer user_data){
5403
/* current_time, notafter, next_queue_run */
5404
assert_open_and_parse_question_with_notafter(0, 1, 1);
5408
test_open_and_parse_question_notafter_1_2(__attribute__((unused))
5409
test_fixture *fixture,
5410
__attribute__((unused))
5411
gconstpointer user_data){
5412
/* current_time, notafter, next_queue_run */
5413
assert_open_and_parse_question_with_notafter(0, 1, 2);
5417
test_open_and_parse_question_equal_notafter(__attribute__((unused))
5418
test_fixture *fixture,
5419
__attribute__((unused))
5420
gconstpointer user_data){
5421
/* current_time, notafter, next_queue_run */
5422
assert_open_and_parse_question_with_notafter(1, 1, 0);
5426
test_open_and_parse_question_late_notafter(__attribute__((unused))
5427
test_fixture *fixture,
5428
__attribute__((unused))
5429
gconstpointer user_data){
5430
/* current_time, notafter, next_queue_run */
5431
assert_open_and_parse_question_with_notafter(2, 1, 0);
5434
static void assert_cancel_old_question_param(const mono_microsecs
5436
const mono_microsecs
5438
const mono_microsecs
5440
const mono_microsecs
5442
__attribute__((cleanup(string_set_clear)))
5443
string_set cancelled_filenames = {};
5444
__attribute__((cleanup(cleanup_queue)))
5445
task_queue *queue = create_queue();
5446
g_assert_nonnull(queue);
5447
queue->next_run = next_queue_run;
5449
char *const question_filename = strdup("/nonexistent");
5450
g_assert_nonnull(question_filename);
5451
task_context task = {
5452
.func=cancel_old_question,
5453
.question_filename=question_filename,
5454
.filename=question_filename,
5456
.cancelled_filenames=&cancelled_filenames,
5457
.current_time=¤t_time,
5459
task.func(task, queue);
5461
if(current_time >= notafter){
5462
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5463
g_assert_true(string_set_contains(cancelled_filenames,
5466
g_assert_nonnull(find_matching_task(queue, (task_context){
5467
.func=cancel_old_question,
5468
.question_filename=question_filename,
5469
.filename=question_filename,
5471
.cancelled_filenames=&cancelled_filenames,
5472
.current_time=¤t_time,
5475
g_assert_false(string_set_contains(cancelled_filenames,
5476
question_filename));
5478
g_assert_cmpuint((unsigned int)queue->next_run, ==,
5479
(unsigned int)next_set_to);
5482
static void test_cancel_old_question_0_1_2(__attribute__((unused))
5483
test_fixture *fixture,
5484
__attribute__((unused))
5485
gconstpointer user_data){
5486
/* next_queue_run unset,
5487
cancellation should happen because time has come,
5488
next_queue_run should be unchanged */
5489
/* next_queue_run, notafter, current_time, next_set_to */
5490
assert_cancel_old_question_param(0, 1, 2, 0);
5493
static void test_cancel_old_question_0_2_1(__attribute__((unused))
5494
test_fixture *fixture,
5495
__attribute__((unused))
5496
gconstpointer user_data){
5497
/* If next_queue_run is 0, meaning unset, and notafter is 2,
5498
and current_time is not yet notafter or greater,
5499
update value of next_queue_run to value of notafter */
5500
/* next_queue_run, notafter, current_time, next_set_to */
5501
assert_cancel_old_question_param(0, 2, 1, 2);
5504
static void test_cancel_old_question_1_2_3(__attribute__((unused))
5505
test_fixture *fixture,
5506
__attribute__((unused))
5507
gconstpointer user_data){
5508
/* next_queue_run 1,
5509
cancellation should happen because time has come,
5510
next_queue_run should be unchanged */
5511
/* next_queue_run, notafter, current_time, next_set_to */
5512
assert_cancel_old_question_param(1, 2, 3, 1);
5515
static void test_cancel_old_question_1_3_2(__attribute__((unused))
5516
test_fixture *fixture,
5517
__attribute__((unused))
5518
gconstpointer user_data){
5519
/* If next_queue_run is set,
5520
and current_time is not yet notafter or greater,
5521
and notafter is larger than next_queue_run
5522
next_queue_run should be unchanged */
5523
/* next_queue_run, notafter, current_time, next_set_to */
5524
assert_cancel_old_question_param(1, 3, 2, 1);
5527
static void test_cancel_old_question_2_1_3(__attribute__((unused))
5528
test_fixture *fixture,
5529
__attribute__((unused))
5530
gconstpointer user_data){
5531
/* next_queue_run 2,
5532
cancellation should happen because time has come,
5533
next_queue_run should be unchanged */
5534
/* next_queue_run, notafter, current_time, next_set_to */
5535
assert_cancel_old_question_param(2, 1, 3, 2);
5538
static void test_cancel_old_question_2_3_1(__attribute__((unused))
5539
test_fixture *fixture,
5540
__attribute__((unused))
5541
gconstpointer user_data){
5542
/* If next_queue_run is set,
5543
and current_time is not yet notafter or greater,
5544
and notafter is larger than next_queue_run
5545
next_queue_run should be unchanged */
5546
/* next_queue_run, notafter, current_time, next_set_to */
5547
assert_cancel_old_question_param(2, 3, 1, 2);
5550
static void test_cancel_old_question_3_1_2(__attribute__((unused))
5551
test_fixture *fixture,
5552
__attribute__((unused))
5553
gconstpointer user_data){
5554
/* next_queue_run 3,
5555
cancellation should happen because time has come,
5556
next_queue_run should be unchanged */
5557
/* next_queue_run, notafter, current_time, next_set_to */
5558
assert_cancel_old_question_param(3, 1, 2, 3);
5561
static void test_cancel_old_question_3_2_1(__attribute__((unused))
5562
test_fixture *fixture,
5563
__attribute__((unused))
5564
gconstpointer user_data){
5565
/* If next_queue_run is set,
5566
and current_time is not yet notafter or greater,
5567
and notafter is smaller than next_queue_run
5568
update value of next_queue_run to value of notafter */
5569
/* next_queue_run, notafter, current_time, next_set_to */
5570
assert_cancel_old_question_param(3, 2, 1, 2);
5574
test_connect_question_socket_name_too_long(__attribute__((unused))
5575
test_fixture *fixture,
5576
__attribute__((unused))
5577
gconstpointer user_data){
5578
__attribute__((cleanup(cleanup_close)))
5579
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5580
g_assert_cmpint(epoll_fd, >=, 0);
5581
const char question_filename[] = "/nonexistent/question";
5582
__attribute__((cleanup(string_set_clear)))
5583
string_set cancelled_filenames = {};
5584
__attribute__((cleanup(cleanup_queue)))
5585
task_queue *queue = create_queue();
5586
g_assert_nonnull(queue);
5587
__attribute__((cleanup(cleanup_string)))
5588
char *tempdir = make_temporary_directory();
5589
g_assert_nonnull(tempdir);
5590
struct sockaddr_un unix_socket = { .sun_family=AF_LOCAL };
5591
char socket_name[sizeof(unix_socket.sun_path)];
5592
memset(socket_name, 'x', sizeof(socket_name));
5593
socket_name[sizeof(socket_name)-1] = '\0';
5594
char *filename = NULL;
5595
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5597
g_assert_nonnull(filename);
5599
task_context task = {
5600
.func=connect_question_socket,
5601
.question_filename=strdup(question_filename),
5603
.password=(buffer[]){{}},
5605
.cancelled_filenames=&cancelled_filenames,
5606
.mandos_client_exited=(bool[]){false},
5607
.password_is_read=(bool[]){false},
5608
.current_time=(mono_microsecs[]){0},
5610
g_assert_nonnull(task.question_filename);
5611
run_task_with_stderr_to_dev_null(task, queue);
5613
g_assert_true(string_set_contains(cancelled_filenames,
5614
question_filename));
5615
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5616
g_assert_true(queue->next_run == 0);
5618
g_assert_cmpint(rmdir(tempdir), ==, 0);
5622
void test_connect_question_socket_connect_fail(__attribute__((unused))
5623
test_fixture *fixture,
5624
__attribute__((unused))
5627
__attribute__((cleanup(cleanup_close)))
5628
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5629
g_assert_cmpint(epoll_fd, >=, 0);
5630
const char question_filename[] = "/nonexistent/question";
5631
__attribute__((cleanup(string_set_clear)))
5632
string_set cancelled_filenames = {};
5633
const mono_microsecs current_time = 3;
5634
__attribute__((cleanup(cleanup_queue)))
5635
task_queue *queue = create_queue();
5636
g_assert_nonnull(queue);
5637
__attribute__((cleanup(cleanup_string)))
5638
char *tempdir = make_temporary_directory();
5639
g_assert_nonnull(tempdir);
5640
char socket_name[] = "nonexistent";
5641
char *filename = NULL;
5642
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5644
g_assert_nonnull(filename);
5646
task_context task = {
5647
.func=connect_question_socket,
5648
.question_filename=strdup(question_filename),
5650
.password=(buffer[]){{}},
5652
.cancelled_filenames=&cancelled_filenames,
5653
.mandos_client_exited=(bool[]){false},
5654
.password_is_read=(bool[]){false},
5655
.current_time=¤t_time,
5657
g_assert_nonnull(task.question_filename);
5658
run_task_with_stderr_to_dev_null(task, queue);
5660
g_assert_nonnull(find_matching_task(queue, task));
5662
g_assert_false(string_set_contains(cancelled_filenames,
5663
question_filename));
5664
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5665
g_assert_true(queue->next_run == 1000000 + current_time);
5667
g_assert_cmpint(rmdir(tempdir), ==, 0);
5671
void test_connect_question_socket_bad_epoll(__attribute__((unused))
5672
test_fixture *fixture,
5673
__attribute__((unused))
5674
gconstpointer user_data){
5675
__attribute__((cleanup(cleanup_close)))
5676
const int epoll_fd = open("/dev/null",
5677
O_WRONLY | O_CLOEXEC | O_NOCTTY);
5678
__attribute__((cleanup(cleanup_string)))
5679
char *const question_filename = strdup("/nonexistent/question");
5680
g_assert_nonnull(question_filename);
5681
__attribute__((cleanup(string_set_clear)))
5682
string_set cancelled_filenames = {};
5683
const mono_microsecs current_time = 5;
5684
__attribute__((cleanup(cleanup_queue)))
5685
task_queue *queue = create_queue();
5686
g_assert_nonnull(queue);
5687
__attribute__((cleanup(cleanup_string)))
5688
char *tempdir = make_temporary_directory();
5689
g_assert_nonnull(tempdir);
5690
__attribute__((cleanup(cleanup_close)))
5691
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5692
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5693
g_assert_cmpint(sock_fd, >=, 0);
5694
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5695
const char socket_name[] = "socket_name";
5696
__attribute__((cleanup(cleanup_string)))
5697
char *filename = NULL;
5698
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5700
g_assert_nonnull(filename);
5701
g_assert_cmpint((int)strlen(filename), <,
5702
(int)sizeof(sock_name.sun_path));
5703
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5704
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5705
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5706
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5707
task_context task = {
5708
.func=connect_question_socket,
5709
.question_filename=strdup(question_filename),
5711
.password=(buffer[]){{}},
5712
.filename=strdup(filename),
5713
.cancelled_filenames=&cancelled_filenames,
5714
.mandos_client_exited=(bool[]){false},
5715
.password_is_read=(bool[]){false},
5716
.current_time=¤t_time,
5718
g_assert_nonnull(task.question_filename);
5719
run_task_with_stderr_to_dev_null(task, queue);
5721
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5722
const task_context *const added_task
5723
= find_matching_task(queue, task);
5724
g_assert_nonnull(added_task);
5725
g_assert_true(queue->next_run == 1000000 + current_time);
5727
g_assert_cmpint(unlink(filename), ==, 0);
5728
g_assert_cmpint(rmdir(tempdir), ==, 0);
5732
void test_connect_question_socket_usable(__attribute__((unused))
5733
test_fixture *fixture,
5734
__attribute__((unused))
5735
gconstpointer user_data){
5736
__attribute__((cleanup(cleanup_close)))
5737
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5738
g_assert_cmpint(epoll_fd, >=, 0);
5739
__attribute__((cleanup(cleanup_string)))
5740
char *const question_filename = strdup("/nonexistent/question");
5741
g_assert_nonnull(question_filename);
5742
__attribute__((cleanup(string_set_clear)))
5743
string_set cancelled_filenames = {};
5744
buffer password = {};
5745
bool mandos_client_exited = false;
5746
bool password_is_read = false;
5747
const mono_microsecs current_time = 0;
5748
__attribute__((cleanup(cleanup_queue)))
5749
task_queue *queue = create_queue();
5750
g_assert_nonnull(queue);
5751
__attribute__((cleanup(cleanup_string)))
5752
char *tempdir = make_temporary_directory();
5753
g_assert_nonnull(tempdir);
5754
__attribute__((cleanup(cleanup_close)))
5755
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5756
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5757
g_assert_cmpint(sock_fd, >=, 0);
5758
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5759
const char socket_name[] = "socket_name";
5760
__attribute__((cleanup(cleanup_string)))
5761
char *filename = NULL;
5762
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5764
g_assert_nonnull(filename);
5765
g_assert_cmpint((int)strlen(filename), <,
5766
(int)sizeof(sock_name.sun_path));
5767
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5768
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5769
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5770
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5771
task_context task = {
5772
.func=connect_question_socket,
5773
.question_filename=strdup(question_filename),
5775
.password=&password,
5776
.filename=strdup(filename),
5777
.cancelled_filenames=&cancelled_filenames,
5778
.mandos_client_exited=&mandos_client_exited,
5779
.password_is_read=&password_is_read,
5780
.current_time=¤t_time,
5782
g_assert_nonnull(task.question_filename);
5783
task.func(task, queue);
5785
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5786
const task_context *const added_task
5787
= find_matching_task(queue, (task_context){
5788
.func=send_password_to_socket,
5789
.question_filename=question_filename,
5792
.password=&password,
5793
.cancelled_filenames=&cancelled_filenames,
5794
.mandos_client_exited=&mandos_client_exited,
5795
.password_is_read=&password_is_read,
5796
.current_time=¤t_time,
5798
g_assert_nonnull(added_task);
5799
g_assert_cmpint(added_task->fd, >, 0);
5801
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5804
const int fd = added_task->fd;
5805
g_assert_cmpint(fd, >, 0);
5806
g_assert_true(fd_has_cloexec_and_nonblock(fd));
5809
char write_data[PIPE_BUF];
5811
/* Construct test password buffer */
5812
/* Start with + since that is what the real protocol uses */
5813
write_data[0] = '+';
5814
/* Set a special character at string end just to mark the end */
5815
write_data[sizeof(write_data)-2] = 'y';
5816
/* Set NUL at buffer end, as suggested by the protocol */
5817
write_data[sizeof(write_data)-1] = '\0';
5818
/* Fill rest of password with 'x' */
5819
memset(write_data+1, 'x', sizeof(write_data)-3);
5820
g_assert_cmpint((int)send(fd, write_data, sizeof(write_data),
5821
MSG_NOSIGNAL), ==, sizeof(write_data));
5824
/* read from sock_fd */
5825
char read_data[sizeof(write_data)];
5826
g_assert_cmpint((int)read(sock_fd, read_data, sizeof(read_data)),
5827
==, sizeof(read_data));
5829
g_assert_true(memcmp(write_data, read_data, sizeof(write_data))
5832
/* writing to sock_fd should fail */
5833
g_assert_cmpint(send(sock_fd, write_data, sizeof(write_data),
5834
MSG_NOSIGNAL), <, 0);
5836
/* reading from fd should fail */
5837
g_assert_cmpint((int)recv(fd, read_data, sizeof(read_data),
5838
MSG_NOSIGNAL), <, 0);
5840
g_assert_cmpint(unlink(filename), ==, 0);
5841
g_assert_cmpint(rmdir(tempdir), ==, 0);
5845
test_send_password_to_socket_client_not_exited(__attribute__((unused))
5846
test_fixture *fixture,
5847
__attribute__((unused))
5850
__attribute__((cleanup(cleanup_close)))
5851
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5852
g_assert_cmpint(epoll_fd, >=, 0);
5853
__attribute__((cleanup(cleanup_string)))
5854
char *const question_filename = strdup("/nonexistent/question");
5855
g_assert_nonnull(question_filename);
5856
__attribute__((cleanup(cleanup_string)))
5857
char *const filename = strdup("/nonexistent/socket");
5858
g_assert_nonnull(filename);
5859
__attribute__((cleanup(string_set_clear)))
5860
string_set cancelled_filenames = {};
5861
buffer password = {};
5862
bool password_is_read = true;
5863
__attribute__((cleanup(cleanup_queue)))
5864
task_queue *queue = create_queue();
5865
g_assert_nonnull(queue);
5867
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5868
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5870
__attribute__((cleanup(cleanup_close)))
5871
const int read_socket = socketfds[0];
5872
const int write_socket = socketfds[1];
5873
task_context task = {
5874
.func=send_password_to_socket,
5875
.question_filename=strdup(question_filename),
5876
.filename=strdup(filename),
5879
.password=&password,
5880
.cancelled_filenames=&cancelled_filenames,
5881
.mandos_client_exited=(bool[]){false},
5882
.password_is_read=&password_is_read,
5883
.current_time=(mono_microsecs[]){0},
5885
g_assert_nonnull(task.question_filename);
5887
task.func(task, queue);
5889
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5891
const task_context *const added_task
5892
= find_matching_task(queue, task);
5893
g_assert_nonnull(added_task);
5894
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5895
g_assert_true(password_is_read);
5897
g_assert_cmpint(added_task->fd, >, 0);
5898
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5903
test_send_password_to_socket_password_not_read(__attribute__((unused))
5904
test_fixture *fixture,
5905
__attribute__((unused))
5908
__attribute__((cleanup(cleanup_close)))
5909
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5910
g_assert_cmpint(epoll_fd, >=, 0);
5911
__attribute__((cleanup(cleanup_string)))
5912
char *const question_filename = strdup("/nonexistent/question");
5913
g_assert_nonnull(question_filename);
5914
__attribute__((cleanup(cleanup_string)))
5915
char *const filename = strdup("/nonexistent/socket");
5916
__attribute__((cleanup(string_set_clear)))
5917
string_set cancelled_filenames = {};
5918
buffer password = {};
5919
__attribute__((cleanup(cleanup_queue)))
5920
task_queue *queue = create_queue();
5921
g_assert_nonnull(queue);
5923
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5924
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5926
__attribute__((cleanup(cleanup_close)))
5927
const int read_socket = socketfds[0];
5928
const int write_socket = socketfds[1];
5929
task_context task = {
5930
.func=send_password_to_socket,
5931
.question_filename=strdup(question_filename),
5932
.filename=strdup(filename),
5935
.password=&password,
5936
.cancelled_filenames=&cancelled_filenames,
5937
.mandos_client_exited=(bool[]){false},
5938
.password_is_read=(bool[]){false},
5939
.current_time=(mono_microsecs[]){0},
5941
g_assert_nonnull(task.question_filename);
5943
task.func(task, queue);
5945
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5947
const task_context *const added_task = find_matching_task(queue,
5949
g_assert_nonnull(added_task);
5950
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5951
g_assert_true(queue->next_run == 0);
5953
g_assert_cmpint(added_task->fd, >, 0);
5954
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5959
void test_send_password_to_socket_EMSGSIZE(__attribute__((unused))
5960
test_fixture *fixture,
5961
__attribute__((unused))
5962
gconstpointer user_data){
5963
__attribute__((cleanup(cleanup_close)))
5964
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5965
g_assert_cmpint(epoll_fd, >=, 0);
5966
const char question_filename[] = "/nonexistent/question";
5967
char *const filename = strdup("/nonexistent/socket");
5968
__attribute__((cleanup(string_set_clear)))
5969
string_set cancelled_filenames = {};
5972
/* Find a message size which triggers EMSGSIZE */
5973
__attribute__((cleanup(cleanup_string)))
5974
char *message_buffer = NULL;
5975
size_t message_size = PIPE_BUF + 1;
5976
for(ssize_t ssret = 0; ssret >= 0; message_size += 1024){
5977
if(message_size >= 1024*1024*1024){ /* 1 GiB */
5978
g_test_skip("Skipping EMSGSIZE test: Will not try 1GiB");
5981
message_buffer = realloc(message_buffer, message_size);
5982
if(message_buffer == NULL){
5983
g_test_skip("Skipping EMSGSIZE test");
5984
g_test_message("Failed to malloc() %" PRIuMAX " bytes",
5985
(uintmax_t)message_size);
5988
/* Fill buffer with 'x' */
5989
memset(message_buffer, 'x', message_size);
5990
/* Create a new socketpair for each message size to avoid having
5991
to empty the pipe by reading the message to a separate buffer
5993
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5994
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5996
ssret = send(socketfds[1], message_buffer, message_size,
5998
error_t saved_errno = errno;
5999
g_assert_cmpint(close(socketfds[0]), ==, 0);
6000
g_assert_cmpint(close(socketfds[1]), ==, 0);
6003
if(saved_errno != EMSGSIZE) {
6004
g_test_skip("Skipping EMSGSIZE test");
6005
g_test_message("Error on send(): %s", strerror(saved_errno));
6009
} else if(ssret != (ssize_t)message_size){
6010
g_test_skip("Skipping EMSGSIZE test");
6011
g_test_message("Partial send(): %" PRIuMAX " of %" PRIdMAX
6012
" bytes", (uintmax_t)ssret,
6013
(intmax_t)message_size);
6017
g_test_message("EMSGSIZE triggered by %" PRIdMAX " bytes",
6018
(intmax_t)message_size);
6021
.data=message_buffer,
6022
.length=message_size - 2, /* Compensate for added '+' and NUL */
6023
.allocated=message_size,
6025
if(mlock(password.data, password.allocated) != 0){
6026
g_assert_true(errno == EPERM or errno == ENOMEM);
6029
__attribute__((cleanup(cleanup_queue)))
6030
task_queue *queue = create_queue();
6031
g_assert_nonnull(queue);
6032
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6033
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6035
__attribute__((cleanup(cleanup_close)))
6036
const int read_socket = socketfds[0];
6037
__attribute__((cleanup(cleanup_close)))
6038
const int write_socket = socketfds[1];
6039
task_context task = {
6040
.func=send_password_to_socket,
6041
.question_filename=strdup(question_filename),
6045
.password=&password,
6046
.cancelled_filenames=&cancelled_filenames,
6047
.mandos_client_exited=(bool[]){true},
6048
.password_is_read=(bool[]){true},
6049
.current_time=(mono_microsecs[]){0},
6051
g_assert_nonnull(task.question_filename);
6053
run_task_with_stderr_to_dev_null(task, queue);
6055
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6056
g_assert_true(string_set_contains(cancelled_filenames,
6057
question_filename));
6060
static void test_send_password_to_socket_retry(__attribute__((unused))
6061
test_fixture *fixture,
6062
__attribute__((unused))
6065
__attribute__((cleanup(cleanup_close)))
6066
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6067
g_assert_cmpint(epoll_fd, >=, 0);
6068
__attribute__((cleanup(cleanup_string)))
6069
char *const question_filename = strdup("/nonexistent/question");
6070
g_assert_nonnull(question_filename);
6071
__attribute__((cleanup(cleanup_string)))
6072
char *const filename = strdup("/nonexistent/socket");
6073
g_assert_nonnull(filename);
6074
__attribute__((cleanup(string_set_clear)))
6075
string_set cancelled_filenames = {};
6076
__attribute__((cleanup(cleanup_buffer)))
6077
buffer password = {};
6079
__attribute__((cleanup(cleanup_queue)))
6080
task_queue *queue = create_queue();
6081
g_assert_nonnull(queue);
6083
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6084
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6086
__attribute__((cleanup(cleanup_close)))
6087
const int read_socket = socketfds[0];
6088
const int write_socket = socketfds[1];
6089
/* Close the server side socket to force ECONNRESET on client */
6090
g_assert_cmpint(close(read_socket), ==, 0);
6091
task_context task = {
6092
.func=send_password_to_socket,
6093
.question_filename=strdup(question_filename),
6094
.filename=strdup(filename),
6097
.password=&password,
6098
.cancelled_filenames=&cancelled_filenames,
6099
.mandos_client_exited=(bool[]){true},
6100
.password_is_read=(bool[]){true},
6101
.current_time=(mono_microsecs[]){0},
6103
g_assert_nonnull(task.question_filename);
6105
task.func(task, queue);
6107
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6109
const task_context *const added_task = find_matching_task(queue,
6111
g_assert_nonnull(added_task);
6112
g_assert_cmpuint((unsigned int)password.length, ==, 0);
6114
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
6119
void test_send_password_to_socket_bad_epoll(__attribute__((unused))
6120
test_fixture *fixture,
6121
__attribute__((unused))
6122
gconstpointer user_data){
6123
__attribute__((cleanup(cleanup_close)))
6124
const int epoll_fd = open("/dev/null",
6125
O_WRONLY | O_CLOEXEC | O_NOCTTY);
6126
__attribute__((cleanup(cleanup_string)))
6127
char *const question_filename = strdup("/nonexistent/question");
6128
g_assert_nonnull(question_filename);
6129
__attribute__((cleanup(cleanup_string)))
6130
char *const filename = strdup("/nonexistent/socket");
6131
g_assert_nonnull(filename);
6132
__attribute__((cleanup(string_set_clear)))
6133
string_set cancelled_filenames = {};
6134
__attribute__((cleanup(cleanup_buffer)))
6135
buffer password = {};
6137
const mono_microsecs current_time = 11;
6138
__attribute__((cleanup(cleanup_queue)))
6139
task_queue *queue = create_queue();
6140
g_assert_nonnull(queue);
6142
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6143
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6145
__attribute__((cleanup(cleanup_close)))
6146
const int read_socket = socketfds[0];
6147
const int write_socket = socketfds[1];
6148
/* Close the server side socket to force ECONNRESET on client */
6149
g_assert_cmpint(close(read_socket), ==, 0);
6150
task_context task = {
6151
.func=send_password_to_socket,
6152
.question_filename=strdup(question_filename),
6153
.filename=strdup(filename),
6156
.password=&password,
6157
.cancelled_filenames=&cancelled_filenames,
6158
.mandos_client_exited=(bool[]){true},
6159
.password_is_read=(bool[]){true},
6160
.current_time=¤t_time,
6162
g_assert_nonnull(task.question_filename);
6164
run_task_with_stderr_to_dev_null(task, queue);
6166
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6168
const task_context *const added_task = find_matching_task(queue,
6170
g_assert_nonnull(added_task);
6171
g_assert_true(queue->next_run == current_time + 1000000);
6172
g_assert_cmpuint((unsigned int)password.length, ==, 0);
6175
static void assert_send_password_to_socket_password(buffer password){
6176
__attribute__((cleanup(cleanup_close)))
6177
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6178
g_assert_cmpint(epoll_fd, >=, 0);
6179
char *const question_filename = strdup("/nonexistent/question");
6180
g_assert_nonnull(question_filename);
6181
char *const filename = strdup("/nonexistent/socket");
6182
g_assert_nonnull(filename);
6183
__attribute__((cleanup(string_set_clear)))
6184
string_set cancelled_filenames = {};
6186
__attribute__((cleanup(cleanup_queue)))
6187
task_queue *queue = create_queue();
6188
g_assert_nonnull(queue);
6190
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6191
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6193
__attribute__((cleanup(cleanup_close)))
6194
const int read_socket = socketfds[0];
6195
const int write_socket = socketfds[1];
6196
task_context task = {
6197
.func=send_password_to_socket,
6198
.question_filename=question_filename,
6202
.password=&password,
6203
.cancelled_filenames=&cancelled_filenames,
6204
.mandos_client_exited=(bool[]){true},
6205
.password_is_read=(bool[]){true},
6206
.current_time=(mono_microsecs[]){0},
6209
char *expected_written_data = malloc(password.length + 2);
6210
g_assert_nonnull(expected_written_data);
6211
expected_written_data[0] = '+';
6212
expected_written_data[password.length + 1] = '\0';
6213
if(password.length > 0){
6214
g_assert_nonnull(password.data);
6215
memcpy(expected_written_data + 1, password.data, password.length);
6218
task.func(task, queue);
6221
g_assert_cmpint((int)read(read_socket, buf, PIPE_BUF), ==,
6222
(int)(password.length + 2));
6223
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6225
g_assert_true(memcmp(expected_written_data, buf,
6226
password.length + 2) == 0);
6228
g_assert_true(epoll_set_does_not_contain(epoll_fd, write_socket));
6230
free(expected_written_data);
6234
test_send_password_to_socket_null_password(__attribute__((unused))
6235
test_fixture *fixture,
6236
__attribute__((unused))
6237
gconstpointer user_data){
6238
__attribute__((cleanup(cleanup_buffer)))
6239
buffer password = {};
6240
assert_send_password_to_socket_password(password);
6244
test_send_password_to_socket_empty_password(__attribute__((unused))
6245
test_fixture *fixture,
6246
__attribute__((unused))
6247
gconstpointer user_data){
6248
__attribute__((cleanup(cleanup_buffer)))
6250
.data=malloc(1), /* because malloc(0) may return NULL */
6252
.allocated=0, /* deliberate lie */
6254
g_assert_nonnull(password.data);
6255
assert_send_password_to_socket_password(password);
6259
test_send_password_to_socket_empty_str_pass(__attribute__((unused))
6260
test_fixture *fixture,
6261
__attribute__((unused))
6262
gconstpointer user_data){
6263
__attribute__((cleanup(cleanup_buffer)))
6269
if(mlock(password.data, password.allocated) != 0){
6270
g_assert_true(errno == EPERM or errno == ENOMEM);
6272
assert_send_password_to_socket_password(password);
6276
test_send_password_to_socket_text_password(__attribute__((unused))
6277
test_fixture *fixture,
6278
__attribute__((unused))
6279
gconstpointer user_data){
6280
const char dummy_test_password[] = "dummy test password";
6281
__attribute__((cleanup(cleanup_buffer)))
6283
.data = strdup(dummy_test_password),
6284
.length = strlen(dummy_test_password),
6285
.allocated = sizeof(dummy_test_password),
6287
if(mlock(password.data, password.allocated) != 0){
6288
g_assert_true(errno == EPERM or errno == ENOMEM);
6290
assert_send_password_to_socket_password(password);
6294
test_send_password_to_socket_binary_password(__attribute__((unused))
6295
test_fixture *fixture,
6296
__attribute__((unused))
6297
gconstpointer user_data){
6298
__attribute__((cleanup(cleanup_buffer)))
6304
g_assert_nonnull(password.data);
6305
if(mlock(password.data, password.allocated) != 0){
6306
g_assert_true(errno == EPERM or errno == ENOMEM);
6308
char c = 1; /* Start at 1, avoiding NUL */
6309
for(int i=0; i < 255; i++){
6310
password.data[i] = c++;
6312
assert_send_password_to_socket_password(password);
6316
test_send_password_to_socket_nuls_in_password(__attribute__((unused))
6317
test_fixture *fixture,
6318
__attribute__((unused))
6321
char test_password[] = {'\0', 'a', '\0', 'b', '\0', 'c', '\0'};
6322
__attribute__((cleanup(cleanup_buffer)))
6324
.data=malloc(sizeof(test_password)),
6325
.length=sizeof(test_password),
6326
.allocated=sizeof(test_password),
6328
g_assert_nonnull(password.data);
6329
if(mlock(password.data, password.allocated) !=0){
6330
g_assert_true(errno == EPERM or errno == ENOMEM);
6332
memcpy(password.data, test_password, password.allocated);
6333
assert_send_password_to_socket_password(password);
6336
static bool assert_add_existing_questions_to_devnull(task_queue
6349
static void test_add_existing_questions_ENOENT(__attribute__((unused))
6350
test_fixture *fixture,
6351
__attribute__((unused))
6354
__attribute__((cleanup(cleanup_queue)))
6355
task_queue *queue = create_queue();
6356
g_assert_nonnull(queue);
6357
__attribute__((cleanup(cleanup_close)))
6358
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6359
g_assert_cmpint(epoll_fd, >=, 0);
6360
__attribute__((cleanup(string_set_clear)))
6361
string_set cancelled_filenames = {};
6363
g_assert_false(assert_add_existing_questions_to_devnull
6366
(buffer[]){{}}, /* password */
6367
&cancelled_filenames,
6368
(mono_microsecs[]){0}, /* current_time */
6369
(bool[]){false}, /* mandos_client_exited */
6370
(bool[]){false}, /* password_is_read */
6371
"/nonexistent")); /* dirname */
6373
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6377
bool assert_add_existing_questions_to_devnull(task_queue
6384
*cancelled_filenames,
6385
const mono_microsecs
6386
*const current_time,
6388
mandos_client_exited,
6393
__attribute__((cleanup(cleanup_close)))
6394
const int devnull_fd = open("/dev/null",
6395
O_WRONLY | O_CLOEXEC | O_NOCTTY);
6396
g_assert_cmpint(devnull_fd, >=, 0);
6397
__attribute__((cleanup(cleanup_close)))
6398
const int real_stderr_fd = dup(STDERR_FILENO);
6399
g_assert_cmpint(real_stderr_fd, >=, 0);
6400
dup2(devnull_fd, STDERR_FILENO);
6401
const bool ret = add_existing_questions(queue, epoll_fd, password,
6402
cancelled_filenames,
6404
mandos_client_exited,
6405
password_is_read, dirname);
6406
dup2(real_stderr_fd, STDERR_FILENO);
6411
void test_add_existing_questions_no_questions(__attribute__((unused))
6412
test_fixture *fixture,
6413
__attribute__((unused))
6416
__attribute__((cleanup(cleanup_queue)))
6417
task_queue *queue = create_queue();
6418
g_assert_nonnull(queue);
6419
__attribute__((cleanup(cleanup_close)))
6420
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6421
g_assert_cmpint(epoll_fd, >=, 0);
6422
__attribute__((cleanup(string_set_clear)))
6423
string_set cancelled_filenames = {};
6424
__attribute__((cleanup(cleanup_string)))
6425
char *tempdir = make_temporary_directory();
6426
g_assert_nonnull(tempdir);
6428
g_assert_false(assert_add_existing_questions_to_devnull
6431
(buffer[]){{}}, /* password */
6432
&cancelled_filenames,
6433
(mono_microsecs[]){0}, /* current_time */
6434
(bool[]){false}, /* mandos_client_exited */
6435
(bool[]){false}, /* password_is_read */
6438
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6440
g_assert_cmpint(rmdir(tempdir), ==, 0);
6443
static char *make_question_file_in_directory(const char *const);
6446
void test_add_existing_questions_one_question(__attribute__((unused))
6447
test_fixture *fixture,
6448
__attribute__((unused))
6451
__attribute__((cleanup(cleanup_queue)))
6452
task_queue *queue = create_queue();
6453
g_assert_nonnull(queue);
6454
__attribute__((cleanup(cleanup_close)))
6455
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6456
g_assert_cmpint(epoll_fd, >=, 0);
6457
__attribute__((cleanup(cleanup_buffer)))
6458
buffer password = {};
6459
__attribute__((cleanup(string_set_clear)))
6460
string_set cancelled_filenames = {};
6461
const mono_microsecs current_time = 0;
6462
bool mandos_client_exited = false;
6463
bool password_is_read = false;
6464
__attribute__((cleanup(cleanup_string)))
6465
char *tempdir = make_temporary_directory();
6466
g_assert_nonnull(tempdir);
6467
__attribute__((cleanup(cleanup_string)))
6468
char *question_filename
6469
= make_question_file_in_directory(tempdir);
6470
g_assert_nonnull(question_filename);
6472
g_assert_true(assert_add_existing_questions_to_devnull
6476
&cancelled_filenames,
6478
&mandos_client_exited,
6482
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6484
g_assert_nonnull(find_matching_task(queue, (task_context){
6485
.func=open_and_parse_question,
6487
.filename=question_filename,
6488
.question_filename=question_filename,
6489
.password=&password,
6490
.cancelled_filenames=&cancelled_filenames,
6491
.current_time=¤t_time,
6492
.mandos_client_exited=&mandos_client_exited,
6493
.password_is_read=&password_is_read,
6496
g_assert_true(queue->next_run == 1);
6498
g_assert_cmpint(unlink(question_filename), ==, 0);
6499
g_assert_cmpint(rmdir(tempdir), ==, 0);
6502
static char *make_question_file_in_directory(const char
6504
return make_temporary_prefixed_file_in_directory("ask.", dir);
6508
void test_add_existing_questions_two_questions(__attribute__((unused))
6509
test_fixture *fixture,
6510
__attribute__((unused))
6513
__attribute__((cleanup(cleanup_queue)))
6514
task_queue *queue = create_queue();
6515
g_assert_nonnull(queue);
6516
__attribute__((cleanup(cleanup_close)))
6517
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6518
g_assert_cmpint(epoll_fd, >=, 0);
6519
__attribute__((cleanup(cleanup_buffer)))
6520
buffer password = {};
6521
__attribute__((cleanup(string_set_clear)))
6522
string_set cancelled_filenames = {};
6523
const mono_microsecs current_time = 0;
6524
bool mandos_client_exited = false;
6525
bool password_is_read = false;
6526
__attribute__((cleanup(cleanup_string)))
6527
char *tempdir = make_temporary_directory();
6528
g_assert_nonnull(tempdir);
6529
__attribute__((cleanup(cleanup_string)))
6530
char *question_filename1
6531
= make_question_file_in_directory(tempdir);
6532
g_assert_nonnull(question_filename1);
6533
__attribute__((cleanup(cleanup_string)))
6534
char *question_filename2
6535
= make_question_file_in_directory(tempdir);
6536
g_assert_nonnull(question_filename2);
6538
g_assert_true(assert_add_existing_questions_to_devnull
6542
&cancelled_filenames,
6544
&mandos_client_exited,
6548
g_assert_cmpuint((unsigned int)queue->length, ==, 2);
6550
g_assert_true(queue->next_run == 1);
6552
__attribute__((cleanup(string_set_clear)))
6553
string_set seen_questions = {};
6555
bool queue_contains_question_opener(char *const question_filename){
6556
return(find_matching_task(queue, (task_context){
6557
.func=open_and_parse_question,
6559
.question_filename=question_filename,
6560
.password=&password,
6561
.cancelled_filenames=&cancelled_filenames,
6562
.current_time=¤t_time,
6563
.mandos_client_exited=&mandos_client_exited,
6564
.password_is_read=&password_is_read,
6568
g_assert_true(queue_contains_question_opener(question_filename1));
6569
g_assert_true(queue_contains_question_opener(question_filename2));
6571
g_assert_true(queue->next_run == 1);
6573
g_assert_cmpint(unlink(question_filename1), ==, 0);
6574
g_assert_cmpint(unlink(question_filename2), ==, 0);
6575
g_assert_cmpint(rmdir(tempdir), ==, 0);
6579
test_add_existing_questions_non_questions(__attribute__((unused))
6580
test_fixture *fixture,
6581
__attribute__((unused))
6582
gconstpointer user_data){
6583
__attribute__((cleanup(cleanup_queue)))
6584
task_queue *queue = create_queue();
6585
g_assert_nonnull(queue);
6586
__attribute__((cleanup(cleanup_close)))
6587
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6588
g_assert_cmpint(epoll_fd, >=, 0);
6589
__attribute__((cleanup(string_set_clear)))
6590
string_set cancelled_filenames = {};
6591
__attribute__((cleanup(cleanup_string)))
6592
char *tempdir = make_temporary_directory();
6593
g_assert_nonnull(tempdir);
6594
__attribute__((cleanup(cleanup_string)))
6595
char *question_filename1
6596
= make_temporary_file_in_directory(tempdir);
6597
g_assert_nonnull(question_filename1);
6598
__attribute__((cleanup(cleanup_string)))
6599
char *question_filename2
6600
= make_temporary_file_in_directory(tempdir);
6601
g_assert_nonnull(question_filename2);
6603
g_assert_false(assert_add_existing_questions_to_devnull
6606
(buffer[]){{}}, /* password */
6607
&cancelled_filenames,
6608
(mono_microsecs[]){0}, /* current_time */
6609
(bool[]){false}, /* mandos_client_exited */
6610
(bool[]){false}, /* password_is_read */
6613
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6615
g_assert_cmpint(unlink(question_filename1), ==, 0);
6616
g_assert_cmpint(unlink(question_filename2), ==, 0);
6617
g_assert_cmpint(rmdir(tempdir), ==, 0);
6621
test_add_existing_questions_both_types(__attribute__((unused))
6622
test_fixture *fixture,
6623
__attribute__((unused))
6624
gconstpointer user_data){
6625
__attribute__((cleanup(cleanup_queue)))
6626
task_queue *queue = create_queue();
6627
g_assert_nonnull(queue);
6628
__attribute__((cleanup(cleanup_close)))
6629
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6630
g_assert_cmpint(epoll_fd, >=, 0);
6631
__attribute__((cleanup(cleanup_buffer)))
6632
buffer password = {};
6633
__attribute__((cleanup(string_set_clear)))
6634
string_set cancelled_filenames = {};
6635
const mono_microsecs current_time = 0;
6636
bool mandos_client_exited = false;
6637
bool password_is_read = false;
6638
__attribute__((cleanup(cleanup_string)))
6639
char *tempdir = make_temporary_directory();
6640
g_assert_nonnull(tempdir);
6641
__attribute__((cleanup(cleanup_string)))
6642
char *tempfilename1 = make_temporary_file_in_directory(tempdir);
6643
g_assert_nonnull(tempfilename1);
6644
__attribute__((cleanup(cleanup_string)))
6645
char *tempfilename2 = make_temporary_file_in_directory(tempdir);
6646
g_assert_nonnull(tempfilename2);
6647
__attribute__((cleanup(cleanup_string)))
6648
char *question_filename
6649
= make_question_file_in_directory(tempdir);
6650
g_assert_nonnull(question_filename);
6652
g_assert_true(assert_add_existing_questions_to_devnull
6656
&cancelled_filenames,
6658
&mandos_client_exited,
6662
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6664
g_assert_nonnull(find_matching_task(queue, (task_context){
6665
.func=open_and_parse_question,
6667
.filename=question_filename,
6668
.question_filename=question_filename,
6669
.password=&password,
6670
.cancelled_filenames=&cancelled_filenames,
6671
.current_time=¤t_time,
6672
.mandos_client_exited=&mandos_client_exited,
6673
.password_is_read=&password_is_read,
6676
g_assert_true(queue->next_run == 1);
6678
g_assert_cmpint(unlink(tempfilename1), ==, 0);
6679
g_assert_cmpint(unlink(tempfilename2), ==, 0);
6680
g_assert_cmpint(unlink(question_filename), ==, 0);
6681
g_assert_cmpint(rmdir(tempdir), ==, 0);
6684
static void test_wait_for_event_timeout(__attribute__((unused))
6685
test_fixture *fixture,
6686
__attribute__((unused))
6687
gconstpointer user_data){
6688
__attribute__((cleanup(cleanup_close)))
6689
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6690
g_assert_cmpint(epoll_fd, >=, 0);
6692
g_assert_true(wait_for_event(epoll_fd, 1, 0));
6695
static void test_wait_for_event_event(__attribute__((unused))
6696
test_fixture *fixture,
6697
__attribute__((unused))
6698
gconstpointer user_data){
6699
__attribute__((cleanup(cleanup_close)))
6700
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6701
g_assert_cmpint(epoll_fd, >=, 0);
6703
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6704
__attribute__((cleanup(cleanup_close)))
6705
const int read_pipe = pipefds[0];
6706
__attribute__((cleanup(cleanup_close)))
6707
const int write_pipe = pipefds[1];
6708
g_assert_cmpint(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, read_pipe,
6709
&(struct epoll_event)
6710
{ .events=EPOLLIN | EPOLLRDHUP }), ==, 0);
6711
g_assert_cmpint((int)write(write_pipe, "x", 1), ==, 1);
6713
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6716
static void test_wait_for_event_sigchld(test_fixture *fixture,
6717
__attribute__((unused))
6718
gconstpointer user_data){
6719
const pid_t pid = fork();
6720
if(pid == 0){ /* Child */
6721
if(not restore_signal_handler(&fixture->orig_sigaction)){
6722
_exit(EXIT_FAILURE);
6724
if(not restore_sigmask(&fixture->orig_sigmask)){
6725
_exit(EXIT_FAILURE);
6729
g_assert_true(pid != -1);
6730
__attribute__((cleanup(cleanup_close)))
6731
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6732
g_assert_cmpint(epoll_fd, >=, 0);
6734
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6737
g_assert_true(waitpid(pid, &status, 0) == pid);
6738
g_assert_true(WIFEXITED(status));
6739
g_assert_cmpint(WEXITSTATUS(status), ==, EXIT_SUCCESS);
6742
static void test_run_queue_zeroes_next_run(__attribute__((unused))
6743
test_fixture *fixture,
6744
__attribute__((unused))
6745
gconstpointer user_data){
6746
__attribute__((cleanup(cleanup_queue)))
6747
task_queue *queue = create_queue();
6748
g_assert_nonnull(queue);
6749
queue->next_run = 1;
6750
__attribute__((cleanup(cleanup_close)))
6751
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6752
__attribute__((cleanup(string_set_clear)))
6753
string_set cancelled_filenames = {};
6754
bool quit_now = false;
6756
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6757
g_assert_false(quit_now);
6758
g_assert_cmpuint((unsigned int)queue->next_run, ==, 0);
6762
void test_run_queue_clears_cancelled_filenames(__attribute__((unused))
6763
test_fixture *fixture,
6764
__attribute__((unused))
6767
__attribute__((cleanup(cleanup_queue)))
6768
task_queue *queue = create_queue();
6769
g_assert_nonnull(queue);
6770
__attribute__((cleanup(string_set_clear)))
6771
string_set cancelled_filenames = {};
6772
bool quit_now = false;
6773
const char question_filename[] = "/nonexistent/question_filename";
6774
g_assert_true(string_set_add(&cancelled_filenames,
6775
question_filename));
6777
g_assert_true(add_to_queue(queue,
6778
(task_context){ .func=dummy_func }));
6780
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6781
g_assert_false(quit_now);
6782
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6783
g_assert_false(string_set_contains(cancelled_filenames,
6784
question_filename));
6788
void test_run_queue_skips_cancelled_filenames(__attribute__((unused))
6789
test_fixture *fixture,
6790
__attribute__((unused))
6793
__attribute__((cleanup(cleanup_queue)))
6794
task_queue *queue = create_queue();
6795
g_assert_nonnull(queue);
6796
__attribute__((cleanup(string_set_clear)))
6797
string_set cancelled_filenames = {};
6798
bool quit_now = false;
6800
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6801
__attribute__((cleanup(cleanup_close)))
6802
const int read_pipe = pipefds[0];
6803
g_assert_cmpint(close(pipefds[1]), ==, 0);
6804
const char question_filename[] = "/nonexistent/question_filename";
6805
g_assert_true(string_set_add(&cancelled_filenames,
6806
question_filename));
6807
__attribute__((nonnull))
6808
void quit_func(const task_context task,
6809
__attribute__((unused)) task_queue *const q){
6810
g_assert_nonnull(task.quit_now);
6811
*task.quit_now = true;
6813
task_context task = {
6815
.question_filename=strdup(question_filename),
6816
.quit_now=&quit_now,
6819
g_assert_nonnull(task.question_filename);
6821
g_assert_true(add_to_queue(queue, task));
6823
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6824
g_assert_false(quit_now);
6826
/* read_pipe should be closed already */
6828
bool read_pipe_closed = (close(read_pipe) == -1);
6829
read_pipe_closed &= (errno == EBADF);
6830
g_assert_true(read_pipe_closed);
6833
static void test_run_queue_one_task(__attribute__((unused))
6834
test_fixture *fixture,
6835
__attribute__((unused))
6836
gconstpointer user_data){
6837
__attribute__((cleanup(cleanup_queue)))
6838
task_queue *queue = create_queue();
6839
g_assert_nonnull(queue);
6840
__attribute__((cleanup(string_set_clear)))
6841
string_set cancelled_filenames = {};
6842
bool quit_now = false;
6844
__attribute__((nonnull))
6845
void next_run_func(__attribute__((unused))
6846
const task_context task,
6847
task_queue *const q){
6851
task_context task = {
6852
.func=next_run_func,
6854
g_assert_true(add_to_queue(queue, task));
6856
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6857
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6858
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6861
static void test_run_queue_two_tasks(__attribute__((unused))
6862
test_fixture *fixture,
6863
__attribute__((unused))
6864
gconstpointer user_data){
6865
__attribute__((cleanup(cleanup_queue)))
6866
task_queue *queue = create_queue();
6867
g_assert_nonnull(queue);
6868
queue->next_run = 1;
6869
__attribute__((cleanup(string_set_clear)))
6870
string_set cancelled_filenames = {};
6871
bool quit_now = false;
6872
bool mandos_client_exited = false;
6874
__attribute__((nonnull))
6875
void next_run_func(__attribute__((unused))
6876
const task_context task,
6877
task_queue *const q){
6881
__attribute__((nonnull))
6882
void exited_func(const task_context task,
6883
__attribute__((unused)) task_queue *const q){
6884
*task.mandos_client_exited = true;
6887
task_context task1 = {
6888
.func=next_run_func,
6890
g_assert_true(add_to_queue(queue, task1));
6892
task_context task2 = {
6894
.mandos_client_exited=&mandos_client_exited,
6896
g_assert_true(add_to_queue(queue, task2));
6898
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6899
g_assert_false(quit_now);
6900
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6901
g_assert_true(mandos_client_exited);
6902
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6905
static void test_run_queue_two_tasks_quit(__attribute__((unused))
6906
test_fixture *fixture,
6907
__attribute__((unused))
6908
gconstpointer user_data){
6909
__attribute__((cleanup(cleanup_queue)))
6910
task_queue *queue = create_queue();
6911
g_assert_nonnull(queue);
6912
__attribute__((cleanup(string_set_clear)))
6913
string_set cancelled_filenames = {};
6914
bool quit_now = false;
6915
bool mandos_client_exited = false;
6916
bool password_is_read = false;
6918
__attribute__((nonnull))
6919
void set_exited_func(const task_context task,
6920
__attribute__((unused)) task_queue *const q){
6921
*task.mandos_client_exited = true;
6922
*task.quit_now = true;
6924
task_context task1 = {
6925
.func=set_exited_func,
6926
.quit_now=&quit_now,
6927
.mandos_client_exited=&mandos_client_exited,
6929
g_assert_true(add_to_queue(queue, task1));
6931
__attribute__((nonnull))
6932
void set_read_func(const task_context task,
6933
__attribute__((unused)) task_queue *const q){
6934
*task.quit_now = true;
6935
*task.password_is_read = true;
6937
task_context task2 = {
6938
.func=set_read_func,
6939
.quit_now=&quit_now,
6940
.password_is_read=&password_is_read,
6942
g_assert_true(add_to_queue(queue, task2));
6944
g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6945
g_assert_true(quit_now);
6946
g_assert_true(mandos_client_exited xor password_is_read);
6947
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6950
static void test_run_queue_two_tasks_cleanup(__attribute__((unused))
6951
test_fixture *fixture,
6952
__attribute__((unused))
6953
gconstpointer user_data){
6954
__attribute__((cleanup(cleanup_queue)))
6955
task_queue *queue = create_queue();
6956
g_assert_nonnull(queue);
6957
__attribute__((cleanup(string_set_clear)))
6958
string_set cancelled_filenames = {};
6960
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6961
__attribute__((cleanup(cleanup_close)))
6962
const int read_pipe = pipefds[0];
6963
__attribute__((cleanup(cleanup_close)))
6964
const int write_pipe = pipefds[1];
6965
bool quit_now = false;
6967
__attribute__((nonnull))
6968
void read_func(const task_context task,
6969
__attribute__((unused)) task_queue *const q){
6970
*task.quit_now = true;
6972
task_context task1 = {
6974
.quit_now=&quit_now,
6977
g_assert_true(add_to_queue(queue, task1));
6979
__attribute__((nonnull))
6980
void write_func(const task_context task,
6981
__attribute__((unused)) task_queue *const q){
6982
*task.quit_now = true;
6984
task_context task2 = {
6986
.quit_now=&quit_now,
6989
g_assert_true(add_to_queue(queue, task2));
6991
g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6992
g_assert_true(quit_now);
6994
/* Either read_pipe or write_pipe should be closed already */
6996
bool close_read_pipe = (close(read_pipe) == -1);
6997
close_read_pipe &= (errno == EBADF);
6999
bool close_write_pipe = (close(write_pipe) == -1);
7000
close_write_pipe &= (errno == EBADF);
7001
g_assert_true(close_read_pipe xor close_write_pipe);
7002
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
7005
static void test_setup_signal_handler(__attribute__((unused))
7006
test_fixture *fixture,
7007
__attribute__((unused))
7008
gconstpointer user_data){
7009
/* Save current SIGCHLD action, whatever it is */
7010
struct sigaction expected_sigchld_action;
7011
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
7014
/* Act; i.e. run the setup_signal_handler() function */
7015
struct sigaction actual_old_sigchld_action;
7016
g_assert_true(setup_signal_handler(&actual_old_sigchld_action));
7018
/* Check that the function correctly set "actual_old_sigchld_action"
7019
to the same values as the previously saved
7020
"expected_sigchld_action" */
7021
/* Check member sa_handler */
7022
g_assert_true(actual_old_sigchld_action.sa_handler
7023
== expected_sigchld_action.sa_handler);
7024
/* Check member sa_mask */
7025
for(int signum = 1; signum < NSIG; signum++){
7026
const int expected_old_block_state
7027
= sigismember(&expected_sigchld_action.sa_mask, signum);
7028
g_assert_cmpint(expected_old_block_state, >=, 0);
7029
const int actual_old_block_state
7030
= sigismember(&actual_old_sigchld_action.sa_mask, signum);
7031
g_assert_cmpint(actual_old_block_state, >=, 0);
7032
g_assert_cmpint(actual_old_block_state,
7033
==, expected_old_block_state);
7035
/* Check member sa_flags */
7036
g_assert_true((actual_old_sigchld_action.sa_flags
7037
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
7038
== (expected_sigchld_action.sa_flags
7039
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
7041
/* Retrieve the current signal handler for SIGCHLD as set by
7042
setup_signal_handler() */
7043
struct sigaction actual_new_sigchld_action;
7044
g_assert_cmpint(sigaction(SIGCHLD, NULL,
7045
&actual_new_sigchld_action), ==, 0);
7046
/* Check that the signal handler (member sa_handler) is correctly
7047
set to the "handle_sigchld" function */
7048
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_DFL);
7049
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_IGN);
7050
g_assert_true(actual_new_sigchld_action.sa_handler
7052
/* Check (in member sa_mask) that at least a handful of signals are
7053
actually blocked during the signal handler */
7054
for(int signum = 1; signum < NSIG; signum++){
7055
int actual_new_block_state;
7061
actual_new_block_state
7062
= sigismember(&actual_new_sigchld_action.sa_mask, signum);
7063
g_assert_cmpint(actual_new_block_state, ==, 1);
7065
case SIGKILL: /* non-blockable */
7066
case SIGSTOP: /* non-blockable */
7067
case SIGCHLD: /* always blocked */
7072
/* Check member sa_flags */
7073
g_assert_true((actual_new_sigchld_action.sa_flags
7074
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
7075
== (SA_NOCLDSTOP | SA_RESTART));
7077
/* Restore signal handler */
7078
g_assert_cmpint(sigaction(SIGCHLD, &expected_sigchld_action, NULL),
7082
static void test_restore_signal_handler(__attribute__((unused))
7083
test_fixture *fixture,
7084
__attribute__((unused))
7085
gconstpointer user_data){
7086
/* Save current SIGCHLD action, whatever it is */
7087
struct sigaction expected_sigchld_action;
7088
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
7090
/* Since we haven't established a signal handler yet, there should
7091
not be one established. But another test may have relied on
7092
restore_signal_handler() to restore the signal handler, and if
7093
restore_signal_handler() is buggy (which we should be prepared
7094
for in this test) the signal handler may not have been restored
7095
properly; check for this: */
7096
g_assert_true(expected_sigchld_action.sa_handler != handle_sigchld);
7098
/* Establish a signal handler */
7099
struct sigaction sigchld_action = {
7100
.sa_handler=handle_sigchld,
7101
.sa_flags=SA_RESTART | SA_NOCLDSTOP,
7103
g_assert_cmpint(sigfillset(&sigchld_action.sa_mask), ==, 0);
7104
g_assert_cmpint(sigaction(SIGCHLD, &sigchld_action, NULL), ==, 0);
7106
/* Act; i.e. run the restore_signal_handler() function */
7107
g_assert_true(restore_signal_handler(&expected_sigchld_action));
7109
/* Retrieve the restored signal handler data */
7110
struct sigaction actual_restored_sigchld_action;
7111
g_assert_cmpint(sigaction(SIGCHLD, NULL,
7112
&actual_restored_sigchld_action), ==, 0);
7114
/* Check that the function correctly restored the signal action, as
7115
saved in "actual_restored_sigchld_action", to the same values as
7116
the previously saved "expected_sigchld_action" */
7117
/* Check member sa_handler */
7118
g_assert_true(actual_restored_sigchld_action.sa_handler
7119
== expected_sigchld_action.sa_handler);
7120
/* Check member sa_mask */
7121
for(int signum = 1; signum < NSIG; signum++){
7122
const int expected_old_block_state
7123
= sigismember(&expected_sigchld_action.sa_mask, signum);
7124
g_assert_cmpint(expected_old_block_state, >=, 0);
7125
const int actual_restored_block_state
7126
= sigismember(&actual_restored_sigchld_action.sa_mask, signum);
7127
g_assert_cmpint(actual_restored_block_state, >=, 0);
7128
g_assert_cmpint(actual_restored_block_state,
7129
==, expected_old_block_state);
7131
/* Check member sa_flags */
7132
g_assert_true((actual_restored_sigchld_action.sa_flags
7133
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
7134
== (expected_sigchld_action.sa_flags
7135
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
7138
static void test_block_sigchld(__attribute__((unused))
7139
test_fixture *fixture,
7140
__attribute__((unused))
7141
gconstpointer user_data){
7142
/* Save original signal mask */
7143
sigset_t expected_sigmask;
7144
g_assert_cmpint(pthread_sigmask(-1, NULL, &expected_sigmask),
7147
/* Make sure SIGCHLD is unblocked for this test */
7148
sigset_t sigchld_sigmask;
7149
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
7150
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
7151
g_assert_cmpint(pthread_sigmask(SIG_UNBLOCK, &sigchld_sigmask,
7154
/* Act; i.e. run the block_sigchld() function */
7155
sigset_t actual_old_sigmask;
7156
g_assert_true(block_sigchld(&actual_old_sigmask));
7158
/* Check the actual_old_sigmask; it should be the same as the
7159
previously saved signal mask "expected_sigmask". */
7160
for(int signum = 1; signum < NSIG; signum++){
7161
const int expected_old_block_state
7162
= sigismember(&expected_sigmask, signum);
7163
g_assert_cmpint(expected_old_block_state, >=, 0);
7164
const int actual_old_block_state
7165
= sigismember(&actual_old_sigmask, signum);
7166
g_assert_cmpint(actual_old_block_state, >=, 0);
7167
g_assert_cmpint(actual_old_block_state,
7168
==, expected_old_block_state);
7171
/* Retrieve the newly set signal mask */
7172
sigset_t actual_sigmask;
7173
g_assert_cmpint(pthread_sigmask(-1, NULL, &actual_sigmask), ==, 0);
7175
/* SIGCHLD should be blocked */
7176
g_assert_cmpint(sigismember(&actual_sigmask, SIGCHLD), ==, 1);
7178
/* Restore signal mask */
7179
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &expected_sigmask,
7183
static void test_restore_sigmask(__attribute__((unused))
7184
test_fixture *fixture,
7185
__attribute__((unused))
7186
gconstpointer user_data){
7187
/* Save original signal mask */
7188
sigset_t orig_sigmask;
7189
g_assert_cmpint(pthread_sigmask(-1, NULL, &orig_sigmask), ==, 0);
7191
/* Make sure SIGCHLD is blocked for this test */
7192
sigset_t sigchld_sigmask;
7193
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
7194
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
7195
g_assert_cmpint(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask,
7198
/* Act; i.e. run the restore_sigmask() function */
7199
g_assert_true(restore_sigmask(&orig_sigmask));
7201
/* Retrieve the newly restored signal mask */
7202
sigset_t restored_sigmask;
7203
g_assert_cmpint(pthread_sigmask(-1, NULL, &restored_sigmask),
7206
/* Check the restored_sigmask; it should be the same as the
7207
previously saved signal mask "orig_sigmask". */
7208
for(int signum = 1; signum < NSIG; signum++){
7209
const int orig_block_state = sigismember(&orig_sigmask, signum);
7210
g_assert_cmpint(orig_block_state, >=, 0);
7211
const int restored_block_state = sigismember(&restored_sigmask,
7213
g_assert_cmpint(restored_block_state, >=, 0);
7214
g_assert_cmpint(restored_block_state, ==, orig_block_state);
7217
/* Restore signal mask */
7218
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &orig_sigmask,
7222
static void test_parse_arguments_noargs(__attribute__((unused))
7223
test_fixture *fixture,
7224
__attribute__((unused))
7225
gconstpointer user_data){
7229
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7231
char *agent_directory = NULL;
7232
char *helper_directory = NULL;
7235
char *mandos_argz = NULL;
7236
size_t mandos_argz_length = 0;
7238
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7239
&helper_directory, &user, &group,
7240
&mandos_argz, &mandos_argz_length));
7241
g_assert_null(agent_directory);
7242
g_assert_null(helper_directory);
7243
g_assert_true(user == 0);
7244
g_assert_true(group == 0);
7245
g_assert_null(mandos_argz);
7246
g_assert_true(mandos_argz_length == 0);
7248
for(char **arg = argv; *arg != NULL; arg++){
7253
__attribute__((nonnull))
7254
static bool parse_arguments_devnull(int argc, char *argv[],
7255
const bool exit_failure,
7256
char **agent_directory,
7257
char **helper_directory,
7261
size_t *mandos_argz_length){
7263
FILE *real_stderr = stderr;
7264
FILE *devnull = fopen("/dev/null", "we");
7265
g_assert_nonnull(devnull);
7268
const bool ret = parse_arguments(argc, argv, exit_failure,
7270
helper_directory, user, group,
7271
mandos_argz, mandos_argz_length);
7272
const error_t saved_errno = errno;
7274
stderr = real_stderr;
7275
g_assert_cmpint(fclose(devnull), ==, 0);
7277
errno = saved_errno;
7282
static void test_parse_arguments_invalid(__attribute__((unused))
7283
test_fixture *fixture,
7284
__attribute__((unused))
7285
gconstpointer user_data){
7288
strdup("--invalid"),
7290
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7292
char *agent_directory = NULL;
7293
char *helper_directory = NULL;
7296
char *mandos_argz = NULL;
7297
size_t mandos_argz_length = 0;
7299
g_assert_false(parse_arguments_devnull(argc, argv, false,
7301
&helper_directory, &user,
7302
&group, &mandos_argz,
7303
&mandos_argz_length));
7305
g_assert_true(errno == EINVAL);
7306
g_assert_null(agent_directory);
7307
g_assert_null(helper_directory);
7308
g_assert_null(mandos_argz);
7309
g_assert_true(mandos_argz_length == 0);
7311
for(char **arg = argv; *arg != NULL; arg++){
7316
static void test_parse_arguments_long_dir(__attribute__((unused))
7317
test_fixture *fixture,
7318
__attribute__((unused))
7319
gconstpointer user_data){
7322
strdup("--agent-directory"),
7325
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7327
__attribute__((cleanup(cleanup_string)))
7328
char *agent_directory = NULL;
7329
char *helper_directory = NULL;
7332
__attribute__((cleanup(cleanup_string)))
7333
char *mandos_argz = NULL;
7334
size_t mandos_argz_length = 0;
7336
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7337
&helper_directory, &user, &group,
7338
&mandos_argz, &mandos_argz_length));
7340
g_assert_cmpstr(agent_directory, ==, "/tmp");
7341
g_assert_null(helper_directory);
7342
g_assert_true(user == 0);
7343
g_assert_true(group == 0);
7344
g_assert_null(mandos_argz);
7345
g_assert_true(mandos_argz_length == 0);
7347
for(char **arg = argv; *arg != NULL; arg++){
7352
static void test_parse_arguments_short_dir(__attribute__((unused))
7353
test_fixture *fixture,
7354
__attribute__((unused))
7355
gconstpointer user_data){
7361
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7363
__attribute__((cleanup(cleanup_string)))
7364
char *agent_directory = NULL;
7365
char *helper_directory = NULL;
7368
__attribute__((cleanup(cleanup_string)))
7369
char *mandos_argz = NULL;
7370
size_t mandos_argz_length = 0;
7372
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7373
&helper_directory, &user, &group,
7374
&mandos_argz, &mandos_argz_length));
7376
g_assert_cmpstr(agent_directory, ==, "/tmp");
7377
g_assert_null(helper_directory);
7378
g_assert_true(user == 0);
7379
g_assert_true(group == 0);
7380
g_assert_null(mandos_argz);
7381
g_assert_true(mandos_argz_length == 0);
7383
for(char **arg = argv; *arg != NULL; arg++){
7389
void test_parse_arguments_helper_directory(__attribute__((unused))
7390
test_fixture *fixture,
7391
__attribute__((unused))
7392
gconstpointer user_data){
7395
strdup("--helper-directory"),
7398
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7400
char *agent_directory = NULL;
7401
__attribute__((cleanup(cleanup_string)))
7402
char *helper_directory = NULL;
7405
__attribute__((cleanup(cleanup_string)))
7406
char *mandos_argz = NULL;
7407
size_t mandos_argz_length = 0;
7409
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7410
&helper_directory, &user, &group,
7411
&mandos_argz, &mandos_argz_length));
7413
g_assert_cmpstr(helper_directory, ==, "/tmp");
7414
g_assert_null(agent_directory);
7415
g_assert_true(user == 0);
7416
g_assert_true(group == 0);
7417
g_assert_null(mandos_argz);
7418
g_assert_true(mandos_argz_length == 0);
7420
for(char **arg = argv; *arg != NULL; arg++){
7426
void test_parse_arguments_plugin_helper_dir(__attribute__((unused))
7427
test_fixture *fixture,
7428
__attribute__((unused))
7429
gconstpointer user_data){
7432
strdup("--plugin-helper-dir"),
7435
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7437
char *agent_directory = NULL;
7438
__attribute__((cleanup(cleanup_string)))
7439
char *helper_directory = NULL;
7442
__attribute__((cleanup(cleanup_string)))
7443
char *mandos_argz = NULL;
7444
size_t mandos_argz_length = 0;
7446
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7447
&helper_directory, &user, &group,
7448
&mandos_argz, &mandos_argz_length));
7450
g_assert_cmpstr(helper_directory, ==, "/tmp");
7451
g_assert_null(agent_directory);
7452
g_assert_true(user == 0);
7453
g_assert_true(group == 0);
7454
g_assert_null(mandos_argz);
7455
g_assert_true(mandos_argz_length == 0);
7457
for(char **arg = argv; *arg != NULL; arg++){
7462
static void test_parse_arguments_user(__attribute__((unused))
7463
test_fixture *fixture,
7464
__attribute__((unused))
7465
gconstpointer user_data){
7471
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7473
char *agent_directory = NULL;
7474
__attribute__((cleanup(cleanup_string)))
7475
char *helper_directory = NULL;
7478
__attribute__((cleanup(cleanup_string)))
7479
char *mandos_argz = NULL;
7480
size_t mandos_argz_length = 0;
7482
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7483
&helper_directory, &user, &group,
7484
&mandos_argz, &mandos_argz_length));
7486
g_assert_null(helper_directory);
7487
g_assert_null(agent_directory);
7488
g_assert_cmpuint((unsigned int)user, ==, 1000);
7489
g_assert_true(group == 0);
7490
g_assert_null(mandos_argz);
7491
g_assert_true(mandos_argz_length == 0);
7493
for(char **arg = argv; *arg != NULL; arg++){
7498
static void test_parse_arguments_user_invalid(__attribute__((unused))
7499
test_fixture *fixture,
7500
__attribute__((unused))
7508
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7510
char *agent_directory = NULL;
7511
__attribute__((cleanup(cleanup_string)))
7512
char *helper_directory = NULL;
7515
__attribute__((cleanup(cleanup_string)))
7516
char *mandos_argz = NULL;
7517
size_t mandos_argz_length = 0;
7519
g_assert_false(parse_arguments_devnull(argc, argv, false,
7521
&helper_directory, &user,
7522
&group, &mandos_argz,
7523
&mandos_argz_length));
7525
g_assert_null(helper_directory);
7526
g_assert_null(agent_directory);
7527
g_assert_cmpuint((unsigned int)user, ==, 0);
7528
g_assert_true(group == 0);
7529
g_assert_null(mandos_argz);
7530
g_assert_true(mandos_argz_length == 0);
7532
for(char **arg = argv; *arg != NULL; arg++){
7538
void test_parse_arguments_user_zero_invalid(__attribute__((unused))
7539
test_fixture *fixture,
7540
__attribute__((unused))
7541
gconstpointer user_data){
7547
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7549
char *agent_directory = NULL;
7550
__attribute__((cleanup(cleanup_string)))
7551
char *helper_directory = NULL;
7554
__attribute__((cleanup(cleanup_string)))
7555
char *mandos_argz = NULL;
7556
size_t mandos_argz_length = 0;
7558
g_assert_false(parse_arguments_devnull(argc, argv, false,
7560
&helper_directory, &user,
7561
&group, &mandos_argz,
7562
&mandos_argz_length));
7564
g_assert_null(helper_directory);
7565
g_assert_null(agent_directory);
7566
g_assert_cmpuint((unsigned int)user, ==, 0);
7567
g_assert_true(group == 0);
7568
g_assert_null(mandos_argz);
7569
g_assert_true(mandos_argz_length == 0);
7571
for(char **arg = argv; *arg != NULL; arg++){
7576
static void test_parse_arguments_group(__attribute__((unused))
7577
test_fixture *fixture,
7578
__attribute__((unused))
7579
gconstpointer user_data){
7585
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7587
char *agent_directory = NULL;
7588
__attribute__((cleanup(cleanup_string)))
7589
char *helper_directory = NULL;
7592
__attribute__((cleanup(cleanup_string)))
7593
char *mandos_argz = NULL;
7594
size_t mandos_argz_length = 0;
7596
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7597
&helper_directory, &user, &group,
7598
&mandos_argz, &mandos_argz_length));
7600
g_assert_null(helper_directory);
7601
g_assert_null(agent_directory);
7602
g_assert_true(user == 0);
7603
g_assert_cmpuint((unsigned int)group, ==, 1000);
7604
g_assert_null(mandos_argz);
7605
g_assert_true(mandos_argz_length == 0);
7607
for(char **arg = argv; *arg != NULL; arg++){
7612
static void test_parse_arguments_group_invalid(__attribute__((unused))
7613
test_fixture *fixture,
7614
__attribute__((unused))
7622
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7624
char *agent_directory = NULL;
7625
__attribute__((cleanup(cleanup_string)))
7626
char *helper_directory = NULL;
7629
__attribute__((cleanup(cleanup_string)))
7630
char *mandos_argz = NULL;
7631
size_t mandos_argz_length = 0;
7633
g_assert_false(parse_arguments_devnull(argc, argv, false,
7635
&helper_directory, &user,
7636
&group, &mandos_argz,
7637
&mandos_argz_length));
7639
g_assert_null(helper_directory);
7640
g_assert_null(agent_directory);
7641
g_assert_true(user == 0);
7642
g_assert_true(group == 0);
7643
g_assert_null(mandos_argz);
7644
g_assert_true(mandos_argz_length == 0);
7646
for(char **arg = argv; *arg != NULL; arg++){
7652
void test_parse_arguments_group_zero_invalid(__attribute__((unused))
7653
test_fixture *fixture,
7654
__attribute__((unused))
7655
gconstpointer user_data){
7661
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7663
char *agent_directory = NULL;
7664
__attribute__((cleanup(cleanup_string)))
7665
char *helper_directory = NULL;
7668
__attribute__((cleanup(cleanup_string)))
7669
char *mandos_argz = NULL;
7670
size_t mandos_argz_length = 0;
7672
g_assert_false(parse_arguments_devnull(argc, argv, false,
7674
&helper_directory, &user,
7675
&group, &mandos_argz,
7676
&mandos_argz_length));
7678
g_assert_null(helper_directory);
7679
g_assert_null(agent_directory);
7680
g_assert_cmpuint((unsigned int)group, ==, 0);
7681
g_assert_true(group == 0);
7682
g_assert_null(mandos_argz);
7683
g_assert_true(mandos_argz_length == 0);
7685
for(char **arg = argv; *arg != NULL; arg++){
7690
static void test_parse_arguments_mandos_noargs(__attribute__((unused))
7691
test_fixture *fixture,
7692
__attribute__((unused))
7697
strdup("mandos-client"),
7699
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7701
__attribute__((cleanup(cleanup_string)))
7702
char *agent_directory = NULL;
7703
__attribute__((cleanup(cleanup_string)))
7704
char *helper_directory = NULL;
7707
__attribute__((cleanup(cleanup_string)))
7708
char *mandos_argz = NULL;
7709
size_t mandos_argz_length = 0;
7711
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7712
&helper_directory, &user, &group,
7713
&mandos_argz, &mandos_argz_length));
7715
g_assert_null(agent_directory);
7716
g_assert_null(helper_directory);
7717
g_assert_true(user == 0);
7718
g_assert_true(group == 0);
7719
g_assert_cmpstr(mandos_argz, ==, "mandos-client");
7720
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7721
mandos_argz_length),
7724
for(char **arg = argv; *arg != NULL; arg++){
7729
static void test_parse_arguments_mandos_args(__attribute__((unused))
7730
test_fixture *fixture,
7731
__attribute__((unused))
7732
gconstpointer user_data){
7735
strdup("mandos-client"),
7740
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7742
__attribute__((cleanup(cleanup_string)))
7743
char *agent_directory = NULL;
7744
__attribute__((cleanup(cleanup_string)))
7745
char *helper_directory = NULL;
7748
__attribute__((cleanup(cleanup_string)))
7749
char *mandos_argz = NULL;
7750
size_t mandos_argz_length = 0;
7752
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7753
&helper_directory, &user, &group,
7754
&mandos_argz, &mandos_argz_length));
7756
g_assert_null(agent_directory);
7757
g_assert_null(helper_directory);
7758
g_assert_true(user == 0);
7759
g_assert_true(group == 0);
7760
char *marg = mandos_argz;
7761
g_assert_cmpstr(marg, ==, "mandos-client");
7762
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7763
g_assert_cmpstr(marg, ==, "one");
7764
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7765
g_assert_cmpstr(marg, ==, "two");
7766
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7767
g_assert_cmpstr(marg, ==, "three");
7768
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7769
mandos_argz_length),
7772
for(char **arg = argv; *arg != NULL; arg++){
7777
static void test_parse_arguments_all_args(__attribute__((unused))
7778
test_fixture *fixture,
7779
__attribute__((unused))
7780
gconstpointer user_data){
7783
strdup("--agent-directory"),
7785
strdup("--helper-directory"),
7791
strdup("mandos-client"),
7796
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7798
__attribute__((cleanup(cleanup_string)))
7799
char *agent_directory = NULL;
7800
__attribute__((cleanup(cleanup_string)))
7801
char *helper_directory = NULL;
7804
__attribute__((cleanup(cleanup_string)))
7805
char *mandos_argz = NULL;
7806
size_t mandos_argz_length = 0;
7808
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7809
&helper_directory, &user, &group,
7810
&mandos_argz, &mandos_argz_length));
7812
g_assert_cmpstr(agent_directory, ==, "/tmp");
7813
g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7814
g_assert_true(user == 1);
7815
g_assert_true(group == 2);
7816
char *marg = mandos_argz;
7817
g_assert_cmpstr(marg, ==, "mandos-client");
7818
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7819
g_assert_cmpstr(marg, ==, "one");
7820
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7821
g_assert_cmpstr(marg, ==, "two");
7822
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7823
g_assert_cmpstr(marg, ==, "three");
7824
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7825
mandos_argz_length),
7828
for(char **arg = argv; *arg != NULL; arg++){
7833
static void test_parse_arguments_mixed(__attribute__((unused))
7834
test_fixture *fixture,
7835
__attribute__((unused))
7836
gconstpointer user_data){
7839
strdup("mandos-client"),
7843
strdup("--agent-directory"),
7847
strdup("--helper-directory=/var/tmp"),
7849
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7851
__attribute__((cleanup(cleanup_string)))
7852
char *agent_directory = NULL;
7853
__attribute__((cleanup(cleanup_string)))
7854
char *helper_directory = NULL;
7857
__attribute__((cleanup(cleanup_string)))
7858
char *mandos_argz = NULL;
7859
size_t mandos_argz_length = 0;
7861
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7862
&helper_directory, &user, &group,
7863
&mandos_argz, &mandos_argz_length));
7865
g_assert_cmpstr(agent_directory, ==, "/tmp");
7866
g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7867
g_assert_true(user == 1);
7868
g_assert_true(group == 0);
7869
char *marg = mandos_argz;
7870
g_assert_cmpstr(marg, ==, "mandos-client");
7871
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7872
g_assert_cmpstr(marg, ==, "one");
7873
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7874
g_assert_cmpstr(marg, ==, "two");
7875
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7876
g_assert_cmpstr(marg, ==, "three");
7877
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7878
mandos_argz_length),
7881
for(char **arg = argv; *arg != NULL; arg++){
7886
/* End of tests section */
7888
/* Test boilerplate section; New tests should be added to the test
7889
suite definition here, in the "run_tests" function.
7891
Finally, this section also contains the should_only_run_tests()
7892
function used by main() for deciding if tests should be run or to
7895
__attribute__((cold))
7896
static bool run_tests(int argc, char *argv[]){
7897
g_test_init(&argc, &argv, NULL);
7899
/* A macro to add a test with no setup or teardown functions */
7900
#define test_add(testpath, testfunc) \
7902
g_test_add((testpath), test_fixture, NULL, NULL, \
7903
(testfunc), NULL); \
7906
/* Test the signal-related functions first, since some other tests
7907
depend on these functions in their setups and teardowns */
7908
test_add("/signal-handling/setup", test_setup_signal_handler);
7909
test_add("/signal-handling/restore", test_restore_signal_handler);
7910
test_add("/signal-handling/block", test_block_sigchld);
7911
test_add("/signal-handling/restore-sigmask", test_restore_sigmask);
7913
/* Regular non-signal-related tests; these use no setups or
7915
test_add("/parse_arguments/noargs", test_parse_arguments_noargs);
7916
test_add("/parse_arguments/invalid", test_parse_arguments_invalid);
7917
test_add("/parse_arguments/long-dir",
7918
test_parse_arguments_long_dir);
7919
test_add("/parse_arguments/short-dir",
7920
test_parse_arguments_short_dir);
7921
test_add("/parse_arguments/helper-directory",
7922
test_parse_arguments_helper_directory);
7923
test_add("/parse_arguments/plugin-helper-dir",
7924
test_parse_arguments_plugin_helper_dir);
7925
test_add("/parse_arguments/user", test_parse_arguments_user);
7926
test_add("/parse_arguments/user-invalid",
7927
test_parse_arguments_user_invalid);
7928
test_add("/parse_arguments/user-zero-invalid",
7929
test_parse_arguments_user_zero_invalid);
7930
test_add("/parse_arguments/group", test_parse_arguments_group);
7931
test_add("/parse_arguments/group-invalid",
7932
test_parse_arguments_group_invalid);
7933
test_add("/parse_arguments/group-zero-invalid",
7934
test_parse_arguments_group_zero_invalid);
7935
test_add("/parse_arguments/mandos-noargs",
7936
test_parse_arguments_mandos_noargs);
7937
test_add("/parse_arguments/mandos-args",
7938
test_parse_arguments_mandos_args);
7939
test_add("/parse_arguments/all-args",
7940
test_parse_arguments_all_args);
7941
test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7942
test_add("/queue/create", test_create_queue);
7943
test_add("/queue/add", test_add_to_queue);
7944
test_add("/queue/add/overflow", test_add_to_queue_overflow);
7945
test_add("/queue/has_question/empty",
7946
test_queue_has_question_empty);
7947
test_add("/queue/has_question/false",
7948
test_queue_has_question_false);
7949
test_add("/queue/has_question/true", test_queue_has_question_true);
7950
test_add("/queue/has_question/false2",
7951
test_queue_has_question_false2);
7952
test_add("/queue/has_question/true2",
7953
test_queue_has_question_true2);
7954
test_add("/buffer/cleanup", test_cleanup_buffer);
7955
test_add("/string_set/net-set-contains-nothing",
7956
test_string_set_new_set_contains_nothing);
7957
test_add("/string_set/with-added-string-contains-it",
7958
test_string_set_with_added_string_contains_it);
7959
test_add("/string_set/cleared-does-not-contain-string",
7960
test_string_set_cleared_does_not_contain_str);
7961
test_add("/string_set/swap/one-with-empty",
7962
test_string_set_swap_one_with_empty);
7963
test_add("/string_set/swap/empty-with-one",
7964
test_string_set_swap_empty_with_one);
7965
test_add("/string_set/swap/one-with-one",
7966
test_string_set_swap_one_with_one);
7968
/* A macro to add a test using the setup and teardown functions */
7969
#define test_add_st(path, func) \
7971
g_test_add((path), test_fixture, NULL, test_setup, (func), \
7975
/* Signal-related tests; these use setups and teardowns which
7976
establish, during each test run, a signal handler for, and a
7977
signal mask blocking, the SIGCHLD signal, just like main() */
7978
test_add_st("/wait_for_event/timeout", test_wait_for_event_timeout);
7979
test_add_st("/wait_for_event/event", test_wait_for_event_event);
7980
test_add_st("/wait_for_event/sigchld", test_wait_for_event_sigchld);
7981
test_add_st("/run_queue/zeroes-next-run",
7982
test_run_queue_zeroes_next_run);
7983
test_add_st("/run_queue/clears-cancelled_filenames",
7984
test_run_queue_clears_cancelled_filenames);
7985
test_add_st("/run_queue/skips-cancelled-filenames",
7986
test_run_queue_skips_cancelled_filenames);
7987
test_add_st("/run_queue/one-task", test_run_queue_one_task);
7988
test_add_st("/run_queue/two-tasks", test_run_queue_two_tasks);
7989
test_add_st("/run_queue/two-tasks/quit",
7990
test_run_queue_two_tasks_quit);
7991
test_add_st("/run_queue/two-tasks-cleanup",
7992
test_run_queue_two_tasks_cleanup);
7993
test_add_st("/task-creators/start_mandos_client",
7994
test_start_mandos_client);
7995
test_add_st("/task-creators/start_mandos_client/execv",
7996
test_start_mandos_client_execv);
7997
test_add_st("/task-creators/start_mandos_client/suid/euid",
7998
test_start_mandos_client_suid_euid);
7999
test_add_st("/task-creators/start_mandos_client/suid/egid",
8000
test_start_mandos_client_suid_egid);
8001
test_add_st("/task-creators/start_mandos_client/suid/ruid",
8002
test_start_mandos_client_suid_ruid);
8003
test_add_st("/task-creators/start_mandos_client/suid/rgid",
8004
test_start_mandos_client_suid_rgid);
8005
test_add_st("/task-creators/start_mandos_client/read",
8006
test_start_mandos_client_read);
8007
test_add_st("/task-creators/start_mandos_client/helper-directory",
8008
test_start_mandos_client_helper_directory);
8009
test_add_st("/task-creators/start_mandos_client/sigmask",
8010
test_start_mandos_client_sigmask);
8011
test_add_st("/task/wait_for_mandos_client_exit/badpid",
8012
test_wait_for_mandos_client_exit_badpid);
8013
test_add_st("/task/wait_for_mandos_client_exit/noexit",
8014
test_wait_for_mandos_client_exit_noexit);
8015
test_add_st("/task/wait_for_mandos_client_exit/success",
8016
test_wait_for_mandos_client_exit_success);
8017
test_add_st("/task/wait_for_mandos_client_exit/failure",
8018
test_wait_for_mandos_client_exit_failure);
8019
test_add_st("/task/wait_for_mandos_client_exit/killed",
8020
test_wait_for_mandos_client_exit_killed);
8021
test_add_st("/task/read_mandos_client_output/readerror",
8022
test_read_mandos_client_output_readerror);
8023
test_add_st("/task/read_mandos_client_output/nodata",
8024
test_read_mandos_client_output_nodata);
8025
test_add_st("/task/read_mandos_client_output/eof",
8026
test_read_mandos_client_output_eof);
8027
test_add_st("/task/read_mandos_client_output/once",
8028
test_read_mandos_client_output_once);
8029
test_add_st("/task/read_mandos_client_output/malloc",
8030
test_read_mandos_client_output_malloc);
8031
test_add_st("/task/read_mandos_client_output/append",
8032
test_read_mandos_client_output_append);
8033
test_add_st("/task-creators/add_inotify_dir_watch",
8034
test_add_inotify_dir_watch);
8035
test_add_st("/task-creators/add_inotify_dir_watch/fail",
8036
test_add_inotify_dir_watch_fail);
8037
test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory",
8038
test_add_inotify_dir_watch_nondir);
8039
test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
8040
test_add_inotify_dir_watch_EAGAIN);
8041
test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
8042
test_add_inotify_dir_watch_IN_CLOSE_WRITE);
8043
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
8044
test_add_inotify_dir_watch_IN_MOVED_TO);
8045
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
8046
test_add_inotify_dir_watch_IN_MOVED_FROM);
8047
test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
8048
test_add_inotify_dir_watch_IN_EXCL_UNLINK);
8049
test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
8050
test_add_inotify_dir_watch_IN_DELETE);
8051
test_add_st("/task/read_inotify_event/readerror",
8052
test_read_inotify_event_readerror);
8053
test_add_st("/task/read_inotify_event/bad-epoll",
8054
test_read_inotify_event_bad_epoll);
8055
test_add_st("/task/read_inotify_event/nodata",
8056
test_read_inotify_event_nodata);
8057
test_add_st("/task/read_inotify_event/eof",
8058
test_read_inotify_event_eof);
8059
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE",
8060
test_read_inotify_event_IN_CLOSE_WRITE);
8061
test_add_st("/task/read_inotify_event/IN_MOVED_TO",
8062
test_read_inotify_event_IN_MOVED_TO);
8063
test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
8064
test_read_inotify_event_IN_MOVED_FROM);
8065
test_add_st("/task/read_inotify_event/IN_DELETE",
8066
test_read_inotify_event_IN_DELETE);
8067
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
8068
test_read_inotify_event_IN_CLOSE_WRITE_badname);
8069
test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
8070
test_read_inotify_event_IN_MOVED_TO_badname);
8071
test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
8072
test_read_inotify_event_IN_MOVED_FROM_badname);
8073
test_add_st("/task/read_inotify_event/IN_DELETE/badname",
8074
test_read_inotify_event_IN_DELETE_badname);
8075
test_add_st("/task/open_and_parse_question/ENOENT",
8076
test_open_and_parse_question_ENOENT);
8077
test_add_st("/task/open_and_parse_question/EIO",
8078
test_open_and_parse_question_EIO);
8079
test_add_st("/task/open_and_parse_question/parse-error",
8080
test_open_and_parse_question_parse_error);
8081
test_add_st("/task/open_and_parse_question/nosocket",
8082
test_open_and_parse_question_nosocket);
8083
test_add_st("/task/open_and_parse_question/badsocket",
8084
test_open_and_parse_question_badsocket);
8085
test_add_st("/task/open_and_parse_question/nopid",
8086
test_open_and_parse_question_nopid);
8087
test_add_st("/task/open_and_parse_question/badpid",
8088
test_open_and_parse_question_badpid);
8089
test_add_st("/task/open_and_parse_question/noexist_pid",
8090
test_open_and_parse_question_noexist_pid);
8091
test_add_st("/task/open_and_parse_question/no-notafter",
8092
test_open_and_parse_question_no_notafter);
8093
test_add_st("/task/open_and_parse_question/bad-notafter",
8094
test_open_and_parse_question_bad_notafter);
8095
test_add_st("/task/open_and_parse_question/notafter-0",
8096
test_open_and_parse_question_notafter_0);
8097
test_add_st("/task/open_and_parse_question/notafter-1",
8098
test_open_and_parse_question_notafter_1);
8099
test_add_st("/task/open_and_parse_question/notafter-1-1",
8100
test_open_and_parse_question_notafter_1_1);
8101
test_add_st("/task/open_and_parse_question/notafter-1-2",
8102
test_open_and_parse_question_notafter_1_2);
8103
test_add_st("/task/open_and_parse_question/equal-notafter",
8104
test_open_and_parse_question_equal_notafter);
8105
test_add_st("/task/open_and_parse_question/late-notafter",
8106
test_open_and_parse_question_late_notafter);
8107
test_add_st("/task/cancel_old_question/0-1-2",
8108
test_cancel_old_question_0_1_2);
8109
test_add_st("/task/cancel_old_question/0-2-1",
8110
test_cancel_old_question_0_2_1);
8111
test_add_st("/task/cancel_old_question/1-2-3",
8112
test_cancel_old_question_1_2_3);
8113
test_add_st("/task/cancel_old_question/1-3-2",
8114
test_cancel_old_question_1_3_2);
8115
test_add_st("/task/cancel_old_question/2-1-3",
8116
test_cancel_old_question_2_1_3);
8117
test_add_st("/task/cancel_old_question/2-3-1",
8118
test_cancel_old_question_2_3_1);
8119
test_add_st("/task/cancel_old_question/3-1-2",
8120
test_cancel_old_question_3_1_2);
8121
test_add_st("/task/cancel_old_question/3-2-1",
8122
test_cancel_old_question_3_2_1);
8123
test_add_st("/task/connect_question_socket/name-too-long",
8124
test_connect_question_socket_name_too_long);
8125
test_add_st("/task/connect_question_socket/connect-fail",
8126
test_connect_question_socket_connect_fail);
8127
test_add_st("/task/connect_question_socket/bad-epoll",
8128
test_connect_question_socket_bad_epoll);
8129
test_add_st("/task/connect_question_socket/usable",
8130
test_connect_question_socket_usable);
8131
test_add_st("/task/send_password_to_socket/client-not-exited",
8132
test_send_password_to_socket_client_not_exited);
8133
test_add_st("/task/send_password_to_socket/password-not-read",
8134
test_send_password_to_socket_password_not_read);
8135
test_add_st("/task/send_password_to_socket/EMSGSIZE",
8136
test_send_password_to_socket_EMSGSIZE);
8137
test_add_st("/task/send_password_to_socket/retry",
8138
test_send_password_to_socket_retry);
8139
test_add_st("/task/send_password_to_socket/bad-epoll",
8140
test_send_password_to_socket_bad_epoll);
8141
test_add_st("/task/send_password_to_socket/null-password",
8142
test_send_password_to_socket_null_password);
8143
test_add_st("/task/send_password_to_socket/empty-password",
8144
test_send_password_to_socket_empty_password);
8145
test_add_st("/task/send_password_to_socket/empty-str-password",
8146
test_send_password_to_socket_empty_str_pass);
8147
test_add_st("/task/send_password_to_socket/text-password",
8148
test_send_password_to_socket_text_password);
8149
test_add_st("/task/send_password_to_socket/binary-password",
8150
test_send_password_to_socket_binary_password);
8151
test_add_st("/task/send_password_to_socket/nuls-in-password",
8152
test_send_password_to_socket_nuls_in_password);
8153
test_add_st("/task-creators/add_existing_questions/ENOENT",
8154
test_add_existing_questions_ENOENT);
8155
test_add_st("/task-creators/add_existing_questions/no-questions",
8156
test_add_existing_questions_no_questions);
8157
test_add_st("/task-creators/add_existing_questions/one-question",
8158
test_add_existing_questions_one_question);
8159
test_add_st("/task-creators/add_existing_questions/two-questions",
8160
test_add_existing_questions_two_questions);
8161
test_add_st("/task-creators/add_existing_questions/non-questions",
8162
test_add_existing_questions_non_questions);
8163
test_add_st("/task-creators/add_existing_questions/both-types",
8164
test_add_existing_questions_both_types);
8166
return g_test_run() == 0;
8169
static bool should_only_run_tests(int *argc_p, char **argv_p[]){
8170
GOptionContext *context = g_option_context_new("");
8172
g_option_context_set_help_enabled(context, FALSE);
8173
g_option_context_set_ignore_unknown_options(context, TRUE);
8175
gboolean should_run_tests = FALSE;
8176
GOptionEntry entries[] = {
8177
{ "test", 0, 0, G_OPTION_ARG_NONE,
8178
&should_run_tests, "Run tests", NULL },
8181
g_option_context_add_main_entries(context, entries, NULL);
8183
GError *error = NULL;
8185
if(g_option_context_parse(context, argc_p, argv_p, &error) != TRUE){
8186
g_option_context_free(context);
8187
g_error("Failed to parse options: %s", error->message);
8190
g_option_context_free(context);
8191
return should_run_tests != FALSE;