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-2021 Teddy Hogeborn
6
* Copyright © 2019-2021 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>.
26
#define _GNU_SOURCE /* pipe2(), O_CLOEXEC, setresgid(),
27
setresuid(), asprintf(), getline(),
29
#include <inttypes.h> /* uintmax_t, strtoumax(), PRIuMAX,
30
PRIdMAX, intmax_t, uint32_t,
31
SCNx32, SCNuMAX, SCNxMAX */
32
#include <stddef.h> /* size_t, NULL */
33
#include <sys/types.h> /* pid_t, uid_t, gid_t, getuid(),
35
#include <stdbool.h> /* bool, true, false */
36
#include <signal.h> /* struct sigaction, sigset_t,
37
sigemptyset(), sigaddset(),
38
SIGCHLD, pthread_sigmask(),
39
SIG_BLOCK, SIG_SETMASK, SA_RESTART,
40
SA_NOCLDSTOP, sigfillset(), kill(),
41
SIGTERM, sigdelset(), SIGKILL,
42
NSIG, sigismember(), SA_ONSTACK,
43
SIG_DFL, SIG_IGN, SIGINT, SIGQUIT,
44
SIGHUP, SIGSTOP, SIG_UNBLOCK */
45
#include <unistd.h> /* uid_t, gid_t, close(), pipe2(),
46
fork(), _exit(), dup2(),
47
STDOUT_FILENO, setresgid(),
48
setresuid(), execv(), ssize_t,
49
read(), dup3(), getuid(), dup(),
50
STDERR_FILENO, pause(), write(),
51
rmdir(), unlink(), getpid() */
52
#include <stdlib.h> /* EXIT_SUCCESS, EXIT_FAILURE,
53
malloc(), free(), realloc(),
54
setenv(), calloc(), mkdtemp(),
56
#include <iso646.h> /* not, or, and, xor */
57
#include <error.h> /* error() */
58
#include <sysexits.h> /* EX_USAGE, EX_OSERR, EX_OSFILE */
59
#include <errno.h> /* errno, error_t, EACCES,
60
ENAMETOOLONG, ENOENT, ENOTDIR,
61
ENOMEM, EEXIST, ECHILD, EPERM,
62
EAGAIN, EINTR, ENOBUFS, EADDRINUSE,
63
ECONNREFUSED, ECONNRESET,
64
ETOOMANYREFS, EMSGSIZE, EBADF,
66
#include <string.h> /* strdup(), memcpy(),
67
explicit_bzero(), memset(),
68
strcmp(), strlen(), strncpy(),
69
memcmp(), basename(), strerror() */
70
#include <argz.h> /* argz_create(), argz_count(),
71
argz_extract(), argz_next(),
73
#include <sys/epoll.h> /* epoll_create1(), EPOLL_CLOEXEC,
74
epoll_ctl(), EPOLL_CTL_ADD,
75
struct epoll_event, EPOLLIN,
78
#include <time.h> /* struct timespec, clock_gettime(),
80
#include <argp.h> /* struct argp_option, OPTION_HIDDEN,
81
OPTION_ALIAS, struct argp_state,
82
ARGP_ERR_UNKNOWN, ARGP_KEY_ARGS,
83
struct argp, argp_parse(),
85
#include <stdint.h> /* SIZE_MAX, uint32_t */
86
#include <sys/mman.h> /* munlock(), mlock() */
87
#include <fcntl.h> /* O_CLOEXEC, O_NONBLOCK, fcntl(),
88
F_GETFD, F_GETFL, FD_CLOEXEC,
89
open(), O_WRONLY, O_NOCTTY,
90
O_RDONLY, O_NOFOLLOW */
91
#include <sys/wait.h> /* waitpid(), WNOHANG, WIFEXITED(),
93
#include <limits.h> /* PIPE_BUF, NAME_MAX, INT_MAX */
94
#include <sys/inotify.h> /* inotify_init1(), IN_NONBLOCK,
95
IN_CLOEXEC, inotify_add_watch(),
96
IN_CLOSE_WRITE, IN_MOVED_TO,
97
IN_MOVED_FROM, IN_DELETE,
98
IN_EXCL_UNLINK, IN_ONLYDIR,
99
struct inotify_event */
100
#include <fnmatch.h> /* fnmatch(), FNM_FILE_NAME */
101
#include <stdio.h> /* asprintf(), FILE, stderr, fopen(),
102
fclose(), getline(), sscanf(),
103
feof(), ferror(), rename(),
104
fdopen(), fprintf(), fscanf() */
105
#include <glib.h> /* GKeyFile, g_key_file_free(), g_key_file_new(),
106
GError, g_key_file_load_from_file(),
107
G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
108
g_key_file_get_string(), guint64,
109
g_key_file_get_uint64(),
110
G_KEY_FILE_ERROR_KEY_NOT_FOUND, gconstpointer,
111
g_assert_true(), g_assert_nonnull(),
112
g_assert_null(), g_assert_false(),
113
g_assert_cmpint(), g_assert_cmpuint(),
114
g_test_skip(), g_assert_cmpstr(),
115
g_test_message(), g_test_init(), g_test_add(),
116
g_test_run(), GOptionContext,
117
g_option_context_new(),
118
g_option_context_set_help_enabled(), FALSE,
119
g_option_context_set_ignore_unknown_options(),
120
gboolean, GOptionEntry, G_OPTION_ARG_NONE,
121
g_option_context_add_main_entries(),
122
g_option_context_parse(),
123
g_option_context_free(), g_error() */
124
#include <sys/un.h> /* struct sockaddr_un, SUN_LEN */
125
#include <sys/socket.h> /* AF_LOCAL, socket(), PF_LOCAL,
126
SOCK_DGRAM, SOCK_NONBLOCK,
127
SOCK_CLOEXEC, connect(),
128
struct sockaddr, socklen_t,
129
shutdown(), SHUT_RD, send(),
130
MSG_NOSIGNAL, bind(), recv(),
132
#include <glob.h> /* globfree(), glob_t, glob(),
133
GLOB_ERR, GLOB_NOSORT, GLOB_MARK,
134
GLOB_ABORTED, GLOB_NOMATCH,
137
/* End of includes */
139
/* Start of declarations of private types and functions */
141
/* microseconds of CLOCK_MONOTONIC absolute time; 0 means unset */
142
typedef uintmax_t mono_microsecs;
144
/* "task_queue" - A queue of tasks to be run */
146
struct task_struct *tasks; /* Tasks in this queue */
147
size_t length; /* Number of tasks */
148
/* Memory allocated for "tasks", in bytes */
150
/* Time when this queue should be run, at the latest */
151
mono_microsecs next_run;
152
} __attribute__((designated_init)) task_queue;
154
/* "task_func" - A function type for task functions
156
I.e. functions for the code which runs when a task is run, all have
158
typedef void (task_func) (const struct task_struct,
160
__attribute__((nonnull));
162
/* "buffer" - A data buffer for a growing array of bytes
164
Used for the "password" variable */
169
} __attribute__((designated_init)) buffer;
171
/* "string_set" - A set type which can contain strings
173
Used by the "cancelled_filenames" variable */
175
char *argz; /* Do not access these except in */
176
size_t argz_len; /* the string_set_* functions */
177
} __attribute__((designated_init)) string_set;
179
/* "task_context" - local variables for tasks
181
This data structure distinguishes between different tasks which are
182
using the same function. This data structure is passed to every
183
task function when each task is run.
185
Note that not every task uses every struct member. */
186
typedef struct task_struct {
187
task_func *const func; /* The function run by this task */
188
char *const question_filename; /* The question file */
189
const pid_t pid; /* Mandos client process ID */
190
const int epoll_fd; /* The epoll set file descriptor */
191
bool *const quit_now; /* Set to true on fatal errors */
192
const int fd; /* General purpose file descriptor */
193
bool *const mandos_client_exited; /* Set true when client exits */
194
buffer *const password; /* As read from client process */
195
bool *const password_is_read; /* "password" is done growing */
196
char *filename; /* General purpose file name */
197
/* A set of strings of all the file names of questions which have
198
been cancelled for any reason; tasks pertaining to these question
199
files should not be run */
200
string_set *const cancelled_filenames;
201
const mono_microsecs notafter; /* "NotAfter" from question file */
202
/* Updated before each queue run; is compared with queue.next_run */
203
const mono_microsecs *const current_time;
204
} __attribute__((designated_init)) task_context;
206
/* Declare all our functions here so we can define them in any order
207
below. Note: test functions are *not* declared here, they are
208
declared in the test section. */
209
__attribute__((warn_unused_result))
210
static bool should_only_run_tests(int *, char **[]);
211
__attribute__((warn_unused_result, cold))
212
static bool run_tests(int, char *[]);
213
static void handle_sigchld(__attribute__((unused)) int sig){}
214
__attribute__((warn_unused_result, malloc))
215
task_queue *create_queue(void);
216
__attribute__((nonnull, warn_unused_result))
217
bool add_to_queue(task_queue *const, const task_context);
218
__attribute__((nonnull))
219
void cleanup_task(const task_context *const);
220
__attribute__((nonnull))
221
void cleanup_queue(task_queue *const *const);
222
__attribute__((pure, nonnull, warn_unused_result))
223
bool queue_has_question(const task_queue *const);
224
__attribute__((nonnull))
225
void cleanup_close(const int *const);
226
__attribute__((nonnull))
227
void cleanup_string(char *const *const);
228
__attribute__((nonnull))
229
void cleanup_buffer(buffer *const);
230
__attribute__((pure, nonnull, warn_unused_result))
231
bool string_set_contains(const string_set, const char *const);
232
__attribute__((nonnull, warn_unused_result))
233
bool string_set_add(string_set *const, const char *const);
234
__attribute__((nonnull))
235
void string_set_clear(string_set *);
236
void string_set_swap(string_set *const, string_set *const);
237
__attribute__((nonnull, warn_unused_result))
238
bool start_mandos_client(task_queue *const, const int, bool *const,
239
bool *const, buffer *const, bool *const,
240
const struct sigaction *const,
241
const sigset_t, const char *const,
242
const uid_t, const gid_t,
243
const char *const *const);
244
__attribute__((nonnull))
245
task_func wait_for_mandos_client_exit;
246
__attribute__((nonnull))
247
task_func read_mandos_client_output;
248
__attribute__((warn_unused_result))
249
bool add_inotify_dir_watch(task_queue *const, const int, bool *const,
250
buffer *const, const char *const,
251
string_set *, const mono_microsecs *const,
252
bool *const, bool *const);
253
__attribute__((nonnull))
254
task_func read_inotify_event;
255
__attribute__((nonnull))
256
task_func open_and_parse_question;
257
__attribute__((nonnull))
258
task_func cancel_old_question;
259
__attribute__((nonnull))
260
task_func connect_question_socket;
261
__attribute__((nonnull))
262
task_func send_password_to_socket;
263
__attribute__((warn_unused_result))
264
bool add_existing_questions(task_queue *const, const int,
265
buffer *const, string_set *,
266
const mono_microsecs *const,
267
bool *const, bool *const,
269
__attribute__((nonnull, warn_unused_result))
270
bool wait_for_event(const int, const mono_microsecs,
271
const mono_microsecs);
272
bool run_queue(task_queue **const, string_set *const, bool *const);
273
bool clear_all_fds_from_epoll_set(const int);
274
mono_microsecs get_current_time(void);
275
__attribute__((nonnull, warn_unused_result))
276
bool setup_signal_handler(struct sigaction *const);
277
__attribute__((nonnull))
278
bool restore_signal_handler(const struct sigaction *const);
279
__attribute__((nonnull, warn_unused_result))
280
bool block_sigchld(sigset_t *const);
281
__attribute__((nonnull))
282
bool restore_sigmask(const sigset_t *const);
283
__attribute__((nonnull))
284
bool parse_arguments(int, char *[], const bool, char **, char **,
285
uid_t *const , gid_t *const, char **, size_t *);
287
/* End of declarations of private types and functions */
289
/* Start of "main" section; this section LACKS TESTS!
291
Code here should be as simple as possible. */
293
/* These are required to be global by Argp */
294
const char *argp_program_version = "password-agent " VERSION;
295
const char *argp_program_bug_address = "<mandos@recompile.se>";
297
int main(int argc, char *argv[]){
299
/* If the --test option is passed, skip all normal operations and
300
instead only run the run_tests() function, which also does all
301
its own option parsing, so we don't have to do anything here. */
302
if(should_only_run_tests(&argc, &argv)){
303
if(run_tests(argc, argv)){
304
return EXIT_SUCCESS; /* All tests successful */
306
return EXIT_FAILURE; /* Some test(s) failed */
309
__attribute__((cleanup(cleanup_string)))
310
char *agent_directory = NULL;
312
__attribute__((cleanup(cleanup_string)))
313
char *helper_directory = NULL;
318
__attribute__((cleanup(cleanup_string)))
319
char *mandos_argz = NULL;
320
size_t mandos_argz_length = 0;
322
if(not parse_arguments(argc, argv, true, &agent_directory,
323
&helper_directory, &user, &group,
324
&mandos_argz, &mandos_argz_length)){
325
/* This should never happen, since "true" is passed as the third
326
argument to parse_arguments() above, which should make
327
argp_parse() call exit() if any parsing error occurs. */
328
error(EX_USAGE, errno, "Failed to parse arguments");
331
const char default_agent_directory[] = "/run/systemd/ask-password";
332
const char default_helper_directory[]
333
= "/lib/mandos/plugin-helpers";
334
const char *const default_argv[]
335
= {"/lib/mandos/plugins.d/mandos-client", NULL };
337
/* Set variables to default values if unset */
338
if(agent_directory == NULL){
339
agent_directory = strdup(default_agent_directory);
340
if(agent_directory == NULL){
341
error(EX_OSERR, errno, "Failed strdup()");
344
if(helper_directory == NULL){
345
helper_directory = strdup(default_helper_directory);
346
if(helper_directory == NULL){
347
error(EX_OSERR, errno, "Failed strdup()");
351
user = 65534; /* nobody */
354
group = 65534; /* nogroup */
356
/* If parse_opt did not create an argz vector, create one with
358
if(mandos_argz == NULL){
360
#pragma GCC diagnostic push
361
/* argz_create() takes a non-const argv for some unknown reason -
362
argz_create() isn't modifying the strings, just copying them.
363
Therefore, this cast to non-const should be safe. */
364
#pragma GCC diagnostic ignored "-Wcast-qual"
366
errno = argz_create((char *const *)default_argv, &mandos_argz,
367
&mandos_argz_length);
369
#pragma GCC diagnostic pop
372
error(EX_OSERR, errno, "Failed argz_create()");
375
/* Use argz vector to create a normal argv, usable by execv() */
377
char **mandos_argv = malloc((argz_count(mandos_argz,
379
+ 1) * sizeof(char *));
380
if(mandos_argv == NULL){
381
error_t saved_errno = errno;
383
error(EX_OSERR, saved_errno, "Failed malloc()");
385
argz_extract(mandos_argz, mandos_argz_length, mandos_argv);
387
sigset_t orig_sigmask;
388
if(not block_sigchld(&orig_sigmask)){
392
struct sigaction old_sigchld_action;
393
if(not setup_signal_handler(&old_sigchld_action)){
397
mono_microsecs current_time = 0;
399
bool mandos_client_exited = false;
400
bool quit_now = false;
401
__attribute__((cleanup(cleanup_close)))
402
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
404
error(EX_OSERR, errno, "Failed to create epoll set fd");
406
__attribute__((cleanup(cleanup_queue)))
407
task_queue *queue = create_queue();
409
error(EX_OSERR, errno, "Failed to create task queue");
412
__attribute__((cleanup(cleanup_buffer)))
413
buffer password = {};
414
bool password_is_read = false;
416
__attribute__((cleanup(string_set_clear)))
417
string_set cancelled_filenames = {};
419
/* Add tasks to queue */
420
if(not start_mandos_client(queue, epoll_fd, &mandos_client_exited,
421
&quit_now, &password, &password_is_read,
422
&old_sigchld_action, orig_sigmask,
423
helper_directory, user, group,
424
(const char *const *)mandos_argv)){
425
return EX_OSERR; /* Error has already been printed */
427
/* These variables were only for start_mandos_client() and are not
432
if(not add_inotify_dir_watch(queue, epoll_fd, &quit_now, &password,
433
agent_directory, &cancelled_filenames,
434
¤t_time, &mandos_client_exited,
436
switch(errno){ /* Error has already been printed */
446
if(not add_existing_questions(queue, epoll_fd, &password,
447
&cancelled_filenames, ¤t_time,
448
&mandos_client_exited,
449
&password_is_read, agent_directory)){
450
return EXIT_FAILURE; /* Error has already been printed */
455
current_time = get_current_time();
456
if(not wait_for_event(epoll_fd, queue->next_run, current_time)){
457
const error_t saved_errno = errno;
458
error(EXIT_FAILURE, saved_errno, "Failure while waiting for"
462
current_time = get_current_time();
463
if(not run_queue(&queue, &cancelled_filenames, &quit_now)){
464
const error_t saved_errno = errno;
465
error(EXIT_FAILURE, saved_errno, "Failure while running queue");
468
/* When no tasks about questions are left in the queue, break out
469
of the loop (and implicitly exit the program) */
470
} while(queue_has_question(queue));
472
restore_signal_handler(&old_sigchld_action);
473
restore_sigmask(&orig_sigmask);
478
__attribute__((warn_unused_result))
479
mono_microsecs get_current_time(void){
480
struct timespec currtime;
481
if(clock_gettime(CLOCK_MONOTONIC, &currtime) != 0){
482
error(0, errno, "Failed to get current time");
485
return ((mono_microsecs)currtime.tv_sec * 1000000) /* seconds */
486
+ ((mono_microsecs)currtime.tv_nsec / 1000); /* nanoseconds */
489
/* End of "main" section */
491
/* Start of regular code section; ALL this code has tests */
493
__attribute__((nonnull))
494
bool parse_arguments(int argc, char *argv[], const bool exit_failure,
495
char **agent_directory, char **helper_directory,
496
uid_t *const user, gid_t *const group,
497
char **mandos_argz, size_t *mandos_argz_length){
499
const struct argp_option options[] = {
500
{ .name="agent-directory",.key='d', .arg="DIRECTORY",
501
.doc="Systemd password agent directory" },
502
{ .name="helper-directory",.key=128, .arg="DIRECTORY",
503
.doc="Mandos Client password helper directory" },
504
{ .name="plugin-helper-dir", .key=129, /* From plugin-runner */
505
.flags=OPTION_HIDDEN | OPTION_ALIAS },
506
{ .name="user", .key='u', .arg="USERID",
507
.doc="User ID the Mandos Client will use as its unprivileged"
509
{ .name="userid", .key=130, /* From plugin--runner */
510
.flags=OPTION_HIDDEN | OPTION_ALIAS },
511
{ .name="group", .key='g', .arg="GROUPID",
512
.doc="Group ID the Mandos Client will use as its unprivileged"
514
{ .name="groupid", .key=131, /* From plugin--runner */
515
.flags=OPTION_HIDDEN | OPTION_ALIAS },
516
{ .name="test", .key=255, /* See should_only_run_tests() */
517
.doc="Skip normal operation, and only run self-tests. See"
518
" --test --help.", .group=10, },
522
__attribute__((nonnull(3)))
523
error_t parse_opt(int key, char *arg, struct argp_state *state){
526
case 'd': /* --agent-directory */
527
*agent_directory = strdup(arg);
529
case 128: /* --helper-directory */
530
case 129: /* --plugin-helper-dir */
531
*helper_directory = strdup(arg);
533
case 'u': /* --user */
534
case 130: /* --userid */
537
uintmax_t tmp_id = 0;
539
tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
540
if(errno != 0 or tmp == arg or *tmp != '\0'
541
or tmp_id != (uid_t)tmp_id or (uid_t)tmp_id == 0){
542
return ARGP_ERR_UNKNOWN;
544
*user = (uid_t)tmp_id;
548
case 'g': /* --group */
549
case 131: /* --groupid */
552
uintmax_t tmp_id = 0;
554
tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
555
if(errno != 0 or tmp == arg or *tmp != '\0'
556
or tmp_id != (gid_t)tmp_id or (gid_t)tmp_id == 0){
557
return ARGP_ERR_UNKNOWN;
559
*group = (gid_t)tmp_id;
564
/* Copy arguments into argz vector */
565
return argz_create(state->argv + state->next, mandos_argz,
568
return ARGP_ERR_UNKNOWN;
573
const struct argp argp = {
576
.args_doc="[MANDOS_CLIENT [OPTION...]]\n--test",
577
.doc = "Mandos password agent -- runs Mandos client as a"
578
" systemd password agent",
581
errno = argp_parse(&argp, argc, argv,
582
exit_failure ? 0 : ARGP_NO_EXIT, NULL, NULL);
587
__attribute__((nonnull, warn_unused_result))
588
bool block_sigchld(sigset_t *const orig_sigmask){
589
sigset_t sigchld_sigmask;
590
if(sigemptyset(&sigchld_sigmask) < 0){
591
error(0, errno, "Failed to empty signal set");
594
if(sigaddset(&sigchld_sigmask, SIGCHLD) < 0){
595
error(0, errno, "Failed to add SIGCHLD to signal set");
598
if(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask, orig_sigmask) != 0){
599
error(0, errno, "Failed to block SIGCHLD signal");
605
__attribute__((nonnull, warn_unused_result, const))
606
bool restore_sigmask(const sigset_t *const orig_sigmask){
607
if(pthread_sigmask(SIG_SETMASK, orig_sigmask, NULL) != 0){
608
error(0, errno, "Failed to restore blocked signals");
614
__attribute__((nonnull, warn_unused_result))
615
bool setup_signal_handler(struct sigaction *const old_sigchld_action){
616
struct sigaction sigchld_action = {
617
.sa_handler=handle_sigchld,
618
.sa_flags=SA_RESTART | SA_NOCLDSTOP,
620
/* Set all signals in "sa_mask" struct member; this makes all
621
signals automatically blocked during signal handler */
622
if(sigfillset(&sigchld_action.sa_mask) != 0){
623
error(0, errno, "Failed to do sigfillset()");
626
if(sigaction(SIGCHLD, &sigchld_action, old_sigchld_action) != 0){
627
error(0, errno, "Failed to set SIGCHLD signal handler");
633
__attribute__((nonnull, warn_unused_result))
634
bool restore_signal_handler(const struct sigaction *const
636
if(sigaction(SIGCHLD, old_sigchld_action, NULL) != 0){
637
error(0, errno, "Failed to restore signal handler");
643
__attribute__((warn_unused_result, malloc))
644
task_queue *create_queue(void){
645
task_queue *queue = malloc(sizeof(task_queue));
649
queue->allocated = 0;
655
__attribute__((nonnull, warn_unused_result))
656
bool add_to_queue(task_queue *const queue, const task_context task){
657
if((queue->length + 1) > (SIZE_MAX / sizeof(task_context))){
659
error(0, ENOMEM, "Failed to allocate %" PRIuMAX
660
" tasks for queue->tasks", (uintmax_t)(queue->length + 1));
664
const size_t needed_size = sizeof(task_context)*(queue->length + 1);
665
if(needed_size > (queue->allocated)){
666
task_context *const new_tasks = realloc(queue->tasks,
668
if(new_tasks == NULL){
669
error(0, errno, "Failed to allocate %" PRIuMAX
670
" bytes for queue->tasks", (uintmax_t)needed_size);
673
queue->tasks = new_tasks;
674
queue->allocated = needed_size;
676
/* Using memcpy here is necessary because doing */
677
/* queue->tasks[queue->length++] = task; */
678
/* would violate const-ness of task members */
679
memcpy(&(queue->tasks[queue->length++]), &task,
680
sizeof(task_context));
684
__attribute__((nonnull))
685
void cleanup_task(const task_context *const task){
686
const error_t saved_errno = errno;
687
/* free and close all task data */
688
free(task->question_filename);
689
if(task->filename != task->question_filename){
690
free(task->filename);
693
kill(task->pid, SIGTERM);
701
__attribute__((nonnull))
702
void free_queue(task_queue *const queue){
707
__attribute__((nonnull))
708
void cleanup_queue(task_queue *const *const queue){
712
for(size_t i = 0; i < (*queue)->length; i++){
713
const task_context *const task = ((*queue)->tasks)+i;
719
__attribute__((pure, nonnull, warn_unused_result))
720
bool queue_has_question(const task_queue *const queue){
721
for(size_t i=0; i < queue->length; i++){
722
if(queue->tasks[i].question_filename != NULL){
729
__attribute__((nonnull))
730
void cleanup_close(const int *const fd){
731
const error_t saved_errno = errno;
736
__attribute__((nonnull))
737
void cleanup_string(char *const *const ptr){
741
__attribute__((nonnull))
742
void cleanup_buffer(buffer *buf){
743
if(buf->allocated > 0){
744
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
745
explicit_bzero(buf->data, buf->allocated);
747
memset(buf->data, '\0', buf->allocated);
750
if(buf->data != NULL){
751
if(munlock(buf->data, buf->allocated) != 0){
752
error(0, errno, "Failed to unlock memory of old buffer");
761
__attribute__((pure, nonnull, warn_unused_result))
762
bool string_set_contains(const string_set set, const char *const str){
763
for(const char *s = set.argz; s != NULL and set.argz_len > 0;
764
s = argz_next(set.argz, set.argz_len, s)){
765
if(strcmp(s, str) == 0){
772
__attribute__((nonnull, warn_unused_result))
773
bool string_set_add(string_set *const set, const char *const str){
774
if(string_set_contains(*set, str)){
777
error_t error = argz_add(&set->argz, &set->argz_len, str);
785
__attribute__((nonnull))
786
void string_set_clear(string_set *set){
792
__attribute__((nonnull))
793
void string_set_swap(string_set *const set1, string_set *const set2){
794
/* Swap contents of two string sets */
796
char *const tmp_argz = set1->argz;
797
set1->argz = set2->argz;
798
set2->argz = tmp_argz;
801
const size_t tmp_argz_len = set1->argz_len;
802
set1->argz_len = set2->argz_len;
803
set2->argz_len = tmp_argz_len;
807
__attribute__((nonnull, warn_unused_result))
808
bool start_mandos_client(task_queue *const queue,
810
bool *const mandos_client_exited,
811
bool *const quit_now, buffer *const password,
812
bool *const password_is_read,
813
const struct sigaction *const
814
old_sigchld_action, const sigset_t sigmask,
815
const char *const helper_directory,
816
const uid_t user, const gid_t group,
817
const char *const *const argv){
819
if(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK) != 0){
820
error(0, errno, "Failed to pipe2(..., O_CLOEXEC | O_NONBLOCK)");
824
const pid_t pid = fork();
826
if(not restore_signal_handler(old_sigchld_action)){
829
if(not restore_sigmask(&sigmask)){
832
if(close(pipefds[0]) != 0){
833
error(0, errno, "Failed to close() parent pipe fd");
836
if(dup2(pipefds[1], STDOUT_FILENO) == -1){
837
error(0, errno, "Failed to dup2() pipe fd to stdout");
840
if(close(pipefds[1]) != 0){
841
error(0, errno, "Failed to close() old child pipe fd");
844
if(setenv("MANDOSPLUGINHELPERDIR", helper_directory, 1) != 0){
845
error(0, errno, "Failed to setenv(\"MANDOSPLUGINHELPERDIR\","
846
" \"%s\", 1)", helper_directory);
849
if(group != 0 and setresgid(group, 0, 0) == -1){
850
error(0, errno, "Failed to setresgid(-1, %" PRIuMAX ", %"
851
PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
854
if(user != 0 and setresuid(user, 0, 0) == -1){
855
error(0, errno, "Failed to setresuid(-1, %" PRIuMAX ", %"
856
PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
860
#pragma GCC diagnostic push
861
/* For historical reasons, the "argv" argument to execv() is not
862
const, but it is safe to override this. */
863
#pragma GCC diagnostic ignored "-Wcast-qual"
865
execv(argv[0], (char **)argv);
867
#pragma GCC diagnostic pop
869
error(0, errno, "execv(\"%s\", ...) failed", argv[0]);
875
error(0, errno, "Failed to fork()");
880
if(not add_to_queue(queue, (task_context){
881
.func=wait_for_mandos_client_exit,
883
.mandos_client_exited=mandos_client_exited,
886
error(0, errno, "Failed to add wait_for_mandos_client to queue");
891
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipefds[0],
892
&(struct epoll_event)
893
{ .events=EPOLLIN | EPOLLRDHUP });
894
if(ret != 0 and errno != EEXIST){
895
error(0, errno, "Failed to add file descriptor to epoll set");
900
return add_to_queue(queue, (task_context){
901
.func=read_mandos_client_output,
906
.password_is_read=password_is_read,
910
__attribute__((nonnull))
911
void wait_for_mandos_client_exit(const task_context task,
912
task_queue *const queue){
913
const pid_t pid = task.pid;
914
bool *const mandos_client_exited = task.mandos_client_exited;
915
bool *const quit_now = task.quit_now;
918
switch(waitpid(pid, &status, WNOHANG)){
919
case 0: /* Not exited yet */
920
if(not add_to_queue(queue, task)){
921
error(0, errno, "Failed to add myself to queue");
926
error(0, errno, "waitpid(%" PRIdMAX ") failed", (intmax_t)pid);
932
default: /* Has exited */
933
*mandos_client_exited = true;
934
if((not WIFEXITED(status))
935
or (WEXITSTATUS(status) != EXIT_SUCCESS)){
936
error(0, 0, "Mandos client failed or was killed");
942
__attribute__((nonnull))
943
void read_mandos_client_output(const task_context task,
944
task_queue *const queue){
945
buffer *const password = task.password;
946
bool *const quit_now = task.quit_now;
947
bool *const password_is_read = task.password_is_read;
948
const int fd = task.fd;
949
const int epoll_fd = task.epoll_fd;
951
const size_t new_potential_size = (password->length + PIPE_BUF);
952
if(password->allocated < new_potential_size){
953
char *const new_buffer = calloc(new_potential_size, 1);
954
if(new_buffer == NULL){
955
error(0, errno, "Failed to allocate %" PRIuMAX
956
" bytes for password", (uintmax_t)new_potential_size);
961
if(mlock(new_buffer, new_potential_size) != 0){
962
/* Warn but do not treat as fatal error */
963
if(errno != EPERM and errno != ENOMEM){
964
error(0, errno, "Failed to lock memory for password");
967
if(password->length > 0){
968
memcpy(new_buffer, password->data, password->length);
969
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
970
explicit_bzero(password->data, password->allocated);
972
memset(password->data, '\0', password->allocated);
975
if(password->data != NULL){
976
if(munlock(password->data, password->allocated) != 0){
977
error(0, errno, "Failed to unlock memory of old buffer");
979
free(password->data);
981
password->data = new_buffer;
982
password->allocated = new_potential_size;
985
const ssize_t read_length = read(fd, password->data
986
+ password->length, PIPE_BUF);
988
if(read_length == 0){ /* EOF */
989
*password_is_read = true;
993
if(read_length < 0 and errno != EAGAIN){ /* Actual error */
994
error(0, errno, "Failed to read password from Mandos client");
999
if(read_length > 0){ /* Data has been read */
1000
password->length += (size_t)read_length;
1003
/* Either data was read, or EAGAIN was indicated, meaning no data
1006
/* Re-add the fd to the epoll set */
1007
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1008
&(struct epoll_event)
1009
{ .events=EPOLLIN | EPOLLRDHUP });
1010
if(ret != 0 and errno != EEXIST){
1011
error(0, errno, "Failed to re-add file descriptor to epoll set");
1017
/* Re-add myself to the queue */
1018
if(not add_to_queue(queue, task)){
1019
error(0, errno, "Failed to add myself to queue");
1025
__attribute__((nonnull, warn_unused_result))
1026
bool add_inotify_dir_watch(task_queue *const queue,
1027
const int epoll_fd, bool *const quit_now,
1028
buffer *const password,
1029
const char *const dir,
1030
string_set *cancelled_filenames,
1031
const mono_microsecs *const current_time,
1032
bool *const mandos_client_exited,
1033
bool *const password_is_read){
1034
const int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
1036
error(0, errno, "Failed to create inotify instance");
1040
if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO
1041
| IN_MOVED_FROM| IN_DELETE | IN_EXCL_UNLINK
1044
error(0, errno, "Failed to create inotify watch on %s", dir);
1048
/* Add the inotify fd to the epoll set */
1049
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1050
&(struct epoll_event)
1051
{ .events=EPOLLIN | EPOLLRDHUP });
1052
if(ret != 0 and errno != EEXIST){
1053
error(0, errno, "Failed to add file descriptor to epoll set");
1058
const task_context read_inotify_event_task = {
1059
.func=read_inotify_event,
1064
.filename=strdup(dir),
1065
.cancelled_filenames=cancelled_filenames,
1066
.current_time=current_time,
1067
.mandos_client_exited=mandos_client_exited,
1068
.password_is_read=password_is_read,
1070
if(read_inotify_event_task.filename == NULL){
1071
error(0, errno, "Failed to strdup(\"%s\")", dir);
1076
return add_to_queue(queue, read_inotify_event_task);
1079
__attribute__((nonnull))
1080
void read_inotify_event(const task_context task,
1081
task_queue *const queue){
1082
const int fd = task.fd;
1083
const int epoll_fd = task.epoll_fd;
1084
char *const filename = task.filename;
1085
bool *quit_now = task.quit_now;
1086
buffer *const password = task.password;
1087
string_set *const cancelled_filenames = task.cancelled_filenames;
1088
const mono_microsecs *const current_time = task.current_time;
1089
bool *const mandos_client_exited = task.mandos_client_exited;
1090
bool *const password_is_read = task.password_is_read;
1092
/* "sufficient to read at least one event." - inotify(7) */
1093
const size_t ievent_size = (sizeof(struct inotify_event)
1096
struct inotify_event event;
1097
char name_buffer[NAME_MAX + 1];
1099
struct inotify_event *const ievent = &ievent_buffer.event;
1101
const ssize_t read_length = read(fd, ievent, ievent_size);
1102
if(read_length == 0){ /* EOF */
1103
error(0, 0, "Got EOF from inotify fd for directory %s", filename);
1105
cleanup_task(&task);
1108
if(read_length < 0 and errno != EAGAIN){ /* Actual error */
1109
error(0, errno, "Failed to read from inotify fd for directory %s",
1112
cleanup_task(&task);
1115
if(read_length > 0 /* Data has been read */
1116
and fnmatch("ask.*", ievent->name, FNM_FILE_NAME) == 0){
1117
char *question_filename = NULL;
1118
const ssize_t question_filename_length
1119
= asprintf(&question_filename, "%s/%s", filename, ievent->name);
1120
if(question_filename_length < 0){
1121
error(0, errno, "Failed to create file name from directory name"
1122
" %s and file name %s", filename, ievent->name);
1124
if(ievent->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)){
1125
if(not add_to_queue(queue, (task_context){
1126
.func=open_and_parse_question,
1128
.question_filename=question_filename,
1129
.filename=question_filename,
1131
.cancelled_filenames=cancelled_filenames,
1132
.current_time=current_time,
1133
.mandos_client_exited=mandos_client_exited,
1134
.password_is_read=password_is_read,
1136
error(0, errno, "Failed to add open_and_parse_question task"
1137
" for file name %s to queue", filename);
1139
/* Force the added task (open_and_parse_question) to run
1141
queue->next_run = 1;
1143
} else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
1144
if(not string_set_add(cancelled_filenames,
1145
question_filename)){
1146
error(0, errno, "Could not add question %s to"
1147
" cancelled_questions", question_filename);
1149
free(question_filename);
1150
cleanup_task(&task);
1153
free(question_filename);
1158
/* Either data was read, or EAGAIN was indicated, meaning no data
1161
/* Re-add myself to the queue */
1162
if(not add_to_queue(queue, task)){
1163
error(0, errno, "Failed to re-add read_inotify_event(%s) to"
1164
" queue", filename);
1166
cleanup_task(&task);
1170
/* Re-add the fd to the epoll set */
1171
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1172
&(struct epoll_event)
1173
{ .events=EPOLLIN | EPOLLRDHUP });
1174
if(ret != 0 and errno != EEXIST){
1175
error(0, errno, "Failed to re-add inotify file descriptor %d for"
1176
" directory %s to epoll set", fd, filename);
1177
/* Force the added task (read_inotify_event) to run again, at most
1178
one second from now */
1179
if((queue->next_run == 0)
1180
or (queue->next_run > (*current_time + 1000000))){
1181
queue->next_run = *current_time + 1000000;
1186
__attribute__((nonnull))
1187
void open_and_parse_question(const task_context task,
1188
task_queue *const queue){
1189
__attribute__((cleanup(cleanup_string)))
1190
char *question_filename = task.question_filename;
1191
const int epoll_fd = task.epoll_fd;
1192
buffer *const password = task.password;
1193
string_set *const cancelled_filenames = task.cancelled_filenames;
1194
const mono_microsecs *const current_time = task.current_time;
1195
bool *const mandos_client_exited = task.mandos_client_exited;
1196
bool *const password_is_read = task.password_is_read;
1198
/* We use the GLib "Key-value file parser" functions to parse the
1199
question file. See <https://systemd.io/PASSWORD_AGENTS/> for
1200
specification of contents */
1201
__attribute__((nonnull))
1202
void cleanup_g_key_file(GKeyFile **key_file){
1203
if(*key_file != NULL){
1204
g_key_file_free(*key_file);
1208
__attribute__((cleanup(cleanup_g_key_file)))
1209
GKeyFile *key_file = g_key_file_new();
1210
if(key_file == NULL){
1211
error(0, errno, "Failed g_key_file_new() for \"%s\"",
1215
GError *glib_error = NULL;
1216
if(g_key_file_load_from_file(key_file, question_filename,
1217
G_KEY_FILE_NONE, &glib_error) != TRUE){
1218
/* If a file was removed, we should ignore it, so */
1219
/* only show error message if file actually existed */
1220
if(glib_error->code != G_FILE_ERROR_NOENT){
1221
error(0, 0, "Failed to load question data from file \"%s\": %s",
1222
question_filename, glib_error->message);
1227
__attribute__((cleanup(cleanup_string)))
1228
char *socket_name = g_key_file_get_string(key_file, "Ask",
1231
if(socket_name == NULL){
1232
error(0, 0, "Question file \"%s\" did not contain \"Socket\": %s",
1233
question_filename, glib_error->message);
1237
if(strlen(socket_name) == 0){
1238
error(0, 0, "Question file \"%s\" had empty \"Socket\" value",
1243
const guint64 pid = g_key_file_get_uint64(key_file, "Ask", "PID",
1245
if(glib_error != NULL){
1246
error(0, 0, "Question file \"%s\" contained bad \"PID\": %s",
1247
question_filename, glib_error->message);
1251
if((pid != (guint64)((pid_t)pid))
1252
or (kill((pid_t)pid, 0) != 0)){
1253
error(0, 0, "PID %" PRIuMAX " in question file \"%s\" is bad or"
1254
" does not exist", (uintmax_t)pid, question_filename);
1258
guint64 notafter = g_key_file_get_uint64(key_file, "Ask",
1259
"NotAfter", &glib_error);
1260
if(glib_error != NULL){
1261
if(glib_error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND){
1262
error(0, 0, "Question file \"%s\" contained bad \"NotAfter\":"
1263
" %s", question_filename, glib_error->message);
1268
if(queue->next_run == 0 or (queue->next_run > notafter)){
1269
queue->next_run = notafter;
1271
if(*current_time >= notafter){
1276
const task_context connect_question_socket_task = {
1277
.func=connect_question_socket,
1278
.question_filename=strdup(question_filename),
1281
.filename=strdup(socket_name),
1282
.cancelled_filenames=task.cancelled_filenames,
1283
.mandos_client_exited=mandos_client_exited,
1284
.password_is_read=password_is_read,
1285
.current_time=current_time,
1287
if(connect_question_socket_task.question_filename == NULL
1288
or connect_question_socket_task.filename == NULL
1289
or not add_to_queue(queue, connect_question_socket_task)){
1290
error(0, errno, "Failed to add connect_question_socket for socket"
1291
" %s (from \"%s\") to queue", socket_name,
1293
cleanup_task(&connect_question_socket_task);
1296
/* Force the added task (connect_question_socket) to run
1298
queue->next_run = 1;
1301
char *const dup_filename = strdup(question_filename);
1302
const task_context cancel_old_question_task = {
1303
.func=cancel_old_question,
1304
.question_filename=dup_filename,
1306
.filename=dup_filename,
1307
.cancelled_filenames=cancelled_filenames,
1308
.current_time=current_time,
1310
if(cancel_old_question_task.question_filename == NULL
1311
or not add_to_queue(queue, cancel_old_question_task)){
1312
error(0, errno, "Failed to add cancel_old_question for file "
1313
"\"%s\" to queue", question_filename);
1314
cleanup_task(&cancel_old_question_task);
1320
__attribute__((nonnull))
1321
void cancel_old_question(const task_context task,
1322
task_queue *const queue){
1323
char *const question_filename = task.question_filename;
1324
string_set *const cancelled_filenames = task.cancelled_filenames;
1325
const mono_microsecs notafter = task.notafter;
1326
const mono_microsecs *const current_time = task.current_time;
1328
if(*current_time >= notafter){
1329
if(not string_set_add(cancelled_filenames, question_filename)){
1330
error(0, errno, "Failed to cancel question for file %s",
1333
cleanup_task(&task);
1337
if(not add_to_queue(queue, task)){
1338
error(0, errno, "Failed to add cancel_old_question for file "
1339
"%s to queue", question_filename);
1340
cleanup_task(&task);
1344
if((queue->next_run == 0) or (queue->next_run > notafter)){
1345
queue->next_run = notafter;
1349
__attribute__((nonnull))
1350
void connect_question_socket(const task_context task,
1351
task_queue *const queue){
1352
char *const question_filename = task.question_filename;
1353
char *const filename = task.filename;
1354
const int epoll_fd = task.epoll_fd;
1355
buffer *const password = task.password;
1356
string_set *const cancelled_filenames = task.cancelled_filenames;
1357
bool *const mandos_client_exited = task.mandos_client_exited;
1358
bool *const password_is_read = task.password_is_read;
1359
const mono_microsecs *const current_time = task.current_time;
1361
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
1363
if(sizeof(sock_name.sun_path) <= strlen(filename)){
1364
error(0, 0, "Socket filename is larger than"
1365
" sizeof(sockaddr_un.sun_path); %" PRIuMAX ": \"%s\"",
1366
(uintmax_t)sizeof(sock_name.sun_path), filename);
1367
if(not string_set_add(cancelled_filenames, question_filename)){
1368
error(0, errno, "Failed to cancel question for file %s",
1371
cleanup_task(&task);
1375
const int fd = socket(PF_LOCAL, SOCK_DGRAM
1376
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
1379
"Failed to create socket(PF_LOCAL, SOCK_DGRAM, 0)");
1380
if(not add_to_queue(queue, task)){
1381
error(0, errno, "Failed to add connect_question_socket for file"
1382
" \"%s\" and socket \"%s\" to queue", question_filename,
1384
cleanup_task(&task);
1386
/* Force the added task (connect_question_socket) to run
1388
queue->next_run = 1;
1393
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
1394
if(connect(fd, (struct sockaddr *)&sock_name,
1395
(socklen_t)SUN_LEN(&sock_name)) != 0){
1396
error(0, errno, "Failed to connect socket to \"%s\"", filename);
1397
if(not add_to_queue(queue, task)){
1398
error(0, errno, "Failed to add connect_question_socket for file"
1399
" \"%s\" and socket \"%s\" to queue", question_filename,
1401
cleanup_task(&task);
1403
/* Force the added task (connect_question_socket) to run again,
1404
at most one second from now */
1405
if((queue->next_run == 0)
1406
or (queue->next_run > (*current_time + 1000000))){
1407
queue->next_run = *current_time + 1000000;
1413
/* Not necessary, but we can try, and merely warn on failure */
1414
if(shutdown(fd, SHUT_RD) != 0){
1415
error(0, errno, "Failed to shutdown reading from socket \"%s\"",
1419
/* Add the fd to the epoll set */
1420
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1421
&(struct epoll_event){ .events=EPOLLOUT })
1423
error(0, errno, "Failed to add inotify file descriptor %d for"
1424
" socket %s to epoll set", fd, filename);
1425
if(not add_to_queue(queue, task)){
1426
error(0, errno, "Failed to add connect_question_socket for file"
1427
" \"%s\" and socket \"%s\" to queue", question_filename,
1429
cleanup_task(&task);
1431
/* Force the added task (connect_question_socket) to run again,
1432
at most one second from now */
1433
if((queue->next_run == 0)
1434
or (queue->next_run > (*current_time + 1000000))){
1435
queue->next_run = *current_time + 1000000;
1441
/* add task send_password_to_socket to queue */
1442
const task_context send_password_to_socket_task = {
1443
.func=send_password_to_socket,
1444
.question_filename=question_filename,
1449
.cancelled_filenames=cancelled_filenames,
1450
.mandos_client_exited=mandos_client_exited,
1451
.password_is_read=password_is_read,
1452
.current_time=current_time,
1455
if(not add_to_queue(queue, send_password_to_socket_task)){
1456
error(0, errno, "Failed to add send_password_to_socket for"
1457
" file \"%s\" and socket \"%s\" to queue",
1458
question_filename, filename);
1459
cleanup_task(&send_password_to_socket_task);
1463
__attribute__((nonnull))
1464
void send_password_to_socket(const task_context task,
1465
task_queue *const queue){
1466
char *const question_filename=task.question_filename;
1467
char *const filename=task.filename;
1468
const int epoll_fd=task.epoll_fd;
1469
const int fd=task.fd;
1470
buffer *const password=task.password;
1471
string_set *const cancelled_filenames=task.cancelled_filenames;
1472
bool *const mandos_client_exited = task.mandos_client_exited;
1473
bool *const password_is_read = task.password_is_read;
1474
const mono_microsecs *const current_time = task.current_time;
1476
if(*mandos_client_exited and *password_is_read){
1478
const size_t send_buffer_length = password->length + 2;
1479
char *send_buffer = malloc(send_buffer_length);
1480
if(send_buffer == NULL){
1481
error(0, errno, "Failed to allocate send_buffer");
1483
if(mlock(send_buffer, send_buffer_length) != 0){
1484
/* Warn but do not treat as fatal error */
1485
if(errno != EPERM and errno != ENOMEM){
1486
error(0, errno, "Failed to lock memory for password"
1490
/* “[…] send a single datagram to the socket consisting of the
1491
password string either prefixed with "+" or with "-"
1492
depending on whether the password entry was successful or
1493
not. You may but don't have to include a final NUL byte in
1496
— <https://systemd.io/PASSWORD_AGENTS/> (Tue, 15 Sep 2020
1499
send_buffer[0] = '+'; /* Prefix with "+" */
1500
/* Always add an extra NUL */
1501
send_buffer[password->length + 1] = '\0';
1502
if(password->length > 0){
1503
memcpy(send_buffer + 1, password->data, password->length);
1506
ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1508
const error_t saved_errno = (ssret < 0) ? errno : 0;
1509
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1510
explicit_bzero(send_buffer, send_buffer_length);
1512
memset(send_buffer, '\0', send_buffer_length);
1514
if(munlock(send_buffer, send_buffer_length) != 0){
1515
error(0, errno, "Failed to unlock memory of send buffer");
1518
if(ssret < 0 or ssret < (ssize_t)send_buffer_length){
1519
switch(saved_errno){
1532
error(0, saved_errno, "Password of size %" PRIuMAX
1533
" is too big", (uintmax_t)password->length);
1537
__attribute__((fallthrough));
1540
if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1541
error(0, 0, "Password only partially sent to socket %s: %"
1542
PRIuMAX " out of %" PRIuMAX " bytes sent", filename,
1543
(uintmax_t)ssret, (uintmax_t)send_buffer_length);
1548
__attribute__((fallthrough));
1551
error(0, saved_errno, "Failed to send() to socket %s",
1553
if(not string_set_add(cancelled_filenames,
1554
question_filename)){
1555
error(0, errno, "Failed to cancel question for file %s",
1558
cleanup_task(&task);
1563
cleanup_task(&task);
1569
/* We failed or are not ready yet; retry later */
1571
if(not add_to_queue(queue, task)){
1572
error(0, errno, "Failed to add send_password_to_socket for"
1573
" file %s and socket %s to queue", question_filename,
1575
cleanup_task(&task);
1578
/* Add the fd to the epoll set */
1579
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1580
&(struct epoll_event){ .events=EPOLLOUT })
1582
error(0, errno, "Failed to add socket file descriptor %d for"
1583
" socket %s to epoll set", fd, filename);
1584
/* Force the added task (send_password_to_socket) to run again, at
1585
most one second from now */
1586
if((queue->next_run == 0)
1587
or (queue->next_run > (*current_time + 1000000))){
1588
queue->next_run = *current_time + 1000000;
1593
__attribute__((warn_unused_result))
1594
bool add_existing_questions(task_queue *const queue,
1596
buffer *const password,
1597
string_set *cancelled_filenames,
1598
const mono_microsecs *const current_time,
1599
bool *const mandos_client_exited,
1600
bool *const password_is_read,
1601
const char *const dirname){
1602
__attribute__((cleanup(cleanup_string)))
1603
char *dir_pattern = NULL;
1604
const int ret = asprintf(&dir_pattern, "%s/ask.*", dirname);
1605
if(ret < 0 or dir_pattern == NULL){
1606
error(0, errno, "Could not create glob pattern for directory %s",
1610
__attribute__((cleanup(globfree)))
1611
glob_t question_filenames = {};
1612
switch(glob(dir_pattern, GLOB_ERR | GLOB_NOSORT | GLOB_MARK,
1613
NULL, &question_filenames)){
1616
error(0, errno, "Failed to open directory %s", dirname);
1619
error(0, errno, "There are no question files in %s", dirname);
1622
error(0, errno, "Could not allocate memory for question file"
1623
" names in %s", dirname);
1627
__attribute__((fallthrough));
1630
for(size_t i = 0; i < question_filenames.gl_pathc; i++){
1631
char *const question_filename = strdup(question_filenames
1633
const task_context task = {
1634
.func=open_and_parse_question,
1636
.question_filename=question_filename,
1637
.filename=question_filename,
1639
.cancelled_filenames=cancelled_filenames,
1640
.current_time=current_time,
1641
.mandos_client_exited=mandos_client_exited,
1642
.password_is_read=password_is_read,
1645
if(question_filename == NULL
1646
or not add_to_queue(queue, task)){
1647
error(0, errno, "Failed to add open_and_parse_question for"
1648
" file %s to queue",
1649
question_filenames.gl_pathv[i]);
1650
free(question_filename);
1652
queue->next_run = 1;
1659
__attribute__((nonnull, warn_unused_result))
1660
bool wait_for_event(const int epoll_fd,
1661
const mono_microsecs queue_next_run,
1662
const mono_microsecs current_time){
1663
__attribute__((const))
1664
int milliseconds_to_wait(const mono_microsecs currtime,
1665
const mono_microsecs nextrun){
1666
if(currtime >= nextrun){
1669
const uintmax_t wait_time_ms = (nextrun - currtime) / 1000;
1670
if(wait_time_ms > (uintmax_t)INT_MAX){
1673
return (int)wait_time_ms;
1676
const int wait_time_ms = milliseconds_to_wait(current_time,
1679
/* Prepare unblocking of SIGCHLD during epoll_pwait */
1680
sigset_t temporary_unblocked_sigmask;
1681
/* Get current signal mask */
1682
if(pthread_sigmask(-1, NULL, &temporary_unblocked_sigmask) != 0){
1685
/* Remove SIGCHLD from the signal mask */
1686
if(sigdelset(&temporary_unblocked_sigmask, SIGCHLD) != 0){
1689
struct epoll_event events[8]; /* Ignored */
1690
int ret = epoll_pwait(epoll_fd, events,
1691
sizeof(events) / sizeof(struct epoll_event),
1692
queue_next_run == 0 ? -1 : (int)wait_time_ms,
1693
&temporary_unblocked_sigmask);
1694
if(ret < 0 and errno != EINTR){
1695
error(0, errno, "Failed epoll_pwait(epfd=%d, ..., timeout=%d,"
1697
queue_next_run == 0 ? -1 : (int)wait_time_ms);
1700
return clear_all_fds_from_epoll_set(epoll_fd);
1703
bool clear_all_fds_from_epoll_set(const int epoll_fd){
1704
/* Create a new empty epoll set */
1705
__attribute__((cleanup(cleanup_close)))
1706
const int new_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
1707
if(new_epoll_fd < 0){
1710
/* dup3() the new epoll set fd over the old one, replacing it */
1711
if(dup3(new_epoll_fd, epoll_fd, O_CLOEXEC) < 0){
1717
__attribute__((nonnull, warn_unused_result))
1718
bool run_queue(task_queue **const queue,
1719
string_set *const cancelled_filenames,
1720
bool *const quit_now){
1722
task_queue *new_queue = create_queue();
1723
if(new_queue == NULL){
1727
__attribute__((cleanup(string_set_clear)))
1728
string_set old_cancelled_filenames = {};
1729
string_set_swap(cancelled_filenames, &old_cancelled_filenames);
1731
/* Declare i outside the for loop, since we might need i after the
1732
loop in case we aborted in the middle */
1734
for(i=0; i < (*queue)->length and not *quit_now; i++){
1735
task_context *const task = &((*queue)->tasks[i]);
1736
const char *const question_filename = task->question_filename;
1737
/* Skip any task referencing a cancelled question filename */
1738
if(question_filename != NULL
1739
and string_set_contains(old_cancelled_filenames,
1740
question_filename)){
1744
task->func(*task, new_queue);
1748
/* we might be in the middle of the queue, so clean up any
1749
remaining tasks in the current queue */
1750
for(; i < (*queue)->length; i++){
1751
cleanup_task(&((*queue)->tasks[i]));
1765
/* End of regular code section */
1767
/* Start of tests section; here are the tests for the above code */
1769
/* This "fixture" data structure is used by the test setup and
1770
teardown functions */
1772
struct sigaction orig_sigaction;
1773
sigset_t orig_sigmask;
1776
static void test_setup(test_fixture *fixture,
1777
__attribute__((unused))
1778
gconstpointer user_data){
1779
g_assert_true(setup_signal_handler(&fixture->orig_sigaction));
1780
g_assert_true(block_sigchld(&fixture->orig_sigmask));
1783
static void test_teardown(test_fixture *fixture,
1784
__attribute__((unused))
1785
gconstpointer user_data){
1786
g_assert_true(restore_signal_handler(&fixture->orig_sigaction));
1787
g_assert_true(restore_sigmask(&fixture->orig_sigmask));
1790
/* Utility function used by tests to search queue for matching task */
1791
__attribute__((pure, nonnull, warn_unused_result))
1792
static task_context *find_matching_task(const task_queue *const queue,
1793
const task_context task){
1794
/* The argument "task" structure is a pattern to match; 0 in any
1795
member means any value matches, otherwise the value must match.
1796
The filename strings are compared by strcmp(), not by pointer. */
1797
for(size_t i = 0; i < queue->length; i++){
1798
task_context *const current_task = queue->tasks+i;
1799
/* Check all members of task_context, if set to a non-zero value.
1800
If a member does not match, continue to next task in queue */
1802
/* task_func *const func */
1803
if(task.func != NULL and current_task->func != task.func){
1806
/* char *const question_filename; */
1807
if(task.question_filename != NULL
1808
and (current_task->question_filename == NULL
1809
or strcmp(current_task->question_filename,
1810
task.question_filename) != 0)){
1813
/* const pid_t pid; */
1814
if(task.pid != 0 and current_task->pid != task.pid){
1817
/* const int epoll_fd; */
1818
if(task.epoll_fd != 0
1819
and current_task->epoll_fd != task.epoll_fd){
1822
/* bool *const quit_now; */
1823
if(task.quit_now != NULL
1824
and current_task->quit_now != task.quit_now){
1828
if(task.fd != 0 and current_task->fd != task.fd){
1831
/* bool *const mandos_client_exited; */
1832
if(task.mandos_client_exited != NULL
1833
and current_task->mandos_client_exited
1834
!= task.mandos_client_exited){
1837
/* buffer *const password; */
1838
if(task.password != NULL
1839
and current_task->password != task.password){
1842
/* bool *const password_is_read; */
1843
if(task.password_is_read != NULL
1844
and current_task->password_is_read != task.password_is_read){
1847
/* char *filename; */
1848
if(task.filename != NULL
1849
and (current_task->filename == NULL
1850
or strcmp(current_task->filename, task.filename) != 0)){
1853
/* string_set *const cancelled_filenames; */
1854
if(task.cancelled_filenames != NULL
1855
and current_task->cancelled_filenames
1856
!= task.cancelled_filenames){
1859
/* const mono_microsecs notafter; */
1860
if(task.notafter != 0
1861
and current_task->notafter != task.notafter){
1864
/* const mono_microsecs *const current_time; */
1865
if(task.current_time != NULL
1866
and current_task->current_time != task.current_time){
1869
/* Current task matches all members; return it */
1870
return current_task;
1872
/* No task in queue matches passed pattern task */
1876
static void test_create_queue(__attribute__((unused))
1877
test_fixture *fixture,
1878
__attribute__((unused))
1879
gconstpointer user_data){
1880
__attribute__((cleanup(cleanup_queue)))
1881
task_queue *const queue = create_queue();
1882
g_assert_nonnull(queue);
1883
g_assert_null(queue->tasks);
1884
g_assert_true(queue->length == 0);
1885
g_assert_true(queue->next_run == 0);
1888
static task_func dummy_func;
1890
static void test_add_to_queue(__attribute__((unused))
1891
test_fixture *fixture,
1892
__attribute__((unused))
1893
gconstpointer user_data){
1894
__attribute__((cleanup(cleanup_queue)))
1895
task_queue *queue = create_queue();
1896
g_assert_nonnull(queue);
1898
g_assert_true(add_to_queue(queue,
1899
(task_context){ .func=dummy_func }));
1900
g_assert_true(queue->length == 1);
1901
g_assert_nonnull(queue->tasks);
1902
g_assert_true(queue->tasks[0].func == dummy_func);
1905
static void test_add_to_queue_overflow(__attribute__((unused))
1906
test_fixture *fixture,
1907
__attribute__((unused))
1908
gconstpointer user_data){
1909
__attribute__((cleanup(cleanup_queue)))
1910
task_queue *queue = create_queue();
1911
g_assert_nonnull(queue);
1912
g_assert_true(queue->length == 0);
1913
queue->length = SIZE_MAX / sizeof(task_context); /* fake max size */
1915
FILE *real_stderr = stderr;
1916
FILE *devnull = fopen("/dev/null", "we");
1917
g_assert_nonnull(devnull);
1919
const bool ret = add_to_queue(queue,
1920
(task_context){ .func=dummy_func });
1921
g_assert_true(errno == ENOMEM);
1922
g_assert_false(ret);
1923
stderr = real_stderr;
1924
g_assert_cmpint(fclose(devnull), ==, 0);
1925
queue->length = 0; /* Restore real size */
1928
static void dummy_func(__attribute__((unused))
1929
const task_context task,
1930
__attribute__((unused))
1931
task_queue *const queue){
1934
static void test_queue_has_question_empty(__attribute__((unused))
1935
test_fixture *fixture,
1936
__attribute__((unused))
1937
gconstpointer user_data){
1938
__attribute__((cleanup(cleanup_queue)))
1939
task_queue *queue = create_queue();
1940
g_assert_nonnull(queue);
1941
g_assert_false(queue_has_question(queue));
1944
static void test_queue_has_question_false(__attribute__((unused))
1945
test_fixture *fixture,
1946
__attribute__((unused))
1947
gconstpointer user_data){
1948
__attribute__((cleanup(cleanup_queue)))
1949
task_queue *queue = create_queue();
1950
g_assert_nonnull(queue);
1951
g_assert_true(add_to_queue(queue,
1952
(task_context){ .func=dummy_func }));
1953
g_assert_false(queue_has_question(queue));
1956
static void test_queue_has_question_true(__attribute__((unused))
1957
test_fixture *fixture,
1958
__attribute__((unused))
1959
gconstpointer user_data){
1960
__attribute__((cleanup(cleanup_queue)))
1961
task_queue *queue = create_queue();
1962
g_assert_nonnull(queue);
1963
char *const question_filename
1964
= strdup("/nonexistent/question_filename");
1965
g_assert_nonnull(question_filename);
1966
task_context task = {
1968
.question_filename=question_filename,
1970
g_assert_true(add_to_queue(queue, task));
1971
g_assert_true(queue_has_question(queue));
1974
static void test_queue_has_question_false2(__attribute__((unused))
1975
test_fixture *fixture,
1976
__attribute__((unused))
1977
gconstpointer user_data){
1978
__attribute__((cleanup(cleanup_queue)))
1979
task_queue *queue = create_queue();
1980
g_assert_nonnull(queue);
1981
task_context task = { .func=dummy_func };
1982
g_assert_true(add_to_queue(queue, task));
1983
g_assert_true(add_to_queue(queue, task));
1984
g_assert_cmpint((int)queue->length, ==, 2);
1985
g_assert_false(queue_has_question(queue));
1988
static void test_queue_has_question_true2(__attribute__((unused))
1989
test_fixture *fixture,
1990
__attribute__((unused))
1991
gconstpointer user_data){
1992
__attribute__((cleanup(cleanup_queue)))
1993
task_queue *queue = create_queue();
1994
g_assert_nonnull(queue);
1995
task_context task1 = { .func=dummy_func };
1996
g_assert_true(add_to_queue(queue, task1));
1997
char *const question_filename
1998
= strdup("/nonexistent/question_filename");
1999
g_assert_nonnull(question_filename);
2000
task_context task2 = {
2002
.question_filename=question_filename,
2004
g_assert_true(add_to_queue(queue, task2));
2005
g_assert_cmpint((int)queue->length, ==, 2);
2006
g_assert_true(queue_has_question(queue));
2009
static void test_cleanup_buffer(__attribute__((unused))
2010
test_fixture *fixture,
2011
__attribute__((unused))
2012
gconstpointer user_data){
2015
const size_t buffersize = 10;
2017
buf.data = malloc(buffersize);
2018
g_assert_nonnull(buf.data);
2019
if(mlock(buf.data, buffersize) != 0){
2020
g_assert_true(errno == EPERM or errno == ENOMEM);
2023
cleanup_buffer(&buf);
2024
g_assert_null(buf.data);
2028
void test_string_set_new_set_contains_nothing(__attribute__((unused))
2029
test_fixture *fixture,
2030
__attribute__((unused))
2033
__attribute__((cleanup(string_set_clear)))
2034
string_set set = {};
2035
g_assert_false(string_set_contains(set, "")); /* Empty string */
2036
g_assert_false(string_set_contains(set, "test_string"));
2040
test_string_set_with_added_string_contains_it(__attribute__((unused))
2041
test_fixture *fixture,
2042
__attribute__((unused))
2045
__attribute__((cleanup(string_set_clear)))
2046
string_set set = {};
2047
g_assert_true(string_set_add(&set, "test_string"));
2048
g_assert_true(string_set_contains(set, "test_string"));
2052
test_string_set_cleared_does_not_contain_str(__attribute__((unused))
2053
test_fixture *fixture,
2054
__attribute__((unused))
2055
gconstpointer user_data){
2056
__attribute__((cleanup(string_set_clear)))
2057
string_set set = {};
2058
g_assert_true(string_set_add(&set, "test_string"));
2059
string_set_clear(&set);
2060
g_assert_false(string_set_contains(set, "test_string"));
2064
void test_string_set_swap_one_with_empty(__attribute__((unused))
2065
test_fixture *fixture,
2066
__attribute__((unused))
2067
gconstpointer user_data){
2068
__attribute__((cleanup(string_set_clear)))
2069
string_set set1 = {};
2070
__attribute__((cleanup(string_set_clear)))
2071
string_set set2 = {};
2072
g_assert_true(string_set_add(&set1, "test_string1"));
2073
string_set_swap(&set1, &set2);
2074
g_assert_false(string_set_contains(set1, "test_string1"));
2075
g_assert_true(string_set_contains(set2, "test_string1"));
2079
void test_string_set_swap_empty_with_one(__attribute__((unused))
2080
test_fixture *fixture,
2081
__attribute__((unused))
2082
gconstpointer user_data){
2083
__attribute__((cleanup(string_set_clear)))
2084
string_set set1 = {};
2085
__attribute__((cleanup(string_set_clear)))
2086
string_set set2 = {};
2087
g_assert_true(string_set_add(&set2, "test_string2"));
2088
string_set_swap(&set1, &set2);
2089
g_assert_true(string_set_contains(set1, "test_string2"));
2090
g_assert_false(string_set_contains(set2, "test_string2"));
2093
static void test_string_set_swap_one_with_one(__attribute__((unused))
2094
test_fixture *fixture,
2095
__attribute__((unused))
2098
__attribute__((cleanup(string_set_clear)))
2099
string_set set1 = {};
2100
__attribute__((cleanup(string_set_clear)))
2101
string_set set2 = {};
2102
g_assert_true(string_set_add(&set1, "test_string1"));
2103
g_assert_true(string_set_add(&set2, "test_string2"));
2104
string_set_swap(&set1, &set2);
2105
g_assert_false(string_set_contains(set1, "test_string1"));
2106
g_assert_true(string_set_contains(set1, "test_string2"));
2107
g_assert_false(string_set_contains(set2, "test_string2"));
2108
g_assert_true(string_set_contains(set2, "test_string1"));
2111
static bool fd_has_cloexec_and_nonblock(const int);
2113
static bool epoll_set_contains(int, int, uint32_t);
2115
static void test_start_mandos_client(test_fixture *fixture,
2116
__attribute__((unused))
2117
gconstpointer user_data){
2119
bool mandos_client_exited = false;
2120
bool quit_now = false;
2121
__attribute__((cleanup(cleanup_close)))
2122
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2123
g_assert_cmpint(epoll_fd, >=, 0);
2124
__attribute__((cleanup(cleanup_queue)))
2125
task_queue *queue = create_queue();
2126
g_assert_nonnull(queue);
2127
buffer password = {};
2128
bool password_is_read = false;
2129
const char helper_directory[] = "/nonexistent";
2130
const char *const argv[] = { "/bin/true", NULL };
2132
g_assert_true(start_mandos_client(queue, epoll_fd,
2133
&mandos_client_exited, &quit_now,
2134
&password, &password_is_read,
2135
&fixture->orig_sigaction,
2136
fixture->orig_sigmask,
2137
helper_directory, 0, 0, argv));
2139
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
2141
const task_context *const added_wait_task
2142
= find_matching_task(queue, (task_context){
2143
.func=wait_for_mandos_client_exit,
2144
.mandos_client_exited=&mandos_client_exited,
2145
.quit_now=&quit_now,
2147
g_assert_nonnull(added_wait_task);
2148
g_assert_cmpint(added_wait_task->pid, >, 0);
2149
g_assert_cmpint(kill(added_wait_task->pid, SIGKILL), ==, 0);
2150
waitpid(added_wait_task->pid, NULL, 0);
2152
const task_context *const added_read_task
2153
= find_matching_task(queue, (task_context){
2154
.func=read_mandos_client_output,
2156
.password=&password,
2157
.password_is_read=&password_is_read,
2158
.quit_now=&quit_now,
2160
g_assert_nonnull(added_read_task);
2161
g_assert_cmpint(added_read_task->fd, >, 2);
2162
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
2163
g_assert_true(epoll_set_contains(epoll_fd, added_read_task->fd,
2164
EPOLLIN | EPOLLRDHUP));
2167
static bool fd_has_cloexec_and_nonblock(const int fd){
2168
const int socket_fd_flags = fcntl(fd, F_GETFD, 0);
2169
const int socket_file_flags = fcntl(fd, F_GETFL, 0);
2170
return ((socket_fd_flags >= 0)
2171
and (socket_fd_flags & FD_CLOEXEC)
2172
and (socket_file_flags >= 0)
2173
and (socket_file_flags & O_NONBLOCK));
2176
__attribute__((const))
2177
bool is_privileged(void){
2178
uid_t user = getuid() + 1;
2179
if(user == 0){ /* Overflow check */
2182
gid_t group = getuid() + 1;
2183
if(group == 0){ /* Overflow check */
2186
const pid_t pid = fork();
2187
if(pid == 0){ /* Child */
2188
if(setresgid((uid_t)-1, group, group) == -1){
2190
error(EXIT_FAILURE, errno, "Failed to setresgid(-1, %" PRIuMAX
2191
", %" PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
2195
if(setresuid((uid_t)-1, user, user) == -1){
2197
error(EXIT_FAILURE, errno, "Failed to setresuid(-1, %" PRIuMAX
2198
", %" PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
2205
error(EXIT_FAILURE, errno, "Failed to fork()");
2209
waitpid(pid, &status, 0);
2210
if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2216
static bool epoll_set_contains(int epoll_fd, int fd, uint32_t events){
2217
/* Only scan for events in this eventmask */
2218
const uint32_t eventmask = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
2219
__attribute__((cleanup(cleanup_string)))
2220
char *fdinfo_name = NULL;
2221
int ret = asprintf(&fdinfo_name, "/proc/self/fdinfo/%d", epoll_fd);
2222
g_assert_cmpint(ret, >, 0);
2223
g_assert_nonnull(fdinfo_name);
2225
FILE *fdinfo = fopen(fdinfo_name, "r");
2226
g_assert_nonnull(fdinfo);
2227
uint32_t reported_events;
2232
if(getline(&line.data, &line.allocated, fdinfo) < 0){
2235
/* See proc(5) for format of /proc/PID/fdinfo/FD for epoll fd's */
2236
if(sscanf(line.data, "tfd: %d events: %" SCNx32 " ",
2237
&found_fd, &reported_events) == 2){
2242
} while(not feof(fdinfo) and not ferror(fdinfo));
2243
g_assert_cmpint(fclose(fdinfo), ==, 0);
2250
/* Don't check events if none are given */
2253
return (reported_events & eventmask) == (events & eventmask);
2256
static void test_start_mandos_client_execv(test_fixture *fixture,
2257
__attribute__((unused))
2258
gconstpointer user_data){
2259
bool mandos_client_exited = false;
2260
bool quit_now = false;
2261
__attribute__((cleanup(cleanup_close)))
2262
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2263
g_assert_cmpint(epoll_fd, >=, 0);
2264
__attribute__((cleanup(cleanup_queue)))
2265
task_queue *queue = create_queue();
2266
g_assert_nonnull(queue);
2267
__attribute__((cleanup(cleanup_buffer)))
2268
buffer password = {};
2269
const char helper_directory[] = "/nonexistent";
2270
/* Can't execv("/", ...), so this should fail */
2271
const char *const argv[] = { "/", NULL };
2274
__attribute__((cleanup(cleanup_close)))
2275
const int devnull_fd = open("/dev/null",
2276
O_WRONLY | O_CLOEXEC | O_NOCTTY);
2277
g_assert_cmpint(devnull_fd, >=, 0);
2278
__attribute__((cleanup(cleanup_close)))
2279
const int real_stderr_fd = dup(STDERR_FILENO);
2280
g_assert_cmpint(real_stderr_fd, >=, 0);
2281
dup2(devnull_fd, STDERR_FILENO);
2283
const bool success = start_mandos_client(queue, epoll_fd,
2284
&mandos_client_exited,
2288
&fixture->orig_sigaction,
2289
fixture->orig_sigmask,
2290
helper_directory, 0, 0,
2292
dup2(real_stderr_fd, STDERR_FILENO);
2293
g_assert_true(success);
2295
g_assert_cmpuint((unsigned int)queue->length, ==, 2);
2297
struct timespec starttime, currtime;
2298
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2300
queue->next_run = 0;
2301
string_set cancelled_filenames = {};
2304
__attribute__((cleanup(cleanup_close)))
2305
const int devnull_fd = open("/dev/null",
2306
O_WRONLY | O_CLOEXEC | O_NOCTTY);
2307
g_assert_cmpint(devnull_fd, >=, 0);
2308
__attribute__((cleanup(cleanup_close)))
2309
const int real_stderr_fd = dup(STDERR_FILENO);
2310
g_assert_cmpint(real_stderr_fd, >=, 0);
2311
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2312
dup2(devnull_fd, STDERR_FILENO);
2313
const bool success = run_queue(&queue, &cancelled_filenames,
2315
dup2(real_stderr_fd, STDERR_FILENO);
2320
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2321
} while(((queue->length) > 0)
2323
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2325
g_assert_true(quit_now);
2326
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2327
g_assert_true(mandos_client_exited);
2330
static void test_start_mandos_client_suid_euid(test_fixture *fixture,
2331
__attribute__((unused))
2334
if(not is_privileged()){
2335
g_test_skip("Not privileged");
2339
bool mandos_client_exited = false;
2340
bool quit_now = false;
2341
__attribute__((cleanup(cleanup_close)))
2342
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2343
g_assert_cmpint(epoll_fd, >=, 0);
2344
__attribute__((cleanup(cleanup_queue)))
2345
task_queue *queue = create_queue();
2346
g_assert_nonnull(queue);
2347
__attribute__((cleanup(cleanup_buffer)))
2348
buffer password = {};
2349
bool password_is_read = false;
2350
const char helper_directory[] = "/nonexistent";
2351
const char *const argv[] = { "/usr/bin/id", "--user", NULL };
2355
const bool success = start_mandos_client(queue, epoll_fd,
2356
&mandos_client_exited,
2357
&quit_now, &password,
2359
&fixture->orig_sigaction,
2360
fixture->orig_sigmask,
2361
helper_directory, user,
2363
g_assert_true(success);
2364
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2366
struct timespec starttime, currtime;
2367
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2369
queue->next_run = 0;
2370
string_set cancelled_filenames = {};
2371
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2372
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2373
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2374
} while(((queue->length) > 0)
2376
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2378
g_assert_false(quit_now);
2379
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2380
g_assert_true(mandos_client_exited);
2382
g_assert_true(password_is_read);
2383
g_assert_nonnull(password.data);
2386
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2388
g_assert_true((uid_t)id == id);
2390
g_assert_cmpuint((unsigned int)id, ==, 0);
2393
static void test_start_mandos_client_suid_egid(test_fixture *fixture,
2394
__attribute__((unused))
2397
if(not is_privileged()){
2398
g_test_skip("Not privileged");
2402
bool mandos_client_exited = false;
2403
bool quit_now = false;
2404
__attribute__((cleanup(cleanup_close)))
2405
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2406
g_assert_cmpint(epoll_fd, >=, 0);
2407
__attribute__((cleanup(cleanup_queue)))
2408
task_queue *queue = create_queue();
2409
g_assert_nonnull(queue);
2410
__attribute__((cleanup(cleanup_buffer)))
2411
buffer password = {};
2412
bool password_is_read = false;
2413
const char helper_directory[] = "/nonexistent";
2414
const char *const argv[] = { "/usr/bin/id", "--group", NULL };
2418
const bool success = start_mandos_client(queue, epoll_fd,
2419
&mandos_client_exited,
2420
&quit_now, &password,
2422
&fixture->orig_sigaction,
2423
fixture->orig_sigmask,
2424
helper_directory, user,
2426
g_assert_true(success);
2427
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2429
struct timespec starttime, currtime;
2430
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2432
queue->next_run = 0;
2433
string_set cancelled_filenames = {};
2434
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2435
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2436
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2437
} while(((queue->length) > 0)
2439
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2441
g_assert_false(quit_now);
2442
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2443
g_assert_true(mandos_client_exited);
2445
g_assert_true(password_is_read);
2446
g_assert_nonnull(password.data);
2449
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2451
g_assert_true((gid_t)id == id);
2453
g_assert_cmpuint((unsigned int)id, ==, 0);
2456
static void test_start_mandos_client_suid_ruid(test_fixture *fixture,
2457
__attribute__((unused))
2460
if(not is_privileged()){
2461
g_test_skip("Not privileged");
2465
bool mandos_client_exited = false;
2466
bool quit_now = false;
2467
__attribute__((cleanup(cleanup_close)))
2468
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2469
g_assert_cmpint(epoll_fd, >=, 0);
2470
__attribute__((cleanup(cleanup_queue)))
2471
task_queue *queue = create_queue();
2472
g_assert_nonnull(queue);
2473
__attribute__((cleanup(cleanup_buffer)))
2474
buffer password = {};
2475
bool password_is_read = false;
2476
const char helper_directory[] = "/nonexistent";
2477
const char *const argv[] = { "/usr/bin/id", "--user", "--real",
2482
const bool success = start_mandos_client(queue, epoll_fd,
2483
&mandos_client_exited,
2484
&quit_now, &password,
2486
&fixture->orig_sigaction,
2487
fixture->orig_sigmask,
2488
helper_directory, user,
2490
g_assert_true(success);
2491
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2493
struct timespec starttime, currtime;
2494
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2496
queue->next_run = 0;
2497
string_set cancelled_filenames = {};
2498
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2499
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2500
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2501
} while(((queue->length) > 0)
2503
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2505
g_assert_false(quit_now);
2506
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2507
g_assert_true(mandos_client_exited);
2509
g_assert_true(password_is_read);
2510
g_assert_nonnull(password.data);
2513
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2515
g_assert_true((uid_t)id == id);
2517
g_assert_cmpuint((unsigned int)id, ==, user);
2520
static void test_start_mandos_client_suid_rgid(test_fixture *fixture,
2521
__attribute__((unused))
2524
if(not is_privileged()){
2525
g_test_skip("Not privileged");
2529
bool mandos_client_exited = false;
2530
bool quit_now = false;
2531
__attribute__((cleanup(cleanup_close)))
2532
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2533
g_assert_cmpint(epoll_fd, >=, 0);
2534
__attribute__((cleanup(cleanup_queue)))
2535
task_queue *queue = create_queue();
2536
g_assert_nonnull(queue);
2537
__attribute__((cleanup(cleanup_buffer)))
2538
buffer password = {};
2539
bool password_is_read = false;
2540
const char helper_directory[] = "/nonexistent";
2541
const char *const argv[] = { "/usr/bin/id", "--group", "--real",
2546
const bool success = start_mandos_client(queue, epoll_fd,
2547
&mandos_client_exited,
2548
&quit_now, &password,
2550
&fixture->orig_sigaction,
2551
fixture->orig_sigmask,
2552
helper_directory, user,
2554
g_assert_true(success);
2555
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2557
struct timespec starttime, currtime;
2558
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2560
queue->next_run = 0;
2561
string_set cancelled_filenames = {};
2562
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2563
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2564
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2565
} while(((queue->length) > 0)
2567
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2569
g_assert_false(quit_now);
2570
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2571
g_assert_true(mandos_client_exited);
2573
g_assert_true(password_is_read);
2574
g_assert_nonnull(password.data);
2577
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2579
g_assert_true((gid_t)id == id);
2581
g_assert_cmpuint((unsigned int)id, ==, group);
2584
static void test_start_mandos_client_read(test_fixture *fixture,
2585
__attribute__((unused))
2586
gconstpointer user_data){
2587
bool mandos_client_exited = false;
2588
bool quit_now = false;
2589
__attribute__((cleanup(cleanup_close)))
2590
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2591
g_assert_cmpint(epoll_fd, >=, 0);
2592
__attribute__((cleanup(cleanup_queue)))
2593
task_queue *queue = create_queue();
2594
g_assert_nonnull(queue);
2595
__attribute__((cleanup(cleanup_buffer)))
2596
buffer password = {};
2597
bool password_is_read = false;
2598
const char dummy_test_password[] = "dummy test password";
2599
const char helper_directory[] = "/nonexistent";
2600
const char *const argv[] = { "/bin/echo", "-n", dummy_test_password,
2603
const bool success = start_mandos_client(queue, epoll_fd,
2604
&mandos_client_exited,
2605
&quit_now, &password,
2607
&fixture->orig_sigaction,
2608
fixture->orig_sigmask,
2609
helper_directory, 0, 0,
2611
g_assert_true(success);
2612
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2614
struct timespec starttime, currtime;
2615
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2617
queue->next_run = 0;
2618
string_set cancelled_filenames = {};
2619
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2620
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2621
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2622
} while(((queue->length) > 0)
2624
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2626
g_assert_false(quit_now);
2627
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2628
g_assert_true(mandos_client_exited);
2630
g_assert_true(password_is_read);
2631
g_assert_cmpint((int)password.length, ==,
2632
sizeof(dummy_test_password)-1);
2633
g_assert_nonnull(password.data);
2634
g_assert_cmpint(memcmp(dummy_test_password, password.data,
2635
sizeof(dummy_test_password)-1), ==, 0);
2639
void test_start_mandos_client_helper_directory(test_fixture *fixture,
2640
__attribute__((unused))
2643
bool mandos_client_exited = false;
2644
bool quit_now = false;
2645
__attribute__((cleanup(cleanup_close)))
2646
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2647
g_assert_cmpint(epoll_fd, >=, 0);
2648
__attribute__((cleanup(cleanup_queue)))
2649
task_queue *queue = create_queue();
2650
g_assert_nonnull(queue);
2651
__attribute__((cleanup(cleanup_buffer)))
2652
buffer password = {};
2653
bool password_is_read = false;
2654
const char helper_directory[] = "/nonexistent";
2655
const char *const argv[] = { "/bin/sh", "-c",
2656
"echo -n ${MANDOSPLUGINHELPERDIR}", NULL };
2658
const bool success = start_mandos_client(queue, epoll_fd,
2659
&mandos_client_exited,
2660
&quit_now, &password,
2662
&fixture->orig_sigaction,
2663
fixture->orig_sigmask,
2664
helper_directory, 0, 0,
2666
g_assert_true(success);
2667
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2669
struct timespec starttime, currtime;
2670
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2672
queue->next_run = 0;
2673
string_set cancelled_filenames = {};
2674
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2675
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2676
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2677
} while(((queue->length) > 0)
2679
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2681
g_assert_false(quit_now);
2682
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2683
g_assert_true(mandos_client_exited);
2685
g_assert_true(password_is_read);
2686
g_assert_cmpint((int)password.length, ==,
2687
sizeof(helper_directory)-1);
2688
g_assert_nonnull(password.data);
2689
g_assert_cmpint(memcmp(helper_directory, password.data,
2690
sizeof(helper_directory)-1), ==, 0);
2693
__attribute__((nonnull, warn_unused_result))
2694
static bool proc_status_sigblk_to_sigset(const char *const,
2697
static void test_start_mandos_client_sigmask(test_fixture *fixture,
2698
__attribute__((unused))
2699
gconstpointer user_data){
2700
bool mandos_client_exited = false;
2701
bool quit_now = false;
2702
__attribute__((cleanup(cleanup_close)))
2703
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2704
g_assert_cmpint(epoll_fd, >=, 0);
2705
__attribute__((cleanup(cleanup_queue)))
2706
task_queue *queue = create_queue();
2707
g_assert_nonnull(queue);
2708
__attribute__((cleanup(cleanup_buffer)))
2709
buffer password = {};
2710
bool password_is_read = false;
2711
const char helper_directory[] = "/nonexistent";
2712
/* see proc(5) for format of /proc/self/status */
2713
const char *const argv[] = { "/usr/bin/awk",
2714
"$1==\"SigBlk:\"{ print $2 }", "/proc/self/status", NULL };
2716
g_assert_true(start_mandos_client(queue, epoll_fd,
2717
&mandos_client_exited, &quit_now,
2718
&password, &password_is_read,
2719
&fixture->orig_sigaction,
2720
fixture->orig_sigmask,
2721
helper_directory, 0, 0, argv));
2723
struct timespec starttime, currtime;
2724
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2726
queue->next_run = 0;
2727
string_set cancelled_filenames = {};
2728
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2729
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2730
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2731
} while((not (mandos_client_exited and password_is_read))
2733
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2734
g_assert_true(mandos_client_exited);
2735
g_assert_true(password_is_read);
2737
sigset_t parsed_sigmask;
2738
g_assert_true(proc_status_sigblk_to_sigset(password.data,
2741
for(int signum = 1; signum < NSIG; signum++){
2742
const bool has_signal = sigismember(&parsed_sigmask, signum);
2743
if(sigismember(&fixture->orig_sigmask, signum)){
2744
g_assert_true(has_signal);
2746
g_assert_false(has_signal);
2751
__attribute__((nonnull, warn_unused_result))
2752
static bool proc_status_sigblk_to_sigset(const char *const sigblk,
2753
sigset_t *const sigmask){
2754
/* parse /proc/PID/status SigBlk value and convert to a sigset_t */
2755
uintmax_t scanned_sigmask;
2756
if(sscanf(sigblk, "%" SCNxMAX " ", &scanned_sigmask) != 1){
2759
if(sigemptyset(sigmask) != 0){
2762
for(int signum = 1; signum < NSIG; signum++){
2763
if(scanned_sigmask & ((uintmax_t)1 << (signum-1))){
2764
if(sigaddset(sigmask, signum) != 0){
2772
static void run_task_with_stderr_to_dev_null(const task_context task,
2773
task_queue *const queue);
2776
void test_wait_for_mandos_client_exit_badpid(__attribute__((unused))
2777
test_fixture *fixture,
2778
__attribute__((unused))
2779
gconstpointer user_data){
2781
bool mandos_client_exited = false;
2782
bool quit_now = false;
2784
__attribute__((cleanup(cleanup_queue)))
2785
task_queue *queue = create_queue();
2786
g_assert_nonnull(queue);
2787
const task_context task = {
2788
.func=wait_for_mandos_client_exit,
2790
.mandos_client_exited=&mandos_client_exited,
2791
.quit_now=&quit_now,
2793
run_task_with_stderr_to_dev_null(task, queue);
2795
g_assert_false(mandos_client_exited);
2796
g_assert_true(quit_now);
2797
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2800
static void run_task_with_stderr_to_dev_null(const task_context task,
2801
task_queue *const queue){
2802
FILE *real_stderr = stderr;
2803
FILE *devnull = fopen("/dev/null", "we");
2804
g_assert_nonnull(devnull);
2807
task.func(task, queue);
2808
stderr = real_stderr;
2810
g_assert_cmpint(fclose(devnull), ==, 0);
2814
void test_wait_for_mandos_client_exit_noexit(test_fixture *fixture,
2815
__attribute__((unused))
2816
gconstpointer user_data){
2817
bool mandos_client_exited = false;
2818
bool quit_now = false;
2820
pid_t create_eternal_process(void){
2821
const pid_t pid = fork();
2822
if(pid == 0){ /* Child */
2823
if(not restore_signal_handler(&fixture->orig_sigaction)){
2824
_exit(EXIT_FAILURE);
2826
if(not restore_sigmask(&fixture->orig_sigmask)){
2827
_exit(EXIT_FAILURE);
2835
pid_t pid = create_eternal_process();
2836
g_assert_true(pid != -1);
2838
__attribute__((cleanup(cleanup_queue)))
2839
task_queue *queue = create_queue();
2840
g_assert_nonnull(queue);
2841
const task_context task = {
2842
.func=wait_for_mandos_client_exit,
2844
.mandos_client_exited=&mandos_client_exited,
2845
.quit_now=&quit_now,
2847
task.func(task, queue);
2849
g_assert_false(mandos_client_exited);
2850
g_assert_false(quit_now);
2851
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
2853
g_assert_nonnull(find_matching_task(queue, (task_context){
2854
.func=wait_for_mandos_client_exit,
2856
.mandos_client_exited=&mandos_client_exited,
2857
.quit_now=&quit_now,
2862
void test_wait_for_mandos_client_exit_success(test_fixture *fixture,
2863
__attribute__((unused))
2866
bool mandos_client_exited = false;
2867
bool quit_now = false;
2869
pid_t create_successful_process(void){
2870
const pid_t pid = fork();
2871
if(pid == 0){ /* Child */
2872
if(not restore_signal_handler(&fixture->orig_sigaction)){
2873
_exit(EXIT_FAILURE);
2875
if(not restore_sigmask(&fixture->orig_sigmask)){
2876
_exit(EXIT_FAILURE);
2882
const pid_t pid = create_successful_process();
2883
g_assert_true(pid != -1);
2885
__attribute__((cleanup(cleanup_queue)))
2886
task_queue *queue = create_queue();
2887
g_assert_nonnull(queue);
2888
const task_context initial_task = {
2889
.func=wait_for_mandos_client_exit,
2891
.mandos_client_exited=&mandos_client_exited,
2892
.quit_now=&quit_now,
2894
g_assert_true(add_to_queue(queue, initial_task));
2896
struct timespec starttime, currtime;
2897
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2898
__attribute__((cleanup(cleanup_close)))
2899
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2901
queue->next_run = 0;
2902
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2903
g_assert_true(run_queue(&queue, (string_set[]){{}}, &quit_now));
2904
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2905
} while((not mandos_client_exited)
2907
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2909
g_assert_true(mandos_client_exited);
2910
g_assert_false(quit_now);
2911
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2915
void test_wait_for_mandos_client_exit_failure(test_fixture *fixture,
2916
__attribute__((unused))
2919
bool mandos_client_exited = false;
2920
bool quit_now = false;
2922
pid_t create_failing_process(void){
2923
const pid_t pid = fork();
2924
if(pid == 0){ /* Child */
2925
if(not restore_signal_handler(&fixture->orig_sigaction)){
2926
_exit(EXIT_FAILURE);
2928
if(not restore_sigmask(&fixture->orig_sigmask)){
2929
_exit(EXIT_FAILURE);
2935
const pid_t pid = create_failing_process();
2936
g_assert_true(pid != -1);
2938
__attribute__((cleanup(string_set_clear)))
2939
string_set cancelled_filenames = {};
2940
__attribute__((cleanup(cleanup_close)))
2941
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2942
g_assert_cmpint(epoll_fd, >=, 0);
2943
__attribute__((cleanup(cleanup_queue)))
2944
task_queue *queue = create_queue();
2945
g_assert_nonnull(queue);
2946
g_assert_true(add_to_queue(queue, (task_context){
2947
.func=wait_for_mandos_client_exit,
2949
.mandos_client_exited=&mandos_client_exited,
2950
.quit_now=&quit_now,
2953
g_assert_true(sigismember(&fixture->orig_sigmask, SIGCHLD) == 0);
2955
__attribute__((cleanup(cleanup_close)))
2956
const int devnull_fd = open("/dev/null",
2957
O_WRONLY | O_CLOEXEC | O_NOCTTY);
2958
g_assert_cmpint(devnull_fd, >=, 0);
2959
__attribute__((cleanup(cleanup_close)))
2960
const int real_stderr_fd = dup(STDERR_FILENO);
2961
g_assert_cmpint(real_stderr_fd, >=, 0);
2963
struct timespec starttime, currtime;
2964
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2966
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2967
dup2(devnull_fd, STDERR_FILENO);
2968
const bool success = run_queue(&queue, &cancelled_filenames,
2970
dup2(real_stderr_fd, STDERR_FILENO);
2975
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2976
} while((not mandos_client_exited)
2978
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2980
g_assert_true(quit_now);
2981
g_assert_true(mandos_client_exited);
2982
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2986
void test_wait_for_mandos_client_exit_killed(test_fixture *fixture,
2987
__attribute__((unused))
2988
gconstpointer user_data){
2989
bool mandos_client_exited = false;
2990
bool quit_now = false;
2992
pid_t create_killed_process(void){
2993
const pid_t pid = fork();
2994
if(pid == 0){ /* Child */
2995
if(not restore_signal_handler(&fixture->orig_sigaction)){
2996
_exit(EXIT_FAILURE);
2998
if(not restore_sigmask(&fixture->orig_sigmask)){
2999
_exit(EXIT_FAILURE);
3008
const pid_t pid = create_killed_process();
3009
g_assert_true(pid != -1);
3011
__attribute__((cleanup(string_set_clear)))
3012
string_set cancelled_filenames = {};
3013
__attribute__((cleanup(cleanup_close)))
3014
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3015
g_assert_cmpint(epoll_fd, >=, 0);
3016
__attribute__((cleanup(cleanup_queue)))
3017
task_queue *queue = create_queue();
3018
g_assert_nonnull(queue);
3019
g_assert_true(add_to_queue(queue, (task_context){
3020
.func=wait_for_mandos_client_exit,
3022
.mandos_client_exited=&mandos_client_exited,
3023
.quit_now=&quit_now,
3026
__attribute__((cleanup(cleanup_close)))
3027
const int devnull_fd = open("/dev/null",
3028
O_WRONLY | O_CLOEXEC, O_NOCTTY);
3029
g_assert_cmpint(devnull_fd, >=, 0);
3030
__attribute__((cleanup(cleanup_close)))
3031
const int real_stderr_fd = dup(STDERR_FILENO);
3032
g_assert_cmpint(real_stderr_fd, >=, 0);
3034
struct timespec starttime, currtime;
3035
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
3037
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
3038
dup2(devnull_fd, STDERR_FILENO);
3039
const bool success = run_queue(&queue, &cancelled_filenames,
3041
dup2(real_stderr_fd, STDERR_FILENO);
3046
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
3047
} while((not mandos_client_exited)
3049
and ((currtime.tv_sec - starttime.tv_sec) < 10));
3051
g_assert_true(mandos_client_exited);
3052
g_assert_true(quit_now);
3053
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3056
static bool epoll_set_does_not_contain(int, int);
3059
void test_read_mandos_client_output_readerror(__attribute__((unused))
3060
test_fixture *fixture,
3061
__attribute__((unused))
3064
__attribute__((cleanup(cleanup_close)))
3065
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3066
g_assert_cmpint(epoll_fd, >=, 0);
3068
__attribute__((cleanup(cleanup_buffer)))
3069
buffer password = {};
3071
/* Reading /proc/self/mem from offset 0 will always give EIO */
3072
const int fd = open("/proc/self/mem",
3073
O_RDONLY | O_CLOEXEC | O_NOCTTY);
3075
bool password_is_read = false;
3076
bool quit_now = false;
3077
__attribute__((cleanup(cleanup_queue)))
3078
task_queue *queue = create_queue();
3079
g_assert_nonnull(queue);
3081
task_context task = {
3082
.func=read_mandos_client_output,
3085
.password=&password,
3086
.password_is_read=&password_is_read,
3087
.quit_now=&quit_now,
3089
run_task_with_stderr_to_dev_null(task, queue);
3090
g_assert_false(password_is_read);
3091
g_assert_cmpint((int)password.length, ==, 0);
3092
g_assert_true(quit_now);
3093
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3095
g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
3097
g_assert_cmpint(close(fd), ==, -1);
3100
static bool epoll_set_does_not_contain(int epoll_fd, int fd){
3101
return not epoll_set_contains(epoll_fd, fd, 0);
3105
void test_read_mandos_client_output_nodata(__attribute__((unused))
3106
test_fixture *fixture,
3107
__attribute__((unused))
3108
gconstpointer user_data){
3109
__attribute__((cleanup(cleanup_close)))
3110
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3111
g_assert_cmpint(epoll_fd, >=, 0);
3114
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3116
__attribute__((cleanup(cleanup_buffer)))
3117
buffer password = {};
3119
bool password_is_read = false;
3120
bool quit_now = false;
3121
__attribute__((cleanup(cleanup_queue)))
3122
task_queue *queue = create_queue();
3123
g_assert_nonnull(queue);
3125
task_context task = {
3126
.func=read_mandos_client_output,
3129
.password=&password,
3130
.password_is_read=&password_is_read,
3131
.quit_now=&quit_now,
3133
task.func(task, queue);
3134
g_assert_false(password_is_read);
3135
g_assert_cmpint((int)password.length, ==, 0);
3136
g_assert_false(quit_now);
3137
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3139
g_assert_nonnull(find_matching_task(queue, (task_context){
3140
.func=read_mandos_client_output,
3143
.password=&password,
3144
.password_is_read=&password_is_read,
3145
.quit_now=&quit_now,
3148
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3149
EPOLLIN | EPOLLRDHUP));
3151
g_assert_cmpint(close(pipefds[1]), ==, 0);
3154
static void test_read_mandos_client_output_eof(__attribute__((unused))
3155
test_fixture *fixture,
3156
__attribute__((unused))
3159
__attribute__((cleanup(cleanup_close)))
3160
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3161
g_assert_cmpint(epoll_fd, >=, 0);
3164
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3165
g_assert_cmpint(close(pipefds[1]), ==, 0);
3167
__attribute__((cleanup(cleanup_buffer)))
3168
buffer password = {};
3170
bool password_is_read = false;
3171
bool quit_now = false;
3172
__attribute__((cleanup(cleanup_queue)))
3173
task_queue *queue = create_queue();
3174
g_assert_nonnull(queue);
3176
task_context task = {
3177
.func=read_mandos_client_output,
3180
.password=&password,
3181
.password_is_read=&password_is_read,
3182
.quit_now=&quit_now,
3184
task.func(task, queue);
3185
g_assert_true(password_is_read);
3186
g_assert_cmpint((int)password.length, ==, 0);
3187
g_assert_false(quit_now);
3188
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3190
g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
3192
g_assert_cmpint(close(pipefds[0]), ==, -1);
3196
void test_read_mandos_client_output_once(__attribute__((unused))
3197
test_fixture *fixture,
3198
__attribute__((unused))
3199
gconstpointer user_data){
3200
__attribute__((cleanup(cleanup_close)))
3201
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3202
g_assert_cmpint(epoll_fd, >=, 0);
3205
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3207
const char dummy_test_password[] = "dummy test password";
3208
/* Start with a pre-allocated buffer */
3209
__attribute__((cleanup(cleanup_buffer)))
3211
.data=malloc(sizeof(dummy_test_password)),
3213
.allocated=sizeof(dummy_test_password),
3215
g_assert_nonnull(password.data);
3216
if(mlock(password.data, password.allocated) != 0){
3217
g_assert_true(errno == EPERM or errno == ENOMEM);
3220
bool password_is_read = false;
3221
bool quit_now = false;
3222
__attribute__((cleanup(cleanup_queue)))
3223
task_queue *queue = create_queue();
3224
g_assert_nonnull(queue);
3226
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3227
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3228
sizeof(dummy_test_password)),
3229
==, (int)sizeof(dummy_test_password));
3231
task_context task = {
3232
.func=read_mandos_client_output,
3235
.password=&password,
3236
.password_is_read=&password_is_read,
3237
.quit_now=&quit_now,
3239
task.func(task, queue);
3241
g_assert_false(password_is_read);
3242
g_assert_cmpint((int)password.length, ==,
3243
(int)sizeof(dummy_test_password));
3244
g_assert_nonnull(password.data);
3245
g_assert_cmpint(memcmp(password.data, dummy_test_password,
3246
sizeof(dummy_test_password)), ==, 0);
3248
g_assert_false(quit_now);
3249
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3251
g_assert_nonnull(find_matching_task(queue, (task_context){
3252
.func=read_mandos_client_output,
3255
.password=&password,
3256
.password_is_read=&password_is_read,
3257
.quit_now=&quit_now,
3260
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3261
EPOLLIN | EPOLLRDHUP));
3263
g_assert_cmpint(close(pipefds[1]), ==, 0);
3267
void test_read_mandos_client_output_malloc(__attribute__((unused))
3268
test_fixture *fixture,
3269
__attribute__((unused))
3270
gconstpointer user_data){
3271
__attribute__((cleanup(cleanup_close)))
3272
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3273
g_assert_cmpint(epoll_fd, >=, 0);
3276
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3278
const char dummy_test_password[] = "dummy test password";
3279
/* Start with an empty buffer */
3280
__attribute__((cleanup(cleanup_buffer)))
3281
buffer password = {};
3283
bool password_is_read = false;
3284
bool quit_now = false;
3285
__attribute__((cleanup(cleanup_queue)))
3286
task_queue *queue = create_queue();
3287
g_assert_nonnull(queue);
3289
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3290
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3291
sizeof(dummy_test_password)),
3292
==, (int)sizeof(dummy_test_password));
3294
task_context task = {
3295
.func=read_mandos_client_output,
3298
.password=&password,
3299
.password_is_read=&password_is_read,
3300
.quit_now=&quit_now,
3302
task.func(task, queue);
3304
g_assert_false(password_is_read);
3305
g_assert_cmpint((int)password.length, ==,
3306
(int)sizeof(dummy_test_password));
3307
g_assert_nonnull(password.data);
3308
g_assert_cmpint(memcmp(password.data, dummy_test_password,
3309
sizeof(dummy_test_password)), ==, 0);
3311
g_assert_false(quit_now);
3312
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3314
g_assert_nonnull(find_matching_task(queue, (task_context){
3315
.func=read_mandos_client_output,
3318
.password=&password,
3319
.password_is_read=&password_is_read,
3320
.quit_now=&quit_now,
3323
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3324
EPOLLIN | EPOLLRDHUP));
3326
g_assert_cmpint(close(pipefds[1]), ==, 0);
3330
void test_read_mandos_client_output_append(__attribute__((unused))
3331
test_fixture *fixture,
3332
__attribute__((unused))
3333
gconstpointer user_data){
3334
__attribute__((cleanup(cleanup_close)))
3335
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3336
g_assert_cmpint(epoll_fd, >=, 0);
3339
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3341
const char dummy_test_password[] = "dummy test password";
3342
__attribute__((cleanup(cleanup_buffer)))
3344
.data=malloc(PIPE_BUF),
3346
.allocated=PIPE_BUF,
3348
g_assert_nonnull(password.data);
3349
if(mlock(password.data, password.allocated) != 0){
3350
g_assert_true(errno == EPERM or errno == ENOMEM);
3353
memset(password.data, 'x', PIPE_BUF);
3354
char password_expected[PIPE_BUF];
3355
memcpy(password_expected, password.data, PIPE_BUF);
3357
bool password_is_read = false;
3358
bool quit_now = false;
3359
__attribute__((cleanup(cleanup_queue)))
3360
task_queue *queue = create_queue();
3361
g_assert_nonnull(queue);
3363
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3364
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3365
sizeof(dummy_test_password)),
3366
==, (int)sizeof(dummy_test_password));
3368
task_context task = {
3369
.func=read_mandos_client_output,
3372
.password=&password,
3373
.password_is_read=&password_is_read,
3374
.quit_now=&quit_now,
3376
task.func(task, queue);
3378
g_assert_false(password_is_read);
3379
g_assert_cmpint((int)password.length, ==,
3380
PIPE_BUF + sizeof(dummy_test_password));
3381
g_assert_nonnull(password.data);
3382
g_assert_cmpint(memcmp(password_expected, password.data, PIPE_BUF),
3384
g_assert_cmpint(memcmp(password.data + PIPE_BUF,
3385
dummy_test_password,
3386
sizeof(dummy_test_password)), ==, 0);
3387
g_assert_false(quit_now);
3388
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3390
g_assert_nonnull(find_matching_task(queue, (task_context){
3391
.func=read_mandos_client_output,
3394
.password=&password,
3395
.password_is_read=&password_is_read,
3396
.quit_now=&quit_now,
3399
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3400
EPOLLIN | EPOLLRDHUP));
3403
static char *make_temporary_directory(void);
3405
static void test_add_inotify_dir_watch(__attribute__((unused))
3406
test_fixture *fixture,
3407
__attribute__((unused))
3408
gconstpointer user_data){
3409
__attribute__((cleanup(cleanup_close)))
3410
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3411
g_assert_cmpint(epoll_fd, >=, 0);
3412
__attribute__((cleanup(cleanup_queue)))
3413
task_queue *queue = create_queue();
3414
g_assert_nonnull(queue);
3415
__attribute__((cleanup(string_set_clear)))
3416
string_set cancelled_filenames = {};
3417
const mono_microsecs current_time = 0;
3419
bool quit_now = false;
3420
buffer password = {};
3421
bool mandos_client_exited = false;
3422
bool password_is_read = false;
3424
__attribute__((cleanup(cleanup_string)))
3425
char *tempdir = make_temporary_directory();
3426
g_assert_nonnull(tempdir);
3428
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3430
&cancelled_filenames,
3432
&mandos_client_exited,
3433
&password_is_read));
3435
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3437
const task_context *const added_read_task
3438
= find_matching_task(queue, (task_context){
3439
.func=read_inotify_event,
3441
.quit_now=&quit_now,
3442
.password=&password,
3444
.cancelled_filenames=&cancelled_filenames,
3445
.current_time=¤t_time,
3446
.mandos_client_exited=&mandos_client_exited,
3447
.password_is_read=&password_is_read,
3449
g_assert_nonnull(added_read_task);
3451
g_assert_cmpint(added_read_task->fd, >, 2);
3452
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3453
g_assert_true(epoll_set_contains(added_read_task->epoll_fd,
3454
added_read_task->fd,
3455
EPOLLIN | EPOLLRDHUP));
3457
g_assert_cmpint(rmdir(tempdir), ==, 0);
3460
static char *make_temporary_directory(void){
3461
char *name = strdup("/tmp/mandosXXXXXX");
3462
g_assert_nonnull(name);
3463
char *result = mkdtemp(name);
3470
static void test_add_inotify_dir_watch_fail(__attribute__((unused))
3471
test_fixture *fixture,
3472
__attribute__((unused))
3473
gconstpointer user_data){
3474
__attribute__((cleanup(cleanup_close)))
3475
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3476
g_assert_cmpint(epoll_fd, >=, 0);
3477
__attribute__((cleanup(cleanup_queue)))
3478
task_queue *queue = create_queue();
3479
g_assert_nonnull(queue);
3480
__attribute__((cleanup(string_set_clear)))
3481
string_set cancelled_filenames = {};
3482
const mono_microsecs current_time = 0;
3484
bool quit_now = false;
3485
buffer password = {};
3486
bool mandos_client_exited = false;
3487
bool password_is_read = false;
3489
const char nonexistent_dir[] = "/nonexistent";
3491
FILE *real_stderr = stderr;
3492
FILE *devnull = fopen("/dev/null", "we");
3493
g_assert_nonnull(devnull);
3495
g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3496
&password, nonexistent_dir,
3497
&cancelled_filenames,
3499
&mandos_client_exited,
3500
&password_is_read));
3501
stderr = real_stderr;
3502
g_assert_cmpint(fclose(devnull), ==, 0);
3504
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3507
static void test_add_inotify_dir_watch_nondir(__attribute__((unused))
3508
test_fixture *fixture,
3509
__attribute__((unused))
3512
__attribute__((cleanup(cleanup_close)))
3513
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3514
g_assert_cmpint(epoll_fd, >=, 0);
3515
__attribute__((cleanup(cleanup_queue)))
3516
task_queue *queue = create_queue();
3517
g_assert_nonnull(queue);
3518
__attribute__((cleanup(string_set_clear)))
3519
string_set cancelled_filenames = {};
3520
const mono_microsecs current_time = 0;
3522
bool quit_now = false;
3523
buffer password = {};
3524
bool mandos_client_exited = false;
3525
bool password_is_read = false;
3527
const char not_a_directory[] = "/dev/tty";
3529
FILE *real_stderr = stderr;
3530
FILE *devnull = fopen("/dev/null", "we");
3531
g_assert_nonnull(devnull);
3533
g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3534
&password, not_a_directory,
3535
&cancelled_filenames,
3537
&mandos_client_exited,
3538
&password_is_read));
3539
stderr = real_stderr;
3540
g_assert_cmpint(fclose(devnull), ==, 0);
3542
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3545
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3546
test_fixture *fixture,
3547
__attribute__((unused))
3550
__attribute__((cleanup(cleanup_close)))
3551
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3552
g_assert_cmpint(epoll_fd, >=, 0);
3553
__attribute__((cleanup(cleanup_queue)))
3554
task_queue *queue = create_queue();
3555
g_assert_nonnull(queue);
3556
__attribute__((cleanup(string_set_clear)))
3557
string_set cancelled_filenames = {};
3558
const mono_microsecs current_time = 0;
3560
bool quit_now = false;
3561
buffer password = {};
3562
bool mandos_client_exited = false;
3563
bool password_is_read = false;
3565
__attribute__((cleanup(cleanup_string)))
3566
char *tempdir = make_temporary_directory();
3567
g_assert_nonnull(tempdir);
3569
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3571
&cancelled_filenames,
3573
&mandos_client_exited,
3574
&password_is_read));
3576
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3578
const task_context *const added_read_task
3579
= find_matching_task(queue,
3580
(task_context){ .func=read_inotify_event });
3581
g_assert_nonnull(added_read_task);
3583
g_assert_cmpint(added_read_task->fd, >, 2);
3584
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3586
/* "sufficient to read at least one event." - inotify(7) */
3587
const size_t ievent_size = (sizeof(struct inotify_event)
3589
struct inotify_event *ievent = malloc(ievent_size);
3590
g_assert_nonnull(ievent);
3592
g_assert_cmpint(read(added_read_task->fd, ievent, ievent_size), ==,
3594
g_assert_cmpint(errno, ==, EAGAIN);
3598
g_assert_cmpint(rmdir(tempdir), ==, 0);
3601
static char *make_temporary_file_in_directory(const char
3605
void test_add_inotify_dir_watch_IN_CLOSE_WRITE(__attribute__((unused))
3606
test_fixture *fixture,
3607
__attribute__((unused))
3610
__attribute__((cleanup(cleanup_close)))
3611
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3612
g_assert_cmpint(epoll_fd, >=, 0);
3613
__attribute__((cleanup(cleanup_queue)))
3614
task_queue *queue = create_queue();
3615
g_assert_nonnull(queue);
3616
__attribute__((cleanup(string_set_clear)))
3617
string_set cancelled_filenames = {};
3618
const mono_microsecs current_time = 0;
3620
bool quit_now = false;
3621
buffer password = {};
3622
bool mandos_client_exited = false;
3623
bool password_is_read = false;
3625
__attribute__((cleanup(cleanup_string)))
3626
char *tempdir = make_temporary_directory();
3627
g_assert_nonnull(tempdir);
3629
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3631
&cancelled_filenames,
3633
&mandos_client_exited,
3634
&password_is_read));
3636
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3638
const task_context *const added_read_task
3639
= find_matching_task(queue,
3640
(task_context){ .func=read_inotify_event });
3641
g_assert_nonnull(added_read_task);
3643
g_assert_cmpint(added_read_task->fd, >, 2);
3644
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3646
__attribute__((cleanup(cleanup_string)))
3647
char *filename = make_temporary_file_in_directory(tempdir);
3648
g_assert_nonnull(filename);
3650
/* "sufficient to read at least one event." - inotify(7) */
3651
const size_t ievent_size = (sizeof(struct inotify_event)
3653
struct inotify_event *ievent = malloc(ievent_size);
3654
g_assert_nonnull(ievent);
3656
ssize_t read_size = 0;
3657
read_size = read(added_read_task->fd, ievent, ievent_size);
3659
g_assert_cmpint((int)read_size, >, 0);
3660
g_assert_true(ievent->mask & IN_CLOSE_WRITE);
3661
g_assert_cmpstr(ievent->name, ==, basename(filename));
3665
g_assert_cmpint(unlink(filename), ==, 0);
3666
g_assert_cmpint(rmdir(tempdir), ==, 0);
3669
static char *make_temporary_prefixed_file_in_directory(const char
3673
char *filename = NULL;
3674
g_assert_cmpint(asprintf(&filename, "%s/%sXXXXXX", dir, prefix),
3676
g_assert_nonnull(filename);
3677
const int fd = mkostemp(filename, O_CLOEXEC);
3678
g_assert_cmpint(fd, >=, 0);
3679
g_assert_cmpint(close(fd), ==, 0);
3683
static char *make_temporary_file_in_directory(const char
3685
return make_temporary_prefixed_file_in_directory("temp", dir);
3689
void test_add_inotify_dir_watch_IN_MOVED_TO(__attribute__((unused))
3690
test_fixture *fixture,
3691
__attribute__((unused))
3692
gconstpointer user_data){
3693
__attribute__((cleanup(cleanup_close)))
3694
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3695
g_assert_cmpint(epoll_fd, >=, 0);
3696
__attribute__((cleanup(cleanup_queue)))
3697
task_queue *queue = create_queue();
3698
g_assert_nonnull(queue);
3699
__attribute__((cleanup(string_set_clear)))
3700
string_set cancelled_filenames = {};
3701
const mono_microsecs current_time = 0;
3703
bool quit_now = false;
3704
buffer password = {};
3705
bool mandos_client_exited = false;
3706
bool password_is_read = false;
3708
__attribute__((cleanup(cleanup_string)))
3709
char *watchdir = make_temporary_directory();
3710
g_assert_nonnull(watchdir);
3712
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3713
&password, watchdir,
3714
&cancelled_filenames,
3716
&mandos_client_exited,
3717
&password_is_read));
3719
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3721
const task_context *const added_read_task
3722
= find_matching_task(queue,
3723
(task_context){ .func=read_inotify_event });
3724
g_assert_nonnull(added_read_task);
3726
g_assert_cmpint(added_read_task->fd, >, 2);
3727
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3729
char *sourcedir = make_temporary_directory();
3730
g_assert_nonnull(sourcedir);
3732
__attribute__((cleanup(cleanup_string)))
3733
char *filename = make_temporary_file_in_directory(sourcedir);
3734
g_assert_nonnull(filename);
3736
__attribute__((cleanup(cleanup_string)))
3737
char *targetfilename = NULL;
3738
g_assert_cmpint(asprintf(&targetfilename, "%s/%s", watchdir,
3739
basename(filename)), >, 0);
3740
g_assert_nonnull(targetfilename);
3742
g_assert_cmpint(rename(filename, targetfilename), ==, 0);
3743
g_assert_cmpint(rmdir(sourcedir), ==, 0);
3746
/* "sufficient to read at least one event." - inotify(7) */
3747
const size_t ievent_size = (sizeof(struct inotify_event)
3749
struct inotify_event *ievent = malloc(ievent_size);
3750
g_assert_nonnull(ievent);
3752
ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3754
g_assert_cmpint((int)read_size, >, 0);
3755
g_assert_true(ievent->mask & IN_MOVED_TO);
3756
g_assert_cmpstr(ievent->name, ==, basename(targetfilename));
3760
g_assert_cmpint(unlink(targetfilename), ==, 0);
3761
g_assert_cmpint(rmdir(watchdir), ==, 0);
3765
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
3766
test_fixture *fixture,
3767
__attribute__((unused))
3770
__attribute__((cleanup(cleanup_close)))
3771
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3772
g_assert_cmpint(epoll_fd, >=, 0);
3773
__attribute__((cleanup(cleanup_queue)))
3774
task_queue *queue = create_queue();
3775
g_assert_nonnull(queue);
3776
__attribute__((cleanup(string_set_clear)))
3777
string_set cancelled_filenames = {};
3778
const mono_microsecs current_time = 0;
3780
bool quit_now = false;
3781
buffer password = {};
3782
bool mandos_client_exited = false;
3783
bool password_is_read = false;
3785
__attribute__((cleanup(cleanup_string)))
3786
char *tempdir = make_temporary_directory();
3787
g_assert_nonnull(tempdir);
3789
__attribute__((cleanup(cleanup_string)))
3790
char *tempfilename = make_temporary_file_in_directory(tempdir);
3791
g_assert_nonnull(tempfilename);
3793
__attribute__((cleanup(cleanup_string)))
3794
char *targetdir = make_temporary_directory();
3795
g_assert_nonnull(targetdir);
3797
__attribute__((cleanup(cleanup_string)))
3798
char *targetfilename = NULL;
3799
g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
3800
basename(tempfilename)), >, 0);
3801
g_assert_nonnull(targetfilename);
3803
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3805
&cancelled_filenames,
3807
&mandos_client_exited,
3808
&password_is_read));
3810
g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
3812
const task_context *const added_read_task
3813
= find_matching_task(queue,
3814
(task_context){ .func=read_inotify_event });
3815
g_assert_nonnull(added_read_task);
3817
/* "sufficient to read at least one event." - inotify(7) */
3818
const size_t ievent_size = (sizeof(struct inotify_event)
3820
struct inotify_event *ievent = malloc(ievent_size);
3821
g_assert_nonnull(ievent);
3823
ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3825
g_assert_cmpint((int)read_size, >, 0);
3826
g_assert_true(ievent->mask & IN_MOVED_FROM);
3827
g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
3831
g_assert_cmpint(unlink(targetfilename), ==, 0);
3832
g_assert_cmpint(rmdir(targetdir), ==, 0);
3833
g_assert_cmpint(rmdir(tempdir), ==, 0);
3837
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3838
test_fixture *fixture,
3839
__attribute__((unused))
3840
gconstpointer user_data){
3841
__attribute__((cleanup(cleanup_close)))
3842
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3843
g_assert_cmpint(epoll_fd, >=, 0);
3844
__attribute__((cleanup(cleanup_queue)))
3845
task_queue *queue = create_queue();
3846
g_assert_nonnull(queue);
3847
__attribute__((cleanup(string_set_clear)))
3848
string_set cancelled_filenames = {};
3849
const mono_microsecs current_time = 0;
3851
bool quit_now = false;
3852
buffer password = {};
3853
bool mandos_client_exited = false;
3854
bool password_is_read = false;
3856
__attribute__((cleanup(cleanup_string)))
3857
char *tempdir = make_temporary_directory();
3858
g_assert_nonnull(tempdir);
3860
__attribute__((cleanup(cleanup_string)))
3861
char *tempfile = make_temporary_file_in_directory(tempdir);
3862
g_assert_nonnull(tempfile);
3864
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3866
&cancelled_filenames,
3868
&mandos_client_exited,
3869
&password_is_read));
3870
g_assert_cmpint(unlink(tempfile), ==, 0);
3872
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3874
const task_context *const added_read_task
3875
= find_matching_task(queue,
3876
(task_context){ .func=read_inotify_event });
3877
g_assert_nonnull(added_read_task);
3879
g_assert_cmpint(added_read_task->fd, >, 2);
3880
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3882
/* "sufficient to read at least one event." - inotify(7) */
3883
const size_t ievent_size = (sizeof(struct inotify_event)
3885
struct inotify_event *ievent = malloc(ievent_size);
3886
g_assert_nonnull(ievent);
3888
ssize_t read_size = 0;
3889
read_size = read(added_read_task->fd, ievent, ievent_size);
3891
g_assert_cmpint((int)read_size, >, 0);
3892
g_assert_true(ievent->mask & IN_DELETE);
3893
g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3897
g_assert_cmpint(rmdir(tempdir), ==, 0);
3901
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
3902
test_fixture *fixture,
3903
__attribute__((unused))
3906
__attribute__((cleanup(cleanup_close)))
3907
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3908
g_assert_cmpint(epoll_fd, >=, 0);
3909
__attribute__((cleanup(cleanup_queue)))
3910
task_queue *queue = create_queue();
3911
g_assert_nonnull(queue);
3912
__attribute__((cleanup(string_set_clear)))
3913
string_set cancelled_filenames = {};
3914
const mono_microsecs current_time = 0;
3916
bool quit_now = false;
3917
buffer password = {};
3918
bool mandos_client_exited = false;
3919
bool password_is_read = false;
3921
__attribute__((cleanup(cleanup_string)))
3922
char *tempdir = make_temporary_directory();
3923
g_assert_nonnull(tempdir);
3925
__attribute__((cleanup(cleanup_string)))
3926
char *tempfile = make_temporary_file_in_directory(tempdir);
3927
g_assert_nonnull(tempfile);
3928
int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
3930
g_assert_cmpint(tempfile_fd, >, 2);
3932
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3934
&cancelled_filenames,
3936
&mandos_client_exited,
3937
&password_is_read));
3938
g_assert_cmpint(unlink(tempfile), ==, 0);
3940
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3942
const task_context *const added_read_task
3943
= find_matching_task(queue,
3944
(task_context){ .func=read_inotify_event });
3945
g_assert_nonnull(added_read_task);
3947
g_assert_cmpint(added_read_task->fd, >, 2);
3948
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3950
/* "sufficient to read at least one event." - inotify(7) */
3951
const size_t ievent_size = (sizeof(struct inotify_event)
3953
struct inotify_event *ievent = malloc(ievent_size);
3954
g_assert_nonnull(ievent);
3956
ssize_t read_size = 0;
3957
read_size = read(added_read_task->fd, ievent, ievent_size);
3959
g_assert_cmpint((int)read_size, >, 0);
3960
g_assert_true(ievent->mask & IN_DELETE);
3961
g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3963
g_assert_cmpint(close(tempfile_fd), ==, 0);
3965
/* IN_EXCL_UNLINK should make the closing of the previously unlinked
3966
file not appear as an ievent, so we should not see it now. */
3967
read_size = read(added_read_task->fd, ievent, ievent_size);
3968
g_assert_cmpint((int)read_size, ==, -1);
3969
g_assert_true(errno == EAGAIN);
3973
g_assert_cmpint(rmdir(tempdir), ==, 0);
3976
static void test_read_inotify_event_readerror(__attribute__((unused))
3977
test_fixture *fixture,
3978
__attribute__((unused))
3981
__attribute__((cleanup(cleanup_close)))
3982
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3983
g_assert_cmpint(epoll_fd, >=, 0);
3984
const mono_microsecs current_time = 0;
3986
/* Reading /proc/self/mem from offset 0 will always result in EIO */
3987
const int fd = open("/proc/self/mem",
3988
O_RDONLY | O_CLOEXEC | O_NOCTTY);
3990
bool quit_now = false;
3991
__attribute__((cleanup(cleanup_queue)))
3992
task_queue *queue = create_queue();
3993
g_assert_nonnull(queue);
3995
task_context task = {
3996
.func=read_inotify_event,
3999
.quit_now=&quit_now,
4000
.filename=strdup("/nonexistent"),
4001
.cancelled_filenames = &(string_set){},
4003
.current_time=¤t_time,
4005
g_assert_nonnull(task.filename);
4006
run_task_with_stderr_to_dev_null(task, queue);
4007
g_assert_true(quit_now);
4008
g_assert_true(queue->next_run == 0);
4009
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4011
g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
4013
g_assert_cmpint(close(fd), ==, -1);
4016
static void test_read_inotify_event_bad_epoll(__attribute__((unused))
4017
test_fixture *fixture,
4018
__attribute__((unused))
4021
const mono_microsecs current_time = 17;
4024
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4025
const int epoll_fd = pipefds[0]; /* This will obviously fail */
4027
bool quit_now = false;
4028
buffer password = {};
4029
bool mandos_client_exited = false;
4030
bool password_is_read = false;
4031
__attribute__((cleanup(cleanup_queue)))
4032
task_queue *queue = create_queue();
4033
g_assert_nonnull(queue);
4035
task_context task = {
4036
.func=read_inotify_event,
4039
.quit_now=&quit_now,
4040
.password=&password,
4041
.filename=strdup("/nonexistent"),
4042
.cancelled_filenames = &(string_set){},
4044
.current_time=¤t_time,
4045
.mandos_client_exited=&mandos_client_exited,
4046
.password_is_read=&password_is_read,
4048
g_assert_nonnull(task.filename);
4049
run_task_with_stderr_to_dev_null(task, queue);
4051
g_assert_nonnull(find_matching_task(queue, task));
4052
g_assert_true(queue->next_run == 1000000 + current_time);
4054
g_assert_cmpint(close(pipefds[0]), ==, 0);
4055
g_assert_cmpint(close(pipefds[1]), ==, 0);
4058
static void test_read_inotify_event_nodata(__attribute__((unused))
4059
test_fixture *fixture,
4060
__attribute__((unused))
4061
gconstpointer user_data){
4062
__attribute__((cleanup(cleanup_close)))
4063
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4064
g_assert_cmpint(epoll_fd, >=, 0);
4065
const mono_microsecs current_time = 0;
4068
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4070
bool quit_now = false;
4071
buffer password = {};
4072
bool mandos_client_exited = false;
4073
bool password_is_read = false;
4074
__attribute__((cleanup(cleanup_queue)))
4075
task_queue *queue = create_queue();
4076
g_assert_nonnull(queue);
4078
task_context task = {
4079
.func=read_inotify_event,
4082
.quit_now=&quit_now,
4083
.password=&password,
4084
.filename=strdup("/nonexistent"),
4085
.cancelled_filenames = &(string_set){},
4087
.current_time=¤t_time,
4088
.mandos_client_exited=&mandos_client_exited,
4089
.password_is_read=&password_is_read,
4091
g_assert_nonnull(task.filename);
4092
task.func(task, queue);
4093
g_assert_false(quit_now);
4094
g_assert_true(queue->next_run == 0);
4095
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4097
g_assert_nonnull(find_matching_task(queue, (task_context){
4098
.func=read_inotify_event,
4101
.quit_now=&quit_now,
4102
.password=&password,
4103
.filename=task.filename,
4104
.cancelled_filenames=task.cancelled_filenames,
4105
.current_time=¤t_time,
4106
.mandos_client_exited=&mandos_client_exited,
4107
.password_is_read=&password_is_read,
4110
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4111
EPOLLIN | EPOLLRDHUP));
4113
g_assert_cmpint(close(pipefds[1]), ==, 0);
4116
static void test_read_inotify_event_eof(__attribute__((unused))
4117
test_fixture *fixture,
4118
__attribute__((unused))
4119
gconstpointer user_data){
4120
__attribute__((cleanup(cleanup_close)))
4121
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4122
g_assert_cmpint(epoll_fd, >=, 0);
4123
const mono_microsecs current_time = 0;
4126
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4127
g_assert_cmpint(close(pipefds[1]), ==, 0);
4129
bool quit_now = false;
4130
buffer password = {};
4131
__attribute__((cleanup(cleanup_queue)))
4132
task_queue *queue = create_queue();
4133
g_assert_nonnull(queue);
4135
task_context task = {
4136
.func=read_inotify_event,
4139
.quit_now=&quit_now,
4140
.password=&password,
4141
.filename=strdup("/nonexistent"),
4142
.cancelled_filenames = &(string_set){},
4144
.current_time=¤t_time,
4146
run_task_with_stderr_to_dev_null(task, queue);
4147
g_assert_true(quit_now);
4148
g_assert_true(queue->next_run == 0);
4149
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4151
g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
4153
g_assert_cmpint(close(pipefds[0]), ==, -1);
4157
void test_read_inotify_event_IN_CLOSE_WRITE(__attribute__((unused))
4158
test_fixture *fixture,
4159
__attribute__((unused))
4160
gconstpointer user_data){
4161
__attribute__((cleanup(cleanup_close)))
4162
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4163
g_assert_cmpint(epoll_fd, >=, 0);
4164
const mono_microsecs current_time = 0;
4167
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4169
/* "sufficient to read at least one event." - inotify(7) */
4170
const size_t ievent_max_size = (sizeof(struct inotify_event)
4172
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4174
struct inotify_event event;
4175
char name_buffer[NAME_MAX + 1];
4177
struct inotify_event *const ievent = &ievent_buffer.event;
4179
const char dummy_file_name[] = "ask.dummy_file_name";
4180
ievent->mask = IN_CLOSE_WRITE;
4181
ievent->len = sizeof(dummy_file_name);
4182
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4183
const size_t ievent_size = (sizeof(struct inotify_event)
4184
+ sizeof(dummy_file_name));
4185
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4187
g_assert_cmpint(close(pipefds[1]), ==, 0);
4189
bool quit_now = false;
4190
buffer password = {};
4191
bool mandos_client_exited = false;
4192
bool password_is_read = false;
4193
__attribute__((cleanup(cleanup_queue)))
4194
task_queue *queue = create_queue();
4195
g_assert_nonnull(queue);
4197
task_context task = {
4198
.func=read_inotify_event,
4201
.quit_now=&quit_now,
4202
.password=&password,
4203
.filename=strdup("/nonexistent"),
4204
.cancelled_filenames = &(string_set){},
4206
.current_time=¤t_time,
4207
.mandos_client_exited=&mandos_client_exited,
4208
.password_is_read=&password_is_read,
4210
task.func(task, queue);
4211
g_assert_false(quit_now);
4212
g_assert_true(queue->next_run != 0);
4213
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4215
g_assert_nonnull(find_matching_task(queue, (task_context){
4216
.func=read_inotify_event,
4219
.quit_now=&quit_now,
4220
.password=&password,
4221
.filename=task.filename,
4222
.cancelled_filenames=task.cancelled_filenames,
4223
.current_time=¤t_time,
4224
.mandos_client_exited=&mandos_client_exited,
4225
.password_is_read=&password_is_read,
4228
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4229
EPOLLIN | EPOLLRDHUP));
4231
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4233
__attribute__((cleanup(cleanup_string)))
4234
char *filename = NULL;
4235
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4236
dummy_file_name), >, 0);
4237
g_assert_nonnull(filename);
4238
g_assert_nonnull(find_matching_task(queue, (task_context){
4239
.func=open_and_parse_question,
4242
.question_filename=filename,
4243
.password=&password,
4244
.cancelled_filenames=task.cancelled_filenames,
4245
.current_time=¤t_time,
4246
.mandos_client_exited=&mandos_client_exited,
4247
.password_is_read=&password_is_read,
4252
void test_read_inotify_event_IN_MOVED_TO(__attribute__((unused))
4253
test_fixture *fixture,
4254
__attribute__((unused))
4255
gconstpointer user_data){
4256
__attribute__((cleanup(cleanup_close)))
4257
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4258
g_assert_cmpint(epoll_fd, >=, 0);
4259
const mono_microsecs current_time = 0;
4262
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4264
/* "sufficient to read at least one event." - inotify(7) */
4265
const size_t ievent_max_size = (sizeof(struct inotify_event)
4267
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4269
struct inotify_event event;
4270
char name_buffer[NAME_MAX + 1];
4272
struct inotify_event *const ievent = &ievent_buffer.event;
4274
const char dummy_file_name[] = "ask.dummy_file_name";
4275
ievent->mask = IN_MOVED_TO;
4276
ievent->len = sizeof(dummy_file_name);
4277
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4278
const size_t ievent_size = (sizeof(struct inotify_event)
4279
+ sizeof(dummy_file_name));
4280
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4282
g_assert_cmpint(close(pipefds[1]), ==, 0);
4284
bool quit_now = false;
4285
buffer password = {};
4286
bool mandos_client_exited = false;
4287
bool password_is_read = false;
4288
__attribute__((cleanup(cleanup_queue)))
4289
task_queue *queue = create_queue();
4290
g_assert_nonnull(queue);
4292
task_context task = {
4293
.func=read_inotify_event,
4296
.quit_now=&quit_now,
4297
.password=&password,
4298
.filename=strdup("/nonexistent"),
4299
.cancelled_filenames = &(string_set){},
4301
.current_time=¤t_time,
4302
.mandos_client_exited=&mandos_client_exited,
4303
.password_is_read=&password_is_read,
4305
task.func(task, queue);
4306
g_assert_false(quit_now);
4307
g_assert_true(queue->next_run != 0);
4308
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4310
g_assert_nonnull(find_matching_task(queue, (task_context){
4311
.func=read_inotify_event,
4314
.quit_now=&quit_now,
4315
.password=&password,
4316
.filename=task.filename,
4317
.cancelled_filenames=task.cancelled_filenames,
4318
.current_time=¤t_time,
4319
.mandos_client_exited=&mandos_client_exited,
4320
.password_is_read=&password_is_read,
4323
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4324
EPOLLIN | EPOLLRDHUP));
4326
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4328
__attribute__((cleanup(cleanup_string)))
4329
char *filename = NULL;
4330
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4331
dummy_file_name), >, 0);
4332
g_assert_nonnull(filename);
4333
g_assert_nonnull(find_matching_task(queue, (task_context){
4334
.func=open_and_parse_question,
4337
.question_filename=filename,
4338
.password=&password,
4339
.cancelled_filenames=task.cancelled_filenames,
4340
.current_time=¤t_time,
4341
.mandos_client_exited=&mandos_client_exited,
4342
.password_is_read=&password_is_read,
4347
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
4348
test_fixture *fixture,
4349
__attribute__((unused))
4350
gconstpointer user_data){
4351
__attribute__((cleanup(cleanup_close)))
4352
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4353
g_assert_cmpint(epoll_fd, >=, 0);
4354
__attribute__((cleanup(string_set_clear)))
4355
string_set cancelled_filenames = {};
4356
const mono_microsecs current_time = 0;
4359
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4361
/* "sufficient to read at least one event." - inotify(7) */
4362
const size_t ievent_max_size = (sizeof(struct inotify_event)
4364
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4366
struct inotify_event event;
4367
char name_buffer[NAME_MAX + 1];
4369
struct inotify_event *const ievent = &ievent_buffer.event;
4371
const char dummy_file_name[] = "ask.dummy_file_name";
4372
ievent->mask = IN_MOVED_FROM;
4373
ievent->len = sizeof(dummy_file_name);
4374
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4375
const size_t ievent_size = (sizeof(struct inotify_event)
4376
+ sizeof(dummy_file_name));
4377
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4379
g_assert_cmpint(close(pipefds[1]), ==, 0);
4381
bool quit_now = false;
4382
buffer password = {};
4383
bool mandos_client_exited = false;
4384
bool password_is_read = false;
4385
__attribute__((cleanup(cleanup_queue)))
4386
task_queue *queue = create_queue();
4387
g_assert_nonnull(queue);
4389
task_context task = {
4390
.func=read_inotify_event,
4393
.quit_now=&quit_now,
4394
.password=&password,
4395
.filename=strdup("/nonexistent"),
4396
.cancelled_filenames=&cancelled_filenames,
4397
.current_time=¤t_time,
4398
.mandos_client_exited=&mandos_client_exited,
4399
.password_is_read=&password_is_read,
4401
task.func(task, queue);
4402
g_assert_false(quit_now);
4403
g_assert_true(queue->next_run == 0);
4404
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4406
g_assert_nonnull(find_matching_task(queue, (task_context){
4407
.func=read_inotify_event,
4410
.quit_now=&quit_now,
4411
.password=&password,
4412
.filename=task.filename,
4413
.cancelled_filenames=&cancelled_filenames,
4414
.current_time=¤t_time,
4415
.mandos_client_exited=&mandos_client_exited,
4416
.password_is_read=&password_is_read,
4419
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4420
EPOLLIN | EPOLLRDHUP));
4422
__attribute__((cleanup(cleanup_string)))
4423
char *filename = NULL;
4424
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4425
dummy_file_name), >, 0);
4426
g_assert_nonnull(filename);
4427
g_assert_true(string_set_contains(*task.cancelled_filenames,
4431
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4432
test_fixture *fixture,
4433
__attribute__((unused))
4436
__attribute__((cleanup(cleanup_close)))
4437
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4438
g_assert_cmpint(epoll_fd, >=, 0);
4439
__attribute__((cleanup(string_set_clear)))
4440
string_set cancelled_filenames = {};
4441
const mono_microsecs current_time = 0;
4444
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4446
/* "sufficient to read at least one event." - inotify(7) */
4447
const size_t ievent_max_size = (sizeof(struct inotify_event)
4449
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4451
struct inotify_event event;
4452
char name_buffer[NAME_MAX + 1];
4454
struct inotify_event *const ievent = &ievent_buffer.event;
4456
const char dummy_file_name[] = "ask.dummy_file_name";
4457
ievent->mask = IN_DELETE;
4458
ievent->len = sizeof(dummy_file_name);
4459
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4460
const size_t ievent_size = (sizeof(struct inotify_event)
4461
+ sizeof(dummy_file_name));
4462
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4464
g_assert_cmpint(close(pipefds[1]), ==, 0);
4466
bool quit_now = false;
4467
buffer password = {};
4468
bool mandos_client_exited = false;
4469
bool password_is_read = false;
4470
__attribute__((cleanup(cleanup_queue)))
4471
task_queue *queue = create_queue();
4472
g_assert_nonnull(queue);
4474
task_context task = {
4475
.func=read_inotify_event,
4478
.quit_now=&quit_now,
4479
.password=&password,
4480
.filename=strdup("/nonexistent"),
4481
.cancelled_filenames=&cancelled_filenames,
4482
.current_time=¤t_time,
4483
.mandos_client_exited=&mandos_client_exited,
4484
.password_is_read=&password_is_read,
4486
task.func(task, queue);
4487
g_assert_false(quit_now);
4488
g_assert_true(queue->next_run == 0);
4489
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4491
g_assert_nonnull(find_matching_task(queue, (task_context){
4492
.func=read_inotify_event,
4495
.quit_now=&quit_now,
4496
.password=&password,
4497
.filename=task.filename,
4498
.cancelled_filenames=&cancelled_filenames,
4499
.current_time=¤t_time,
4500
.mandos_client_exited=&mandos_client_exited,
4501
.password_is_read=&password_is_read,
4504
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4505
EPOLLIN | EPOLLRDHUP));
4507
__attribute__((cleanup(cleanup_string)))
4508
char *filename = NULL;
4509
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4510
dummy_file_name), >, 0);
4511
g_assert_nonnull(filename);
4512
g_assert_true(string_set_contains(*task.cancelled_filenames,
4517
test_read_inotify_event_IN_CLOSE_WRITE_badname(__attribute__((unused))
4518
test_fixture *fixture,
4519
__attribute__((unused))
4522
__attribute__((cleanup(cleanup_close)))
4523
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4524
g_assert_cmpint(epoll_fd, >=, 0);
4525
const mono_microsecs current_time = 0;
4528
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4530
/* "sufficient to read at least one event." - inotify(7) */
4531
const size_t ievent_max_size = (sizeof(struct inotify_event)
4533
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4535
struct inotify_event event;
4536
char name_buffer[NAME_MAX + 1];
4538
struct inotify_event *const ievent = &ievent_buffer.event;
4540
const char dummy_file_name[] = "ignored.dummy_file_name";
4541
ievent->mask = IN_CLOSE_WRITE;
4542
ievent->len = sizeof(dummy_file_name);
4543
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4544
const size_t ievent_size = (sizeof(struct inotify_event)
4545
+ sizeof(dummy_file_name));
4546
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4548
g_assert_cmpint(close(pipefds[1]), ==, 0);
4550
bool quit_now = false;
4551
buffer password = {};
4552
bool mandos_client_exited = false;
4553
bool password_is_read = false;
4554
__attribute__((cleanup(cleanup_queue)))
4555
task_queue *queue = create_queue();
4556
g_assert_nonnull(queue);
4558
task_context task = {
4559
.func=read_inotify_event,
4562
.quit_now=&quit_now,
4563
.password=&password,
4564
.filename=strdup("/nonexistent"),
4565
.cancelled_filenames = &(string_set){},
4567
.current_time=¤t_time,
4568
.mandos_client_exited=&mandos_client_exited,
4569
.password_is_read=&password_is_read,
4571
task.func(task, queue);
4572
g_assert_false(quit_now);
4573
g_assert_true(queue->next_run == 0);
4574
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4576
g_assert_nonnull(find_matching_task(queue, (task_context){
4577
.func=read_inotify_event,
4580
.quit_now=&quit_now,
4581
.password=&password,
4582
.filename=task.filename,
4583
.cancelled_filenames=task.cancelled_filenames,
4584
.current_time=¤t_time,
4585
.mandos_client_exited=&mandos_client_exited,
4586
.password_is_read=&password_is_read,
4589
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4590
EPOLLIN | EPOLLRDHUP));
4594
test_read_inotify_event_IN_MOVED_TO_badname(__attribute__((unused))
4595
test_fixture *fixture,
4596
__attribute__((unused))
4597
gconstpointer user_data){
4598
__attribute__((cleanup(cleanup_close)))
4599
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4600
g_assert_cmpint(epoll_fd, >=, 0);
4601
const mono_microsecs current_time = 0;
4604
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4606
/* "sufficient to read at least one event." - inotify(7) */
4607
const size_t ievent_max_size = (sizeof(struct inotify_event)
4609
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4611
struct inotify_event event;
4612
char name_buffer[NAME_MAX + 1];
4614
struct inotify_event *const ievent = &ievent_buffer.event;
4616
const char dummy_file_name[] = "ignored.dummy_file_name";
4617
ievent->mask = IN_MOVED_TO;
4618
ievent->len = sizeof(dummy_file_name);
4619
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4620
const size_t ievent_size = (sizeof(struct inotify_event)
4621
+ sizeof(dummy_file_name));
4622
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4624
g_assert_cmpint(close(pipefds[1]), ==, 0);
4626
bool quit_now = false;
4627
buffer password = {};
4628
bool mandos_client_exited = false;
4629
bool password_is_read = false;
4630
__attribute__((cleanup(cleanup_queue)))
4631
task_queue *queue = create_queue();
4632
g_assert_nonnull(queue);
4634
task_context task = {
4635
.func=read_inotify_event,
4638
.quit_now=&quit_now,
4639
.password=&password,
4640
.filename=strdup("/nonexistent"),
4641
.cancelled_filenames = &(string_set){},
4643
.current_time=¤t_time,
4644
.mandos_client_exited=&mandos_client_exited,
4645
.password_is_read=&password_is_read,
4647
task.func(task, queue);
4648
g_assert_false(quit_now);
4649
g_assert_true(queue->next_run == 0);
4650
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4652
g_assert_nonnull(find_matching_task(queue, (task_context){
4653
.func=read_inotify_event,
4656
.quit_now=&quit_now,
4657
.password=&password,
4658
.filename=task.filename,
4659
.cancelled_filenames=task.cancelled_filenames,
4660
.current_time=¤t_time,
4661
.mandos_client_exited=&mandos_client_exited,
4662
.password_is_read=&password_is_read,
4665
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4666
EPOLLIN | EPOLLRDHUP));
4670
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
4671
test_fixture *fixture,
4672
__attribute__((unused))
4675
__attribute__((cleanup(cleanup_close)))
4676
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4677
g_assert_cmpint(epoll_fd, >=, 0);
4678
__attribute__((cleanup(string_set_clear)))
4679
string_set cancelled_filenames = {};
4680
const mono_microsecs current_time = 0;
4683
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4685
/* "sufficient to read at least one event." - inotify(7) */
4686
const size_t ievent_max_size = (sizeof(struct inotify_event)
4688
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4690
struct inotify_event event;
4691
char name_buffer[NAME_MAX + 1];
4693
struct inotify_event *const ievent = &ievent_buffer.event;
4695
const char dummy_file_name[] = "ignored.dummy_file_name";
4696
ievent->mask = IN_MOVED_FROM;
4697
ievent->len = sizeof(dummy_file_name);
4698
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4699
const size_t ievent_size = (sizeof(struct inotify_event)
4700
+ sizeof(dummy_file_name));
4701
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4703
g_assert_cmpint(close(pipefds[1]), ==, 0);
4705
bool quit_now = false;
4706
buffer password = {};
4707
bool mandos_client_exited = false;
4708
bool password_is_read = false;
4709
__attribute__((cleanup(cleanup_queue)))
4710
task_queue *queue = create_queue();
4711
g_assert_nonnull(queue);
4713
task_context task = {
4714
.func=read_inotify_event,
4717
.quit_now=&quit_now,
4718
.password=&password,
4719
.filename=strdup("/nonexistent"),
4720
.cancelled_filenames=&cancelled_filenames,
4721
.current_time=¤t_time,
4722
.mandos_client_exited=&mandos_client_exited,
4723
.password_is_read=&password_is_read,
4725
task.func(task, queue);
4726
g_assert_false(quit_now);
4727
g_assert_true(queue->next_run == 0);
4728
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4730
g_assert_nonnull(find_matching_task(queue, (task_context){
4731
.func=read_inotify_event,
4734
.quit_now=&quit_now,
4735
.password=&password,
4736
.filename=task.filename,
4737
.cancelled_filenames=&cancelled_filenames,
4738
.current_time=¤t_time,
4739
.mandos_client_exited=&mandos_client_exited,
4740
.password_is_read=&password_is_read,
4743
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4744
EPOLLIN | EPOLLRDHUP));
4746
__attribute__((cleanup(cleanup_string)))
4747
char *filename = NULL;
4748
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4749
dummy_file_name), >, 0);
4750
g_assert_nonnull(filename);
4751
g_assert_false(string_set_contains(cancelled_filenames, filename));
4755
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4756
test_fixture *fixture,
4757
__attribute__((unused))
4760
__attribute__((cleanup(cleanup_close)))
4761
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4762
g_assert_cmpint(epoll_fd, >=, 0);
4763
__attribute__((cleanup(string_set_clear)))
4764
string_set cancelled_filenames = {};
4765
const mono_microsecs current_time = 0;
4768
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4770
/* "sufficient to read at least one event." - inotify(7) */
4771
const size_t ievent_max_size = (sizeof(struct inotify_event)
4773
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4775
struct inotify_event event;
4776
char name_buffer[NAME_MAX + 1];
4778
struct inotify_event *const ievent = &ievent_buffer.event;
4780
const char dummy_file_name[] = "ignored.dummy_file_name";
4781
ievent->mask = IN_DELETE;
4782
ievent->len = sizeof(dummy_file_name);
4783
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4784
const size_t ievent_size = (sizeof(struct inotify_event)
4785
+ sizeof(dummy_file_name));
4786
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4788
g_assert_cmpint(close(pipefds[1]), ==, 0);
4790
bool quit_now = false;
4791
buffer password = {};
4792
bool mandos_client_exited = false;
4793
bool password_is_read = false;
4794
__attribute__((cleanup(cleanup_queue)))
4795
task_queue *queue = create_queue();
4796
g_assert_nonnull(queue);
4798
task_context task = {
4799
.func=read_inotify_event,
4802
.quit_now=&quit_now,
4803
.password=&password,
4804
.filename=strdup("/nonexistent"),
4805
.cancelled_filenames=&cancelled_filenames,
4806
.current_time=¤t_time,
4807
.mandos_client_exited=&mandos_client_exited,
4808
.password_is_read=&password_is_read,
4810
task.func(task, queue);
4811
g_assert_false(quit_now);
4812
g_assert_true(queue->next_run == 0);
4813
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4815
g_assert_nonnull(find_matching_task(queue, (task_context){
4816
.func=read_inotify_event,
4819
.quit_now=&quit_now,
4820
.password=&password,
4821
.filename=task.filename,
4822
.cancelled_filenames=&cancelled_filenames,
4823
.current_time=¤t_time,
4824
.mandos_client_exited=&mandos_client_exited,
4825
.password_is_read=&password_is_read,
4828
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4829
EPOLLIN | EPOLLRDHUP));
4831
__attribute__((cleanup(cleanup_string)))
4832
char *filename = NULL;
4833
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4834
dummy_file_name), >, 0);
4835
g_assert_nonnull(filename);
4836
g_assert_false(string_set_contains(cancelled_filenames, filename));
4840
void test_open_and_parse_question_ENOENT(__attribute__((unused))
4841
test_fixture *fixture,
4842
__attribute__((unused))
4843
gconstpointer user_data){
4844
__attribute__((cleanup(cleanup_close)))
4845
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4846
g_assert_cmpint(epoll_fd, >=, 0);
4847
__attribute__((cleanup(string_set_clear)))
4848
string_set cancelled_filenames = {};
4849
bool mandos_client_exited = false;
4850
bool password_is_read = false;
4851
__attribute__((cleanup(cleanup_queue)))
4852
task_queue *queue = create_queue();
4853
g_assert_nonnull(queue);
4855
char *const filename = strdup("/nonexistent");
4856
g_assert_nonnull(filename);
4857
task_context task = {
4858
.func=open_and_parse_question,
4859
.question_filename=filename,
4861
.password=(buffer[]){{}},
4863
.cancelled_filenames=&cancelled_filenames,
4864
.current_time=(mono_microsecs[]){0},
4865
.mandos_client_exited=&mandos_client_exited,
4866
.password_is_read=&password_is_read,
4868
task.func(task, queue);
4869
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4872
static void test_open_and_parse_question_EIO(__attribute__((unused))
4873
test_fixture *fixture,
4874
__attribute__((unused))
4875
gconstpointer user_data){
4876
__attribute__((cleanup(cleanup_close)))
4877
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4878
g_assert_cmpint(epoll_fd, >=, 0);
4879
__attribute__((cleanup(string_set_clear)))
4880
string_set cancelled_filenames = {};
4881
buffer password = {};
4882
bool mandos_client_exited = false;
4883
bool password_is_read = false;
4884
__attribute__((cleanup(cleanup_queue)))
4885
task_queue *queue = create_queue();
4886
g_assert_nonnull(queue);
4887
const mono_microsecs current_time = 0;
4889
char *filename = strdup("/proc/self/mem");
4890
task_context task = {
4891
.func=open_and_parse_question,
4892
.question_filename=filename,
4894
.password=&password,
4896
.cancelled_filenames=&cancelled_filenames,
4897
.current_time=¤t_time,
4898
.mandos_client_exited=&mandos_client_exited,
4899
.password_is_read=&password_is_read,
4901
run_task_with_stderr_to_dev_null(task, queue);
4902
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4906
test_open_and_parse_question_parse_error(__attribute__((unused))
4907
test_fixture *fixture,
4908
__attribute__((unused))
4909
gconstpointer user_data){
4910
__attribute__((cleanup(cleanup_close)))
4911
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4912
g_assert_cmpint(epoll_fd, >=, 0);
4913
__attribute__((cleanup(string_set_clear)))
4914
string_set cancelled_filenames = {};
4915
__attribute__((cleanup(cleanup_queue)))
4916
task_queue *queue = create_queue();
4917
g_assert_nonnull(queue);
4919
__attribute__((cleanup(cleanup_string)))
4920
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4921
g_assert_nonnull(tempfilename);
4922
int tempfile = mkostemp(tempfilename, O_CLOEXEC);
4923
g_assert_cmpint(tempfile, >, 0);
4924
const char bad_data[] = "this is bad syntax\n";
4925
g_assert_cmpint(write(tempfile, bad_data, sizeof(bad_data)),
4926
==, sizeof(bad_data));
4927
g_assert_cmpint(close(tempfile), ==, 0);
4929
char *const filename = strdup(tempfilename);
4930
g_assert_nonnull(filename);
4931
task_context task = {
4932
.func=open_and_parse_question,
4933
.question_filename=filename,
4935
.password=(buffer[]){{}},
4937
.cancelled_filenames=&cancelled_filenames,
4938
.current_time=(mono_microsecs[]){0},
4939
.mandos_client_exited=(bool[]){false},
4940
.password_is_read=(bool[]){false},
4942
run_task_with_stderr_to_dev_null(task, queue);
4944
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4946
g_assert_cmpint(unlink(tempfilename), ==, 0);
4950
void test_open_and_parse_question_nosocket(__attribute__((unused))
4951
test_fixture *fixture,
4952
__attribute__((unused))
4953
gconstpointer user_data){
4954
__attribute__((cleanup(cleanup_close)))
4955
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4956
g_assert_cmpint(epoll_fd, >=, 0);
4957
__attribute__((cleanup(string_set_clear)))
4958
string_set cancelled_filenames = {};
4959
__attribute__((cleanup(cleanup_queue)))
4960
task_queue *queue = create_queue();
4961
g_assert_nonnull(queue);
4963
__attribute__((cleanup(cleanup_string)))
4964
char *tempfilename = strdup("/tmp/mandosXXXXXX");
4965
g_assert_nonnull(tempfilename);
4966
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4967
g_assert_cmpint(questionfile, >, 0);
4968
FILE *qf = fdopen(questionfile, "w");
4969
g_assert_cmpint(fprintf(qf, "[Ask]\nPID=1\n"), >, 0);
4970
g_assert_cmpint(fclose(qf), ==, 0);
4972
char *const filename = strdup(tempfilename);
4973
g_assert_nonnull(filename);
4974
task_context task = {
4975
.func=open_and_parse_question,
4976
.question_filename=filename,
4978
.password=(buffer[]){{}},
4980
.cancelled_filenames=&cancelled_filenames,
4981
.current_time=(mono_microsecs[]){0},
4982
.mandos_client_exited=(bool[]){false},
4983
.password_is_read=(bool[]){false},
4985
run_task_with_stderr_to_dev_null(task, queue);
4986
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4988
g_assert_cmpint(unlink(tempfilename), ==, 0);
4992
void test_open_and_parse_question_badsocket(__attribute__((unused))
4993
test_fixture *fixture,
4994
__attribute__((unused))
4995
gconstpointer user_data){
4996
__attribute__((cleanup(cleanup_close)))
4997
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4998
g_assert_cmpint(epoll_fd, >=, 0);
4999
__attribute__((cleanup(string_set_clear)))
5000
string_set cancelled_filenames = {};
5001
__attribute__((cleanup(cleanup_queue)))
5002
task_queue *queue = create_queue();
5003
g_assert_nonnull(queue);
5005
__attribute__((cleanup(cleanup_string)))
5006
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5007
g_assert_nonnull(tempfilename);
5008
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5009
g_assert_cmpint(questionfile, >, 0);
5010
FILE *qf = fdopen(questionfile, "w");
5011
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=\nPID=1\n"), >, 0);
5012
g_assert_cmpint(fclose(qf), ==, 0);
5014
char *const filename = strdup(tempfilename);
5015
g_assert_nonnull(filename);
5016
task_context task = {
5017
.func=open_and_parse_question,
5018
.question_filename=filename,
5020
.password=(buffer[]){{}},
5022
.cancelled_filenames=&cancelled_filenames,
5023
.current_time=(mono_microsecs[]){0},
5024
.mandos_client_exited=(bool[]){false},
5025
.password_is_read=(bool[]){false},
5027
run_task_with_stderr_to_dev_null(task, queue);
5028
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5030
g_assert_cmpint(unlink(tempfilename), ==, 0);
5034
void test_open_and_parse_question_nopid(__attribute__((unused))
5035
test_fixture *fixture,
5036
__attribute__((unused))
5037
gconstpointer user_data){
5038
__attribute__((cleanup(cleanup_close)))
5039
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5040
g_assert_cmpint(epoll_fd, >=, 0);
5041
__attribute__((cleanup(string_set_clear)))
5042
string_set cancelled_filenames = {};
5043
__attribute__((cleanup(cleanup_queue)))
5044
task_queue *queue = create_queue();
5045
g_assert_nonnull(queue);
5047
__attribute__((cleanup(cleanup_string)))
5048
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5049
g_assert_nonnull(tempfilename);
5050
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5051
g_assert_cmpint(questionfile, >, 0);
5052
FILE *qf = fdopen(questionfile, "w");
5053
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\n"), >, 0);
5054
g_assert_cmpint(fclose(qf), ==, 0);
5056
char *const filename = strdup(tempfilename);
5057
g_assert_nonnull(filename);
5058
task_context task = {
5059
.func=open_and_parse_question,
5060
.question_filename=filename,
5062
.password=(buffer[]){{}},
5064
.cancelled_filenames=&cancelled_filenames,
5065
.current_time=(mono_microsecs[]){0},
5066
.mandos_client_exited=(bool[]){false},
5067
.password_is_read=(bool[]){false},
5069
run_task_with_stderr_to_dev_null(task, queue);
5070
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5072
g_assert_cmpint(unlink(tempfilename), ==, 0);
5076
void test_open_and_parse_question_badpid(__attribute__((unused))
5077
test_fixture *fixture,
5078
__attribute__((unused))
5079
gconstpointer user_data){
5080
__attribute__((cleanup(cleanup_close)))
5081
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5082
g_assert_cmpint(epoll_fd, >=, 0);
5083
__attribute__((cleanup(string_set_clear)))
5084
string_set cancelled_filenames = {};
5085
__attribute__((cleanup(cleanup_queue)))
5086
task_queue *queue = create_queue();
5087
g_assert_nonnull(queue);
5089
__attribute__((cleanup(cleanup_string)))
5090
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5091
g_assert_nonnull(tempfilename);
5092
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5093
g_assert_cmpint(questionfile, >, 0);
5094
FILE *qf = fdopen(questionfile, "w");
5095
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=\n"),
5097
g_assert_cmpint(fclose(qf), ==, 0);
5099
char *const filename = strdup(tempfilename);
5100
g_assert_nonnull(filename);
5101
task_context task = {
5102
.func=open_and_parse_question,
5103
.question_filename=filename,
5105
.password=(buffer[]){{}},
5107
.cancelled_filenames=&cancelled_filenames,
5108
.current_time=(mono_microsecs[]){0},
5109
.mandos_client_exited=(bool[]){false},
5110
.password_is_read=(bool[]){false},
5112
run_task_with_stderr_to_dev_null(task, queue);
5113
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5115
g_assert_cmpint(unlink(tempfilename), ==, 0);
5119
test_open_and_parse_question_noexist_pid(__attribute__((unused))
5120
test_fixture *fixture,
5121
__attribute__((unused))
5122
gconstpointer user_data){
5123
__attribute__((cleanup(cleanup_close)))
5124
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5125
g_assert_cmpint(epoll_fd, >=, 0);
5126
__attribute__((cleanup(string_set_clear)))
5127
string_set cancelled_filenames = {};
5128
buffer password = {};
5129
bool mandos_client_exited = false;
5130
bool password_is_read = false;
5131
__attribute__((cleanup(cleanup_queue)))
5132
task_queue *queue = create_queue();
5133
g_assert_nonnull(queue);
5134
const mono_microsecs current_time = 0;
5136
/* Find value of sysctl kernel.pid_max */
5137
uintmax_t pid_max = 0;
5138
FILE *sysctl_pid_max = fopen("/proc/sys/kernel/pid_max", "r");
5139
g_assert_nonnull(sysctl_pid_max);
5140
g_assert_cmpint(fscanf(sysctl_pid_max, "%" PRIuMAX, &pid_max),
5142
g_assert_cmpint(fclose(sysctl_pid_max), ==, 0);
5144
pid_t nonexisting_pid = ((pid_t)pid_max)+1;
5145
g_assert_true(nonexisting_pid > 0); /* Overflow check */
5147
__attribute__((cleanup(cleanup_string)))
5148
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5149
g_assert_nonnull(tempfilename);
5150
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5151
g_assert_cmpint(questionfile, >, 0);
5152
FILE *qf = fdopen(questionfile, "w");
5153
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5154
PRIuMAX"\n", (uintmax_t)nonexisting_pid),
5156
g_assert_cmpint(fclose(qf), ==, 0);
5158
char *const question_filename = strdup(tempfilename);
5159
g_assert_nonnull(question_filename);
5160
task_context task = {
5161
.func=open_and_parse_question,
5162
.question_filename=question_filename,
5164
.password=&password,
5165
.filename=question_filename,
5166
.cancelled_filenames=&cancelled_filenames,
5167
.current_time=¤t_time,
5168
.mandos_client_exited=&mandos_client_exited,
5169
.password_is_read=&password_is_read,
5171
run_task_with_stderr_to_dev_null(task, queue);
5172
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5174
g_assert_cmpint(unlink(tempfilename), ==, 0);
5178
test_open_and_parse_question_no_notafter(__attribute__((unused))
5179
test_fixture *fixture,
5180
__attribute__((unused))
5181
gconstpointer user_data){
5182
__attribute__((cleanup(cleanup_close)))
5183
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5184
g_assert_cmpint(epoll_fd, >=, 0);
5185
__attribute__((cleanup(string_set_clear)))
5186
string_set cancelled_filenames = {};
5187
buffer password = {};
5188
bool mandos_client_exited = false;
5189
bool password_is_read = false;
5190
__attribute__((cleanup(cleanup_queue)))
5191
task_queue *queue = create_queue();
5192
g_assert_nonnull(queue);
5193
const mono_microsecs current_time = 0;
5195
__attribute__((cleanup(cleanup_string)))
5196
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5197
g_assert_nonnull(tempfilename);
5198
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5199
g_assert_cmpint(questionfile, >, 0);
5200
FILE *qf = fdopen(questionfile, "w");
5201
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5202
PRIuMAX "\n", (uintmax_t)getpid()), >, 0);
5203
g_assert_cmpint(fclose(qf), ==, 0);
5205
char *const filename = strdup(tempfilename);
5206
g_assert_nonnull(filename);
5207
task_context task = {
5208
.func=open_and_parse_question,
5209
.question_filename=filename,
5211
.password=&password,
5213
.cancelled_filenames=&cancelled_filenames,
5214
.current_time=¤t_time,
5215
.mandos_client_exited=&mandos_client_exited,
5216
.password_is_read=&password_is_read,
5218
task.func(task, queue);
5219
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5221
__attribute__((cleanup(cleanup_string)))
5222
char *socket_filename = strdup("/nonexistent");
5223
g_assert_nonnull(socket_filename);
5224
g_assert_nonnull(find_matching_task(queue, (task_context){
5225
.func=connect_question_socket,
5226
.question_filename=tempfilename,
5227
.filename=socket_filename,
5229
.password=&password,
5230
.current_time=¤t_time,
5231
.mandos_client_exited=&mandos_client_exited,
5232
.password_is_read=&password_is_read,
5235
g_assert_true(queue->next_run != 0);
5237
g_assert_cmpint(unlink(tempfilename), ==, 0);
5241
test_open_and_parse_question_bad_notafter(__attribute__((unused))
5242
test_fixture *fixture,
5243
__attribute__((unused))
5244
gconstpointer user_data){
5245
__attribute__((cleanup(cleanup_close)))
5246
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5247
g_assert_cmpint(epoll_fd, >=, 0);
5248
__attribute__((cleanup(string_set_clear)))
5249
string_set cancelled_filenames = {};
5250
buffer password = {};
5251
bool mandos_client_exited = false;
5252
bool password_is_read = false;
5253
__attribute__((cleanup(cleanup_queue)))
5254
task_queue *queue = create_queue();
5255
g_assert_nonnull(queue);
5256
const mono_microsecs current_time = 0;
5258
__attribute__((cleanup(cleanup_string)))
5259
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5260
g_assert_nonnull(tempfilename);
5261
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5262
g_assert_cmpint(questionfile, >, 0);
5263
FILE *qf = fdopen(questionfile, "w");
5264
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5265
PRIuMAX "\nNotAfter=\n",
5266
(uintmax_t)getpid()), >, 0);
5267
g_assert_cmpint(fclose(qf), ==, 0);
5269
char *const filename = strdup(tempfilename);
5270
g_assert_nonnull(filename);
5271
task_context task = {
5272
.func=open_and_parse_question,
5273
.question_filename=filename,
5275
.password=&password,
5277
.cancelled_filenames=&cancelled_filenames,
5278
.current_time=¤t_time,
5279
.mandos_client_exited=&mandos_client_exited,
5280
.password_is_read=&password_is_read,
5282
run_task_with_stderr_to_dev_null(task, queue);
5283
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5285
__attribute__((cleanup(cleanup_string)))
5286
char *socket_filename = strdup("/nonexistent");
5287
g_assert_nonnull(find_matching_task(queue, (task_context){
5288
.func=connect_question_socket,
5289
.question_filename=tempfilename,
5290
.filename=socket_filename,
5292
.password=&password,
5293
.current_time=¤t_time,
5294
.mandos_client_exited=&mandos_client_exited,
5295
.password_is_read=&password_is_read,
5297
g_assert_true(queue->next_run != 0);
5299
g_assert_cmpint(unlink(tempfilename), ==, 0);
5303
void assert_open_and_parse_question_with_notafter(const mono_microsecs
5305
const mono_microsecs
5307
const mono_microsecs
5309
__attribute__((cleanup(cleanup_close)))
5310
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5311
g_assert_cmpint(epoll_fd, >=, 0);
5312
__attribute__((cleanup(string_set_clear)))
5313
string_set cancelled_filenames = {};
5314
buffer password = {};
5315
bool mandos_client_exited = false;
5316
bool password_is_read = false;
5317
__attribute__((cleanup(cleanup_queue)))
5318
task_queue *queue = create_queue();
5319
g_assert_nonnull(queue);
5320
queue->next_run = next_queue_run;
5322
__attribute__((cleanup(cleanup_string)))
5323
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5324
g_assert_nonnull(tempfilename);
5325
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5326
g_assert_cmpint(questionfile, >, 0);
5327
FILE *qf = fdopen(questionfile, "w");
5328
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5329
PRIuMAX "\nNotAfter=%" PRIuMAX "\n",
5330
(uintmax_t)getpid(), notafter), >, 0);
5331
g_assert_cmpint(fclose(qf), ==, 0);
5333
char *const filename = strdup(tempfilename);
5334
g_assert_nonnull(filename);
5335
task_context task = {
5336
.func=open_and_parse_question,
5337
.question_filename=filename,
5339
.password=&password,
5341
.cancelled_filenames=&cancelled_filenames,
5342
.current_time=¤t_time,
5343
.mandos_client_exited=&mandos_client_exited,
5344
.password_is_read=&password_is_read,
5346
task.func(task, queue);
5348
if(queue->length >= 1){
5349
__attribute__((cleanup(cleanup_string)))
5350
char *socket_filename = strdup("/nonexistent");
5351
g_assert_nonnull(find_matching_task(queue, (task_context){
5352
.func=connect_question_socket,
5353
.filename=socket_filename,
5355
.password=&password,
5356
.current_time=¤t_time,
5357
.cancelled_filenames=&cancelled_filenames,
5358
.mandos_client_exited=&mandos_client_exited,
5359
.password_is_read=&password_is_read,
5361
g_assert_true(queue->next_run != 0);
5365
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5366
} else if(current_time >= notafter) {
5367
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5369
g_assert_nonnull(find_matching_task(queue, (task_context){
5370
.func=cancel_old_question,
5371
.question_filename=tempfilename,
5372
.filename=tempfilename,
5374
.cancelled_filenames=&cancelled_filenames,
5375
.current_time=¤t_time,
5378
g_assert_true(queue->next_run == 1);
5380
g_assert_cmpint(unlink(tempfilename), ==, 0);
5384
test_open_and_parse_question_notafter_0(__attribute__((unused))
5385
test_fixture *fixture,
5386
__attribute__((unused))
5387
gconstpointer user_data){
5388
/* current_time, notafter, next_queue_run */
5389
assert_open_and_parse_question_with_notafter(0, 0, 0);
5393
test_open_and_parse_question_notafter_1(__attribute__((unused))
5394
test_fixture *fixture,
5395
__attribute__((unused))
5396
gconstpointer user_data){
5397
/* current_time, notafter, next_queue_run */
5398
assert_open_and_parse_question_with_notafter(0, 1, 0);
5402
test_open_and_parse_question_notafter_1_1(__attribute__((unused))
5403
test_fixture *fixture,
5404
__attribute__((unused))
5405
gconstpointer user_data){
5406
/* current_time, notafter, next_queue_run */
5407
assert_open_and_parse_question_with_notafter(0, 1, 1);
5411
test_open_and_parse_question_notafter_1_2(__attribute__((unused))
5412
test_fixture *fixture,
5413
__attribute__((unused))
5414
gconstpointer user_data){
5415
/* current_time, notafter, next_queue_run */
5416
assert_open_and_parse_question_with_notafter(0, 1, 2);
5420
test_open_and_parse_question_equal_notafter(__attribute__((unused))
5421
test_fixture *fixture,
5422
__attribute__((unused))
5423
gconstpointer user_data){
5424
/* current_time, notafter, next_queue_run */
5425
assert_open_and_parse_question_with_notafter(1, 1, 0);
5429
test_open_and_parse_question_late_notafter(__attribute__((unused))
5430
test_fixture *fixture,
5431
__attribute__((unused))
5432
gconstpointer user_data){
5433
/* current_time, notafter, next_queue_run */
5434
assert_open_and_parse_question_with_notafter(2, 1, 0);
5437
static void assert_cancel_old_question_param(const mono_microsecs
5439
const mono_microsecs
5441
const mono_microsecs
5443
const mono_microsecs
5445
__attribute__((cleanup(string_set_clear)))
5446
string_set cancelled_filenames = {};
5447
__attribute__((cleanup(cleanup_queue)))
5448
task_queue *queue = create_queue();
5449
g_assert_nonnull(queue);
5450
queue->next_run = next_queue_run;
5452
char *const question_filename = strdup("/nonexistent");
5453
g_assert_nonnull(question_filename);
5454
task_context task = {
5455
.func=cancel_old_question,
5456
.question_filename=question_filename,
5457
.filename=question_filename,
5459
.cancelled_filenames=&cancelled_filenames,
5460
.current_time=¤t_time,
5462
task.func(task, queue);
5464
if(current_time >= notafter){
5465
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5466
g_assert_true(string_set_contains(cancelled_filenames,
5469
g_assert_nonnull(find_matching_task(queue, (task_context){
5470
.func=cancel_old_question,
5471
.question_filename=question_filename,
5472
.filename=question_filename,
5474
.cancelled_filenames=&cancelled_filenames,
5475
.current_time=¤t_time,
5478
g_assert_false(string_set_contains(cancelled_filenames,
5479
question_filename));
5481
g_assert_cmpuint((unsigned int)queue->next_run, ==,
5482
(unsigned int)next_set_to);
5485
static void test_cancel_old_question_0_1_2(__attribute__((unused))
5486
test_fixture *fixture,
5487
__attribute__((unused))
5488
gconstpointer user_data){
5489
/* next_queue_run unset,
5490
cancellation should happen because time has come,
5491
next_queue_run should be unchanged */
5492
/* next_queue_run, notafter, current_time, next_set_to */
5493
assert_cancel_old_question_param(0, 1, 2, 0);
5496
static void test_cancel_old_question_0_2_1(__attribute__((unused))
5497
test_fixture *fixture,
5498
__attribute__((unused))
5499
gconstpointer user_data){
5500
/* If next_queue_run is 0, meaning unset, and notafter is 2,
5501
and current_time is not yet notafter or greater,
5502
update value of next_queue_run to value of notafter */
5503
/* next_queue_run, notafter, current_time, next_set_to */
5504
assert_cancel_old_question_param(0, 2, 1, 2);
5507
static void test_cancel_old_question_1_2_3(__attribute__((unused))
5508
test_fixture *fixture,
5509
__attribute__((unused))
5510
gconstpointer user_data){
5511
/* next_queue_run 1,
5512
cancellation should happen because time has come,
5513
next_queue_run should be unchanged */
5514
/* next_queue_run, notafter, current_time, next_set_to */
5515
assert_cancel_old_question_param(1, 2, 3, 1);
5518
static void test_cancel_old_question_1_3_2(__attribute__((unused))
5519
test_fixture *fixture,
5520
__attribute__((unused))
5521
gconstpointer user_data){
5522
/* If next_queue_run is set,
5523
and current_time is not yet notafter or greater,
5524
and notafter is larger than next_queue_run
5525
next_queue_run should be unchanged */
5526
/* next_queue_run, notafter, current_time, next_set_to */
5527
assert_cancel_old_question_param(1, 3, 2, 1);
5530
static void test_cancel_old_question_2_1_3(__attribute__((unused))
5531
test_fixture *fixture,
5532
__attribute__((unused))
5533
gconstpointer user_data){
5534
/* next_queue_run 2,
5535
cancellation should happen because time has come,
5536
next_queue_run should be unchanged */
5537
/* next_queue_run, notafter, current_time, next_set_to */
5538
assert_cancel_old_question_param(2, 1, 3, 2);
5541
static void test_cancel_old_question_2_3_1(__attribute__((unused))
5542
test_fixture *fixture,
5543
__attribute__((unused))
5544
gconstpointer user_data){
5545
/* If next_queue_run is set,
5546
and current_time is not yet notafter or greater,
5547
and notafter is larger than next_queue_run
5548
next_queue_run should be unchanged */
5549
/* next_queue_run, notafter, current_time, next_set_to */
5550
assert_cancel_old_question_param(2, 3, 1, 2);
5553
static void test_cancel_old_question_3_1_2(__attribute__((unused))
5554
test_fixture *fixture,
5555
__attribute__((unused))
5556
gconstpointer user_data){
5557
/* next_queue_run 3,
5558
cancellation should happen because time has come,
5559
next_queue_run should be unchanged */
5560
/* next_queue_run, notafter, current_time, next_set_to */
5561
assert_cancel_old_question_param(3, 1, 2, 3);
5564
static void test_cancel_old_question_3_2_1(__attribute__((unused))
5565
test_fixture *fixture,
5566
__attribute__((unused))
5567
gconstpointer user_data){
5568
/* If next_queue_run is set,
5569
and current_time is not yet notafter or greater,
5570
and notafter is smaller than next_queue_run
5571
update value of next_queue_run to value of notafter */
5572
/* next_queue_run, notafter, current_time, next_set_to */
5573
assert_cancel_old_question_param(3, 2, 1, 2);
5577
test_connect_question_socket_name_too_long(__attribute__((unused))
5578
test_fixture *fixture,
5579
__attribute__((unused))
5580
gconstpointer user_data){
5581
__attribute__((cleanup(cleanup_close)))
5582
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5583
g_assert_cmpint(epoll_fd, >=, 0);
5584
const char question_filename[] = "/nonexistent/question";
5585
__attribute__((cleanup(string_set_clear)))
5586
string_set cancelled_filenames = {};
5587
__attribute__((cleanup(cleanup_queue)))
5588
task_queue *queue = create_queue();
5589
g_assert_nonnull(queue);
5590
__attribute__((cleanup(cleanup_string)))
5591
char *tempdir = make_temporary_directory();
5592
g_assert_nonnull(tempdir);
5593
struct sockaddr_un unix_socket = { .sun_family=AF_LOCAL };
5594
char socket_name[sizeof(unix_socket.sun_path)];
5595
memset(socket_name, 'x', sizeof(socket_name));
5596
socket_name[sizeof(socket_name)-1] = '\0';
5597
char *filename = NULL;
5598
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5600
g_assert_nonnull(filename);
5602
task_context task = {
5603
.func=connect_question_socket,
5604
.question_filename=strdup(question_filename),
5606
.password=(buffer[]){{}},
5608
.cancelled_filenames=&cancelled_filenames,
5609
.mandos_client_exited=(bool[]){false},
5610
.password_is_read=(bool[]){false},
5611
.current_time=(mono_microsecs[]){0},
5613
g_assert_nonnull(task.question_filename);
5614
run_task_with_stderr_to_dev_null(task, queue);
5616
g_assert_true(string_set_contains(cancelled_filenames,
5617
question_filename));
5618
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5619
g_assert_true(queue->next_run == 0);
5621
g_assert_cmpint(rmdir(tempdir), ==, 0);
5625
void test_connect_question_socket_connect_fail(__attribute__((unused))
5626
test_fixture *fixture,
5627
__attribute__((unused))
5630
__attribute__((cleanup(cleanup_close)))
5631
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5632
g_assert_cmpint(epoll_fd, >=, 0);
5633
const char question_filename[] = "/nonexistent/question";
5634
__attribute__((cleanup(string_set_clear)))
5635
string_set cancelled_filenames = {};
5636
const mono_microsecs current_time = 3;
5637
__attribute__((cleanup(cleanup_queue)))
5638
task_queue *queue = create_queue();
5639
g_assert_nonnull(queue);
5640
__attribute__((cleanup(cleanup_string)))
5641
char *tempdir = make_temporary_directory();
5642
g_assert_nonnull(tempdir);
5643
char socket_name[] = "nonexistent";
5644
char *filename = NULL;
5645
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5647
g_assert_nonnull(filename);
5649
task_context task = {
5650
.func=connect_question_socket,
5651
.question_filename=strdup(question_filename),
5653
.password=(buffer[]){{}},
5655
.cancelled_filenames=&cancelled_filenames,
5656
.mandos_client_exited=(bool[]){false},
5657
.password_is_read=(bool[]){false},
5658
.current_time=¤t_time,
5660
g_assert_nonnull(task.question_filename);
5661
run_task_with_stderr_to_dev_null(task, queue);
5663
g_assert_nonnull(find_matching_task(queue, task));
5665
g_assert_false(string_set_contains(cancelled_filenames,
5666
question_filename));
5667
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5668
g_assert_true(queue->next_run == 1000000 + current_time);
5670
g_assert_cmpint(rmdir(tempdir), ==, 0);
5674
void test_connect_question_socket_bad_epoll(__attribute__((unused))
5675
test_fixture *fixture,
5676
__attribute__((unused))
5677
gconstpointer user_data){
5678
__attribute__((cleanup(cleanup_close)))
5679
const int epoll_fd = open("/dev/null",
5680
O_WRONLY | O_CLOEXEC | O_NOCTTY);
5681
__attribute__((cleanup(cleanup_string)))
5682
char *const question_filename = strdup("/nonexistent/question");
5683
g_assert_nonnull(question_filename);
5684
__attribute__((cleanup(string_set_clear)))
5685
string_set cancelled_filenames = {};
5686
const mono_microsecs current_time = 5;
5687
__attribute__((cleanup(cleanup_queue)))
5688
task_queue *queue = create_queue();
5689
g_assert_nonnull(queue);
5690
__attribute__((cleanup(cleanup_string)))
5691
char *tempdir = make_temporary_directory();
5692
g_assert_nonnull(tempdir);
5693
__attribute__((cleanup(cleanup_close)))
5694
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5695
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5696
g_assert_cmpint(sock_fd, >=, 0);
5697
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5698
const char socket_name[] = "socket_name";
5699
__attribute__((cleanup(cleanup_string)))
5700
char *filename = NULL;
5701
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5703
g_assert_nonnull(filename);
5704
g_assert_cmpint((int)strlen(filename), <,
5705
(int)sizeof(sock_name.sun_path));
5706
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5707
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5708
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5709
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5710
task_context task = {
5711
.func=connect_question_socket,
5712
.question_filename=strdup(question_filename),
5714
.password=(buffer[]){{}},
5715
.filename=strdup(filename),
5716
.cancelled_filenames=&cancelled_filenames,
5717
.mandos_client_exited=(bool[]){false},
5718
.password_is_read=(bool[]){false},
5719
.current_time=¤t_time,
5721
g_assert_nonnull(task.question_filename);
5722
run_task_with_stderr_to_dev_null(task, queue);
5724
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5725
const task_context *const added_task
5726
= find_matching_task(queue, task);
5727
g_assert_nonnull(added_task);
5728
g_assert_true(queue->next_run == 1000000 + current_time);
5730
g_assert_cmpint(unlink(filename), ==, 0);
5731
g_assert_cmpint(rmdir(tempdir), ==, 0);
5735
void test_connect_question_socket_usable(__attribute__((unused))
5736
test_fixture *fixture,
5737
__attribute__((unused))
5738
gconstpointer user_data){
5739
__attribute__((cleanup(cleanup_close)))
5740
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5741
g_assert_cmpint(epoll_fd, >=, 0);
5742
__attribute__((cleanup(cleanup_string)))
5743
char *const question_filename = strdup("/nonexistent/question");
5744
g_assert_nonnull(question_filename);
5745
__attribute__((cleanup(string_set_clear)))
5746
string_set cancelled_filenames = {};
5747
buffer password = {};
5748
bool mandos_client_exited = false;
5749
bool password_is_read = false;
5750
const mono_microsecs current_time = 0;
5751
__attribute__((cleanup(cleanup_queue)))
5752
task_queue *queue = create_queue();
5753
g_assert_nonnull(queue);
5754
__attribute__((cleanup(cleanup_string)))
5755
char *tempdir = make_temporary_directory();
5756
g_assert_nonnull(tempdir);
5757
__attribute__((cleanup(cleanup_close)))
5758
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5759
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5760
g_assert_cmpint(sock_fd, >=, 0);
5761
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5762
const char socket_name[] = "socket_name";
5763
__attribute__((cleanup(cleanup_string)))
5764
char *filename = NULL;
5765
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5767
g_assert_nonnull(filename);
5768
g_assert_cmpint((int)strlen(filename), <,
5769
(int)sizeof(sock_name.sun_path));
5770
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5771
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5772
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5773
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5774
task_context task = {
5775
.func=connect_question_socket,
5776
.question_filename=strdup(question_filename),
5778
.password=&password,
5779
.filename=strdup(filename),
5780
.cancelled_filenames=&cancelled_filenames,
5781
.mandos_client_exited=&mandos_client_exited,
5782
.password_is_read=&password_is_read,
5783
.current_time=¤t_time,
5785
g_assert_nonnull(task.question_filename);
5786
task.func(task, queue);
5788
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5789
const task_context *const added_task
5790
= find_matching_task(queue, (task_context){
5791
.func=send_password_to_socket,
5792
.question_filename=question_filename,
5795
.password=&password,
5796
.cancelled_filenames=&cancelled_filenames,
5797
.mandos_client_exited=&mandos_client_exited,
5798
.password_is_read=&password_is_read,
5799
.current_time=¤t_time,
5801
g_assert_nonnull(added_task);
5802
g_assert_cmpint(added_task->fd, >, 0);
5804
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5807
const int fd = added_task->fd;
5808
g_assert_cmpint(fd, >, 0);
5809
g_assert_true(fd_has_cloexec_and_nonblock(fd));
5812
char write_data[PIPE_BUF];
5814
/* Construct test password buffer */
5815
/* Start with + since that is what the real protocol uses */
5816
write_data[0] = '+';
5817
/* Set a special character at string end just to mark the end */
5818
write_data[sizeof(write_data)-2] = 'y';
5819
/* Set NUL at buffer end, as suggested by the protocol */
5820
write_data[sizeof(write_data)-1] = '\0';
5821
/* Fill rest of password with 'x' */
5822
memset(write_data+1, 'x', sizeof(write_data)-3);
5823
g_assert_cmpint((int)send(fd, write_data, sizeof(write_data),
5824
MSG_NOSIGNAL), ==, sizeof(write_data));
5827
/* read from sock_fd */
5828
char read_data[sizeof(write_data)];
5829
g_assert_cmpint((int)read(sock_fd, read_data, sizeof(read_data)),
5830
==, sizeof(read_data));
5832
g_assert_true(memcmp(write_data, read_data, sizeof(write_data))
5835
/* writing to sock_fd should fail */
5836
g_assert_cmpint(send(sock_fd, write_data, sizeof(write_data),
5837
MSG_NOSIGNAL), <, 0);
5839
/* reading from fd should fail */
5840
g_assert_cmpint((int)recv(fd, read_data, sizeof(read_data),
5841
MSG_NOSIGNAL), <, 0);
5843
g_assert_cmpint(unlink(filename), ==, 0);
5844
g_assert_cmpint(rmdir(tempdir), ==, 0);
5848
test_send_password_to_socket_client_not_exited(__attribute__((unused))
5849
test_fixture *fixture,
5850
__attribute__((unused))
5853
__attribute__((cleanup(cleanup_close)))
5854
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5855
g_assert_cmpint(epoll_fd, >=, 0);
5856
__attribute__((cleanup(cleanup_string)))
5857
char *const question_filename = strdup("/nonexistent/question");
5858
g_assert_nonnull(question_filename);
5859
__attribute__((cleanup(cleanup_string)))
5860
char *const filename = strdup("/nonexistent/socket");
5861
g_assert_nonnull(filename);
5862
__attribute__((cleanup(string_set_clear)))
5863
string_set cancelled_filenames = {};
5864
buffer password = {};
5865
bool password_is_read = true;
5866
__attribute__((cleanup(cleanup_queue)))
5867
task_queue *queue = create_queue();
5868
g_assert_nonnull(queue);
5870
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5871
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5873
__attribute__((cleanup(cleanup_close)))
5874
const int read_socket = socketfds[0];
5875
const int write_socket = socketfds[1];
5876
task_context task = {
5877
.func=send_password_to_socket,
5878
.question_filename=strdup(question_filename),
5879
.filename=strdup(filename),
5882
.password=&password,
5883
.cancelled_filenames=&cancelled_filenames,
5884
.mandos_client_exited=(bool[]){false},
5885
.password_is_read=&password_is_read,
5886
.current_time=(mono_microsecs[]){0},
5888
g_assert_nonnull(task.question_filename);
5890
task.func(task, queue);
5892
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5894
const task_context *const added_task
5895
= find_matching_task(queue, task);
5896
g_assert_nonnull(added_task);
5897
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5898
g_assert_true(password_is_read);
5900
g_assert_cmpint(added_task->fd, >, 0);
5901
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5906
test_send_password_to_socket_password_not_read(__attribute__((unused))
5907
test_fixture *fixture,
5908
__attribute__((unused))
5911
__attribute__((cleanup(cleanup_close)))
5912
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5913
g_assert_cmpint(epoll_fd, >=, 0);
5914
__attribute__((cleanup(cleanup_string)))
5915
char *const question_filename = strdup("/nonexistent/question");
5916
g_assert_nonnull(question_filename);
5917
__attribute__((cleanup(cleanup_string)))
5918
char *const filename = strdup("/nonexistent/socket");
5919
__attribute__((cleanup(string_set_clear)))
5920
string_set cancelled_filenames = {};
5921
buffer password = {};
5922
__attribute__((cleanup(cleanup_queue)))
5923
task_queue *queue = create_queue();
5924
g_assert_nonnull(queue);
5926
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5927
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5929
__attribute__((cleanup(cleanup_close)))
5930
const int read_socket = socketfds[0];
5931
const int write_socket = socketfds[1];
5932
task_context task = {
5933
.func=send_password_to_socket,
5934
.question_filename=strdup(question_filename),
5935
.filename=strdup(filename),
5938
.password=&password,
5939
.cancelled_filenames=&cancelled_filenames,
5940
.mandos_client_exited=(bool[]){false},
5941
.password_is_read=(bool[]){false},
5942
.current_time=(mono_microsecs[]){0},
5944
g_assert_nonnull(task.question_filename);
5946
task.func(task, queue);
5948
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5950
const task_context *const added_task = find_matching_task(queue,
5952
g_assert_nonnull(added_task);
5953
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5954
g_assert_true(queue->next_run == 0);
5956
g_assert_cmpint(added_task->fd, >, 0);
5957
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5962
void test_send_password_to_socket_EMSGSIZE(__attribute__((unused))
5963
test_fixture *fixture,
5964
__attribute__((unused))
5965
gconstpointer user_data){
5966
__attribute__((cleanup(cleanup_close)))
5967
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5968
g_assert_cmpint(epoll_fd, >=, 0);
5969
const char question_filename[] = "/nonexistent/question";
5970
char *const filename = strdup("/nonexistent/socket");
5971
__attribute__((cleanup(string_set_clear)))
5972
string_set cancelled_filenames = {};
5975
/* Find a message size which triggers EMSGSIZE */
5976
__attribute__((cleanup(cleanup_string)))
5977
char *message_buffer = NULL;
5978
size_t message_size = PIPE_BUF + 1;
5979
for(ssize_t ssret = 0; ssret >= 0; message_size += 1024){
5980
if(message_size >= 1024*1024*1024){ /* 1 GiB */
5981
g_test_skip("Skipping EMSGSIZE test: Will not try 1GiB");
5984
message_buffer = realloc(message_buffer, message_size);
5985
if(message_buffer == NULL){
5986
g_test_skip("Skipping EMSGSIZE test");
5987
g_test_message("Failed to malloc() %" PRIuMAX " bytes",
5988
(uintmax_t)message_size);
5991
/* Fill buffer with 'x' */
5992
memset(message_buffer, 'x', message_size);
5993
/* Create a new socketpair for each message size to avoid having
5994
to empty the pipe by reading the message to a separate buffer
5996
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5997
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5999
ssret = send(socketfds[1], message_buffer, message_size,
6001
error_t saved_errno = errno;
6002
g_assert_cmpint(close(socketfds[0]), ==, 0);
6003
g_assert_cmpint(close(socketfds[1]), ==, 0);
6006
if(saved_errno != EMSGSIZE) {
6007
g_test_skip("Skipping EMSGSIZE test");
6008
g_test_message("Error on send(%" PRIuMAX " bytes): %s",
6009
(uintmax_t)message_size,
6010
strerror(saved_errno));
6014
} else if(ssret != (ssize_t)message_size){
6015
g_test_skip("Skipping EMSGSIZE test");
6016
g_test_message("Partial send(): %" PRIuMAX " of %" PRIdMAX
6017
" bytes", (uintmax_t)ssret,
6018
(intmax_t)message_size);
6022
g_test_message("EMSGSIZE triggered by %" PRIdMAX " bytes",
6023
(intmax_t)message_size);
6026
.data=message_buffer,
6027
.length=message_size - 2, /* Compensate for added '+' and NUL */
6028
.allocated=message_size,
6030
if(mlock(password.data, password.allocated) != 0){
6031
g_assert_true(errno == EPERM or errno == ENOMEM);
6034
__attribute__((cleanup(cleanup_queue)))
6035
task_queue *queue = create_queue();
6036
g_assert_nonnull(queue);
6037
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6038
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6040
__attribute__((cleanup(cleanup_close)))
6041
const int read_socket = socketfds[0];
6042
__attribute__((cleanup(cleanup_close)))
6043
const int write_socket = socketfds[1];
6044
task_context task = {
6045
.func=send_password_to_socket,
6046
.question_filename=strdup(question_filename),
6050
.password=&password,
6051
.cancelled_filenames=&cancelled_filenames,
6052
.mandos_client_exited=(bool[]){true},
6053
.password_is_read=(bool[]){true},
6054
.current_time=(mono_microsecs[]){0},
6056
g_assert_nonnull(task.question_filename);
6058
run_task_with_stderr_to_dev_null(task, queue);
6060
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6061
g_assert_true(string_set_contains(cancelled_filenames,
6062
question_filename));
6065
static void test_send_password_to_socket_retry(__attribute__((unused))
6066
test_fixture *fixture,
6067
__attribute__((unused))
6070
__attribute__((cleanup(cleanup_close)))
6071
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6072
g_assert_cmpint(epoll_fd, >=, 0);
6073
__attribute__((cleanup(cleanup_string)))
6074
char *const question_filename = strdup("/nonexistent/question");
6075
g_assert_nonnull(question_filename);
6076
__attribute__((cleanup(cleanup_string)))
6077
char *const filename = strdup("/nonexistent/socket");
6078
g_assert_nonnull(filename);
6079
__attribute__((cleanup(string_set_clear)))
6080
string_set cancelled_filenames = {};
6081
__attribute__((cleanup(cleanup_buffer)))
6082
buffer password = {};
6084
__attribute__((cleanup(cleanup_queue)))
6085
task_queue *queue = create_queue();
6086
g_assert_nonnull(queue);
6088
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6089
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6091
__attribute__((cleanup(cleanup_close)))
6092
const int read_socket = socketfds[0];
6093
const int write_socket = socketfds[1];
6094
/* Close the server side socket to force ECONNRESET on client */
6095
g_assert_cmpint(close(read_socket), ==, 0);
6096
task_context task = {
6097
.func=send_password_to_socket,
6098
.question_filename=strdup(question_filename),
6099
.filename=strdup(filename),
6102
.password=&password,
6103
.cancelled_filenames=&cancelled_filenames,
6104
.mandos_client_exited=(bool[]){true},
6105
.password_is_read=(bool[]){true},
6106
.current_time=(mono_microsecs[]){0},
6108
g_assert_nonnull(task.question_filename);
6110
task.func(task, queue);
6112
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6114
const task_context *const added_task = find_matching_task(queue,
6116
g_assert_nonnull(added_task);
6117
g_assert_cmpuint((unsigned int)password.length, ==, 0);
6119
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
6124
void test_send_password_to_socket_bad_epoll(__attribute__((unused))
6125
test_fixture *fixture,
6126
__attribute__((unused))
6127
gconstpointer user_data){
6128
__attribute__((cleanup(cleanup_close)))
6129
const int epoll_fd = open("/dev/null",
6130
O_WRONLY | O_CLOEXEC | O_NOCTTY);
6131
__attribute__((cleanup(cleanup_string)))
6132
char *const question_filename = strdup("/nonexistent/question");
6133
g_assert_nonnull(question_filename);
6134
__attribute__((cleanup(cleanup_string)))
6135
char *const filename = strdup("/nonexistent/socket");
6136
g_assert_nonnull(filename);
6137
__attribute__((cleanup(string_set_clear)))
6138
string_set cancelled_filenames = {};
6139
__attribute__((cleanup(cleanup_buffer)))
6140
buffer password = {};
6142
const mono_microsecs current_time = 11;
6143
__attribute__((cleanup(cleanup_queue)))
6144
task_queue *queue = create_queue();
6145
g_assert_nonnull(queue);
6147
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6148
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6150
__attribute__((cleanup(cleanup_close)))
6151
const int read_socket = socketfds[0];
6152
const int write_socket = socketfds[1];
6153
/* Close the server side socket to force ECONNRESET on client */
6154
g_assert_cmpint(close(read_socket), ==, 0);
6155
task_context task = {
6156
.func=send_password_to_socket,
6157
.question_filename=strdup(question_filename),
6158
.filename=strdup(filename),
6161
.password=&password,
6162
.cancelled_filenames=&cancelled_filenames,
6163
.mandos_client_exited=(bool[]){true},
6164
.password_is_read=(bool[]){true},
6165
.current_time=¤t_time,
6167
g_assert_nonnull(task.question_filename);
6169
run_task_with_stderr_to_dev_null(task, queue);
6171
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6173
const task_context *const added_task = find_matching_task(queue,
6175
g_assert_nonnull(added_task);
6176
g_assert_true(queue->next_run == current_time + 1000000);
6177
g_assert_cmpuint((unsigned int)password.length, ==, 0);
6180
static void assert_send_password_to_socket_password(buffer password){
6181
__attribute__((cleanup(cleanup_close)))
6182
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6183
g_assert_cmpint(epoll_fd, >=, 0);
6184
char *const question_filename = strdup("/nonexistent/question");
6185
g_assert_nonnull(question_filename);
6186
char *const filename = strdup("/nonexistent/socket");
6187
g_assert_nonnull(filename);
6188
__attribute__((cleanup(string_set_clear)))
6189
string_set cancelled_filenames = {};
6191
__attribute__((cleanup(cleanup_queue)))
6192
task_queue *queue = create_queue();
6193
g_assert_nonnull(queue);
6195
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6196
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6198
__attribute__((cleanup(cleanup_close)))
6199
const int read_socket = socketfds[0];
6200
const int write_socket = socketfds[1];
6201
task_context task = {
6202
.func=send_password_to_socket,
6203
.question_filename=question_filename,
6207
.password=&password,
6208
.cancelled_filenames=&cancelled_filenames,
6209
.mandos_client_exited=(bool[]){true},
6210
.password_is_read=(bool[]){true},
6211
.current_time=(mono_microsecs[]){0},
6214
char *expected_written_data = malloc(password.length + 2);
6215
g_assert_nonnull(expected_written_data);
6216
expected_written_data[0] = '+';
6217
expected_written_data[password.length + 1] = '\0';
6218
if(password.length > 0){
6219
g_assert_nonnull(password.data);
6220
memcpy(expected_written_data + 1, password.data, password.length);
6223
task.func(task, queue);
6226
g_assert_cmpint((int)read(read_socket, buf, PIPE_BUF), ==,
6227
(int)(password.length + 2));
6228
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6230
g_assert_true(memcmp(expected_written_data, buf,
6231
password.length + 2) == 0);
6233
g_assert_true(epoll_set_does_not_contain(epoll_fd, write_socket));
6235
free(expected_written_data);
6239
test_send_password_to_socket_null_password(__attribute__((unused))
6240
test_fixture *fixture,
6241
__attribute__((unused))
6242
gconstpointer user_data){
6243
__attribute__((cleanup(cleanup_buffer)))
6244
buffer password = {};
6245
assert_send_password_to_socket_password(password);
6249
test_send_password_to_socket_empty_password(__attribute__((unused))
6250
test_fixture *fixture,
6251
__attribute__((unused))
6252
gconstpointer user_data){
6253
__attribute__((cleanup(cleanup_buffer)))
6255
.data=malloc(1), /* because malloc(0) may return NULL */
6257
.allocated=0, /* deliberate lie */
6259
g_assert_nonnull(password.data);
6260
assert_send_password_to_socket_password(password);
6264
test_send_password_to_socket_empty_str_pass(__attribute__((unused))
6265
test_fixture *fixture,
6266
__attribute__((unused))
6267
gconstpointer user_data){
6268
__attribute__((cleanup(cleanup_buffer)))
6274
if(mlock(password.data, password.allocated) != 0){
6275
g_assert_true(errno == EPERM or errno == ENOMEM);
6277
assert_send_password_to_socket_password(password);
6281
test_send_password_to_socket_text_password(__attribute__((unused))
6282
test_fixture *fixture,
6283
__attribute__((unused))
6284
gconstpointer user_data){
6285
const char dummy_test_password[] = "dummy test password";
6286
__attribute__((cleanup(cleanup_buffer)))
6288
.data = strdup(dummy_test_password),
6289
.length = strlen(dummy_test_password),
6290
.allocated = sizeof(dummy_test_password),
6292
if(mlock(password.data, password.allocated) != 0){
6293
g_assert_true(errno == EPERM or errno == ENOMEM);
6295
assert_send_password_to_socket_password(password);
6299
test_send_password_to_socket_binary_password(__attribute__((unused))
6300
test_fixture *fixture,
6301
__attribute__((unused))
6302
gconstpointer user_data){
6303
__attribute__((cleanup(cleanup_buffer)))
6309
g_assert_nonnull(password.data);
6310
if(mlock(password.data, password.allocated) != 0){
6311
g_assert_true(errno == EPERM or errno == ENOMEM);
6313
char c = 1; /* Start at 1, avoiding NUL */
6314
for(int i=0; i < 255; i++){
6315
password.data[i] = c++;
6317
assert_send_password_to_socket_password(password);
6321
test_send_password_to_socket_nuls_in_password(__attribute__((unused))
6322
test_fixture *fixture,
6323
__attribute__((unused))
6326
char test_password[] = {'\0', 'a', '\0', 'b', '\0', 'c', '\0'};
6327
__attribute__((cleanup(cleanup_buffer)))
6329
.data=malloc(sizeof(test_password)),
6330
.length=sizeof(test_password),
6331
.allocated=sizeof(test_password),
6333
g_assert_nonnull(password.data);
6334
if(mlock(password.data, password.allocated) !=0){
6335
g_assert_true(errno == EPERM or errno == ENOMEM);
6337
memcpy(password.data, test_password, password.allocated);
6338
assert_send_password_to_socket_password(password);
6341
static bool assert_add_existing_questions_to_devnull(task_queue
6354
static void test_add_existing_questions_ENOENT(__attribute__((unused))
6355
test_fixture *fixture,
6356
__attribute__((unused))
6359
__attribute__((cleanup(cleanup_queue)))
6360
task_queue *queue = create_queue();
6361
g_assert_nonnull(queue);
6362
__attribute__((cleanup(cleanup_close)))
6363
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6364
g_assert_cmpint(epoll_fd, >=, 0);
6365
__attribute__((cleanup(string_set_clear)))
6366
string_set cancelled_filenames = {};
6368
g_assert_false(assert_add_existing_questions_to_devnull
6371
(buffer[]){{}}, /* password */
6372
&cancelled_filenames,
6373
(mono_microsecs[]){0}, /* current_time */
6374
(bool[]){false}, /* mandos_client_exited */
6375
(bool[]){false}, /* password_is_read */
6376
"/nonexistent")); /* dirname */
6378
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6382
bool assert_add_existing_questions_to_devnull(task_queue
6389
*cancelled_filenames,
6390
const mono_microsecs
6391
*const current_time,
6393
mandos_client_exited,
6398
__attribute__((cleanup(cleanup_close)))
6399
const int devnull_fd = open("/dev/null",
6400
O_WRONLY | O_CLOEXEC | O_NOCTTY);
6401
g_assert_cmpint(devnull_fd, >=, 0);
6402
__attribute__((cleanup(cleanup_close)))
6403
const int real_stderr_fd = dup(STDERR_FILENO);
6404
g_assert_cmpint(real_stderr_fd, >=, 0);
6405
dup2(devnull_fd, STDERR_FILENO);
6406
const bool ret = add_existing_questions(queue, epoll_fd, password,
6407
cancelled_filenames,
6409
mandos_client_exited,
6410
password_is_read, dirname);
6411
dup2(real_stderr_fd, STDERR_FILENO);
6416
void test_add_existing_questions_no_questions(__attribute__((unused))
6417
test_fixture *fixture,
6418
__attribute__((unused))
6421
__attribute__((cleanup(cleanup_queue)))
6422
task_queue *queue = create_queue();
6423
g_assert_nonnull(queue);
6424
__attribute__((cleanup(cleanup_close)))
6425
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6426
g_assert_cmpint(epoll_fd, >=, 0);
6427
__attribute__((cleanup(string_set_clear)))
6428
string_set cancelled_filenames = {};
6429
__attribute__((cleanup(cleanup_string)))
6430
char *tempdir = make_temporary_directory();
6431
g_assert_nonnull(tempdir);
6433
g_assert_false(assert_add_existing_questions_to_devnull
6436
(buffer[]){{}}, /* password */
6437
&cancelled_filenames,
6438
(mono_microsecs[]){0}, /* current_time */
6439
(bool[]){false}, /* mandos_client_exited */
6440
(bool[]){false}, /* password_is_read */
6443
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6445
g_assert_cmpint(rmdir(tempdir), ==, 0);
6448
static char *make_question_file_in_directory(const char *const);
6451
void test_add_existing_questions_one_question(__attribute__((unused))
6452
test_fixture *fixture,
6453
__attribute__((unused))
6456
__attribute__((cleanup(cleanup_queue)))
6457
task_queue *queue = create_queue();
6458
g_assert_nonnull(queue);
6459
__attribute__((cleanup(cleanup_close)))
6460
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6461
g_assert_cmpint(epoll_fd, >=, 0);
6462
__attribute__((cleanup(cleanup_buffer)))
6463
buffer password = {};
6464
__attribute__((cleanup(string_set_clear)))
6465
string_set cancelled_filenames = {};
6466
const mono_microsecs current_time = 0;
6467
bool mandos_client_exited = false;
6468
bool password_is_read = false;
6469
__attribute__((cleanup(cleanup_string)))
6470
char *tempdir = make_temporary_directory();
6471
g_assert_nonnull(tempdir);
6472
__attribute__((cleanup(cleanup_string)))
6473
char *question_filename
6474
= make_question_file_in_directory(tempdir);
6475
g_assert_nonnull(question_filename);
6477
g_assert_true(assert_add_existing_questions_to_devnull
6481
&cancelled_filenames,
6483
&mandos_client_exited,
6487
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6489
g_assert_nonnull(find_matching_task(queue, (task_context){
6490
.func=open_and_parse_question,
6492
.filename=question_filename,
6493
.question_filename=question_filename,
6494
.password=&password,
6495
.cancelled_filenames=&cancelled_filenames,
6496
.current_time=¤t_time,
6497
.mandos_client_exited=&mandos_client_exited,
6498
.password_is_read=&password_is_read,
6501
g_assert_true(queue->next_run == 1);
6503
g_assert_cmpint(unlink(question_filename), ==, 0);
6504
g_assert_cmpint(rmdir(tempdir), ==, 0);
6507
static char *make_question_file_in_directory(const char
6509
return make_temporary_prefixed_file_in_directory("ask.", dir);
6513
void test_add_existing_questions_two_questions(__attribute__((unused))
6514
test_fixture *fixture,
6515
__attribute__((unused))
6518
__attribute__((cleanup(cleanup_queue)))
6519
task_queue *queue = create_queue();
6520
g_assert_nonnull(queue);
6521
__attribute__((cleanup(cleanup_close)))
6522
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6523
g_assert_cmpint(epoll_fd, >=, 0);
6524
__attribute__((cleanup(cleanup_buffer)))
6525
buffer password = {};
6526
__attribute__((cleanup(string_set_clear)))
6527
string_set cancelled_filenames = {};
6528
const mono_microsecs current_time = 0;
6529
bool mandos_client_exited = false;
6530
bool password_is_read = false;
6531
__attribute__((cleanup(cleanup_string)))
6532
char *tempdir = make_temporary_directory();
6533
g_assert_nonnull(tempdir);
6534
__attribute__((cleanup(cleanup_string)))
6535
char *question_filename1
6536
= make_question_file_in_directory(tempdir);
6537
g_assert_nonnull(question_filename1);
6538
__attribute__((cleanup(cleanup_string)))
6539
char *question_filename2
6540
= make_question_file_in_directory(tempdir);
6541
g_assert_nonnull(question_filename2);
6543
g_assert_true(assert_add_existing_questions_to_devnull
6547
&cancelled_filenames,
6549
&mandos_client_exited,
6553
g_assert_cmpuint((unsigned int)queue->length, ==, 2);
6555
g_assert_true(queue->next_run == 1);
6557
__attribute__((cleanup(string_set_clear)))
6558
string_set seen_questions = {};
6560
bool queue_contains_question_opener(char *const question_filename){
6561
return(find_matching_task(queue, (task_context){
6562
.func=open_and_parse_question,
6564
.question_filename=question_filename,
6565
.password=&password,
6566
.cancelled_filenames=&cancelled_filenames,
6567
.current_time=¤t_time,
6568
.mandos_client_exited=&mandos_client_exited,
6569
.password_is_read=&password_is_read,
6573
g_assert_true(queue_contains_question_opener(question_filename1));
6574
g_assert_true(queue_contains_question_opener(question_filename2));
6576
g_assert_true(queue->next_run == 1);
6578
g_assert_cmpint(unlink(question_filename1), ==, 0);
6579
g_assert_cmpint(unlink(question_filename2), ==, 0);
6580
g_assert_cmpint(rmdir(tempdir), ==, 0);
6584
test_add_existing_questions_non_questions(__attribute__((unused))
6585
test_fixture *fixture,
6586
__attribute__((unused))
6587
gconstpointer user_data){
6588
__attribute__((cleanup(cleanup_queue)))
6589
task_queue *queue = create_queue();
6590
g_assert_nonnull(queue);
6591
__attribute__((cleanup(cleanup_close)))
6592
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6593
g_assert_cmpint(epoll_fd, >=, 0);
6594
__attribute__((cleanup(string_set_clear)))
6595
string_set cancelled_filenames = {};
6596
__attribute__((cleanup(cleanup_string)))
6597
char *tempdir = make_temporary_directory();
6598
g_assert_nonnull(tempdir);
6599
__attribute__((cleanup(cleanup_string)))
6600
char *question_filename1
6601
= make_temporary_file_in_directory(tempdir);
6602
g_assert_nonnull(question_filename1);
6603
__attribute__((cleanup(cleanup_string)))
6604
char *question_filename2
6605
= make_temporary_file_in_directory(tempdir);
6606
g_assert_nonnull(question_filename2);
6608
g_assert_false(assert_add_existing_questions_to_devnull
6611
(buffer[]){{}}, /* password */
6612
&cancelled_filenames,
6613
(mono_microsecs[]){0}, /* current_time */
6614
(bool[]){false}, /* mandos_client_exited */
6615
(bool[]){false}, /* password_is_read */
6618
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6620
g_assert_cmpint(unlink(question_filename1), ==, 0);
6621
g_assert_cmpint(unlink(question_filename2), ==, 0);
6622
g_assert_cmpint(rmdir(tempdir), ==, 0);
6626
test_add_existing_questions_both_types(__attribute__((unused))
6627
test_fixture *fixture,
6628
__attribute__((unused))
6629
gconstpointer user_data){
6630
__attribute__((cleanup(cleanup_queue)))
6631
task_queue *queue = create_queue();
6632
g_assert_nonnull(queue);
6633
__attribute__((cleanup(cleanup_close)))
6634
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6635
g_assert_cmpint(epoll_fd, >=, 0);
6636
__attribute__((cleanup(cleanup_buffer)))
6637
buffer password = {};
6638
__attribute__((cleanup(string_set_clear)))
6639
string_set cancelled_filenames = {};
6640
const mono_microsecs current_time = 0;
6641
bool mandos_client_exited = false;
6642
bool password_is_read = false;
6643
__attribute__((cleanup(cleanup_string)))
6644
char *tempdir = make_temporary_directory();
6645
g_assert_nonnull(tempdir);
6646
__attribute__((cleanup(cleanup_string)))
6647
char *tempfilename1 = make_temporary_file_in_directory(tempdir);
6648
g_assert_nonnull(tempfilename1);
6649
__attribute__((cleanup(cleanup_string)))
6650
char *tempfilename2 = make_temporary_file_in_directory(tempdir);
6651
g_assert_nonnull(tempfilename2);
6652
__attribute__((cleanup(cleanup_string)))
6653
char *question_filename
6654
= make_question_file_in_directory(tempdir);
6655
g_assert_nonnull(question_filename);
6657
g_assert_true(assert_add_existing_questions_to_devnull
6661
&cancelled_filenames,
6663
&mandos_client_exited,
6667
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6669
g_assert_nonnull(find_matching_task(queue, (task_context){
6670
.func=open_and_parse_question,
6672
.filename=question_filename,
6673
.question_filename=question_filename,
6674
.password=&password,
6675
.cancelled_filenames=&cancelled_filenames,
6676
.current_time=¤t_time,
6677
.mandos_client_exited=&mandos_client_exited,
6678
.password_is_read=&password_is_read,
6681
g_assert_true(queue->next_run == 1);
6683
g_assert_cmpint(unlink(tempfilename1), ==, 0);
6684
g_assert_cmpint(unlink(tempfilename2), ==, 0);
6685
g_assert_cmpint(unlink(question_filename), ==, 0);
6686
g_assert_cmpint(rmdir(tempdir), ==, 0);
6689
static void test_wait_for_event_timeout(__attribute__((unused))
6690
test_fixture *fixture,
6691
__attribute__((unused))
6692
gconstpointer user_data){
6693
__attribute__((cleanup(cleanup_close)))
6694
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6695
g_assert_cmpint(epoll_fd, >=, 0);
6697
g_assert_true(wait_for_event(epoll_fd, 1, 0));
6700
static void test_wait_for_event_event(__attribute__((unused))
6701
test_fixture *fixture,
6702
__attribute__((unused))
6703
gconstpointer user_data){
6704
__attribute__((cleanup(cleanup_close)))
6705
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6706
g_assert_cmpint(epoll_fd, >=, 0);
6708
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6709
__attribute__((cleanup(cleanup_close)))
6710
const int read_pipe = pipefds[0];
6711
__attribute__((cleanup(cleanup_close)))
6712
const int write_pipe = pipefds[1];
6713
g_assert_cmpint(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, read_pipe,
6714
&(struct epoll_event)
6715
{ .events=EPOLLIN | EPOLLRDHUP }), ==, 0);
6716
g_assert_cmpint((int)write(write_pipe, "x", 1), ==, 1);
6718
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6721
static void test_wait_for_event_sigchld(test_fixture *fixture,
6722
__attribute__((unused))
6723
gconstpointer user_data){
6724
const pid_t pid = fork();
6725
if(pid == 0){ /* Child */
6726
if(not restore_signal_handler(&fixture->orig_sigaction)){
6727
_exit(EXIT_FAILURE);
6729
if(not restore_sigmask(&fixture->orig_sigmask)){
6730
_exit(EXIT_FAILURE);
6734
g_assert_true(pid != -1);
6735
__attribute__((cleanup(cleanup_close)))
6736
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6737
g_assert_cmpint(epoll_fd, >=, 0);
6739
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6742
g_assert_true(waitpid(pid, &status, 0) == pid);
6743
g_assert_true(WIFEXITED(status));
6744
g_assert_cmpint(WEXITSTATUS(status), ==, EXIT_SUCCESS);
6747
static void test_run_queue_zeroes_next_run(__attribute__((unused))
6748
test_fixture *fixture,
6749
__attribute__((unused))
6750
gconstpointer user_data){
6751
__attribute__((cleanup(cleanup_queue)))
6752
task_queue *queue = create_queue();
6753
g_assert_nonnull(queue);
6754
queue->next_run = 1;
6755
__attribute__((cleanup(cleanup_close)))
6756
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6757
__attribute__((cleanup(string_set_clear)))
6758
string_set cancelled_filenames = {};
6759
bool quit_now = false;
6761
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6762
g_assert_false(quit_now);
6763
g_assert_cmpuint((unsigned int)queue->next_run, ==, 0);
6767
void test_run_queue_clears_cancelled_filenames(__attribute__((unused))
6768
test_fixture *fixture,
6769
__attribute__((unused))
6772
__attribute__((cleanup(cleanup_queue)))
6773
task_queue *queue = create_queue();
6774
g_assert_nonnull(queue);
6775
__attribute__((cleanup(string_set_clear)))
6776
string_set cancelled_filenames = {};
6777
bool quit_now = false;
6778
const char question_filename[] = "/nonexistent/question_filename";
6779
g_assert_true(string_set_add(&cancelled_filenames,
6780
question_filename));
6782
g_assert_true(add_to_queue(queue,
6783
(task_context){ .func=dummy_func }));
6785
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6786
g_assert_false(quit_now);
6787
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6788
g_assert_false(string_set_contains(cancelled_filenames,
6789
question_filename));
6793
void test_run_queue_skips_cancelled_filenames(__attribute__((unused))
6794
test_fixture *fixture,
6795
__attribute__((unused))
6798
__attribute__((cleanup(cleanup_queue)))
6799
task_queue *queue = create_queue();
6800
g_assert_nonnull(queue);
6801
__attribute__((cleanup(string_set_clear)))
6802
string_set cancelled_filenames = {};
6803
bool quit_now = false;
6805
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6806
__attribute__((cleanup(cleanup_close)))
6807
const int read_pipe = pipefds[0];
6808
g_assert_cmpint(close(pipefds[1]), ==, 0);
6809
const char question_filename[] = "/nonexistent/question_filename";
6810
g_assert_true(string_set_add(&cancelled_filenames,
6811
question_filename));
6812
__attribute__((nonnull))
6813
void quit_func(const task_context task,
6814
__attribute__((unused)) task_queue *const q){
6815
g_assert_nonnull(task.quit_now);
6816
*task.quit_now = true;
6818
task_context task = {
6820
.question_filename=strdup(question_filename),
6821
.quit_now=&quit_now,
6824
g_assert_nonnull(task.question_filename);
6826
g_assert_true(add_to_queue(queue, task));
6828
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6829
g_assert_false(quit_now);
6831
/* read_pipe should be closed already */
6833
bool read_pipe_closed = (close(read_pipe) == -1);
6834
read_pipe_closed &= (errno == EBADF);
6835
g_assert_true(read_pipe_closed);
6838
static void test_run_queue_one_task(__attribute__((unused))
6839
test_fixture *fixture,
6840
__attribute__((unused))
6841
gconstpointer user_data){
6842
__attribute__((cleanup(cleanup_queue)))
6843
task_queue *queue = create_queue();
6844
g_assert_nonnull(queue);
6845
__attribute__((cleanup(string_set_clear)))
6846
string_set cancelled_filenames = {};
6847
bool quit_now = false;
6849
__attribute__((nonnull))
6850
void next_run_func(__attribute__((unused))
6851
const task_context task,
6852
task_queue *const q){
6856
task_context task = {
6857
.func=next_run_func,
6859
g_assert_true(add_to_queue(queue, task));
6861
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6862
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6863
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6866
static void test_run_queue_two_tasks(__attribute__((unused))
6867
test_fixture *fixture,
6868
__attribute__((unused))
6869
gconstpointer user_data){
6870
__attribute__((cleanup(cleanup_queue)))
6871
task_queue *queue = create_queue();
6872
g_assert_nonnull(queue);
6873
queue->next_run = 1;
6874
__attribute__((cleanup(string_set_clear)))
6875
string_set cancelled_filenames = {};
6876
bool quit_now = false;
6877
bool mandos_client_exited = false;
6879
__attribute__((nonnull))
6880
void next_run_func(__attribute__((unused))
6881
const task_context task,
6882
task_queue *const q){
6886
__attribute__((nonnull))
6887
void exited_func(const task_context task,
6888
__attribute__((unused)) task_queue *const q){
6889
*task.mandos_client_exited = true;
6892
task_context task1 = {
6893
.func=next_run_func,
6895
g_assert_true(add_to_queue(queue, task1));
6897
task_context task2 = {
6899
.mandos_client_exited=&mandos_client_exited,
6901
g_assert_true(add_to_queue(queue, task2));
6903
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6904
g_assert_false(quit_now);
6905
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6906
g_assert_true(mandos_client_exited);
6907
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6910
static void test_run_queue_two_tasks_quit(__attribute__((unused))
6911
test_fixture *fixture,
6912
__attribute__((unused))
6913
gconstpointer user_data){
6914
__attribute__((cleanup(cleanup_queue)))
6915
task_queue *queue = create_queue();
6916
g_assert_nonnull(queue);
6917
__attribute__((cleanup(string_set_clear)))
6918
string_set cancelled_filenames = {};
6919
bool quit_now = false;
6920
bool mandos_client_exited = false;
6921
bool password_is_read = false;
6923
__attribute__((nonnull))
6924
void set_exited_func(const task_context task,
6925
__attribute__((unused)) task_queue *const q){
6926
*task.mandos_client_exited = true;
6927
*task.quit_now = true;
6929
task_context task1 = {
6930
.func=set_exited_func,
6931
.quit_now=&quit_now,
6932
.mandos_client_exited=&mandos_client_exited,
6934
g_assert_true(add_to_queue(queue, task1));
6936
__attribute__((nonnull))
6937
void set_read_func(const task_context task,
6938
__attribute__((unused)) task_queue *const q){
6939
*task.quit_now = true;
6940
*task.password_is_read = true;
6942
task_context task2 = {
6943
.func=set_read_func,
6944
.quit_now=&quit_now,
6945
.password_is_read=&password_is_read,
6947
g_assert_true(add_to_queue(queue, task2));
6949
g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6950
g_assert_true(quit_now);
6951
g_assert_true(mandos_client_exited xor password_is_read);
6952
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6955
static void test_run_queue_two_tasks_cleanup(__attribute__((unused))
6956
test_fixture *fixture,
6957
__attribute__((unused))
6958
gconstpointer user_data){
6959
__attribute__((cleanup(cleanup_queue)))
6960
task_queue *queue = create_queue();
6961
g_assert_nonnull(queue);
6962
__attribute__((cleanup(string_set_clear)))
6963
string_set cancelled_filenames = {};
6965
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6966
__attribute__((cleanup(cleanup_close)))
6967
const int read_pipe = pipefds[0];
6968
__attribute__((cleanup(cleanup_close)))
6969
const int write_pipe = pipefds[1];
6970
bool quit_now = false;
6972
__attribute__((nonnull))
6973
void read_func(const task_context task,
6974
__attribute__((unused)) task_queue *const q){
6975
*task.quit_now = true;
6977
task_context task1 = {
6979
.quit_now=&quit_now,
6982
g_assert_true(add_to_queue(queue, task1));
6984
__attribute__((nonnull))
6985
void write_func(const task_context task,
6986
__attribute__((unused)) task_queue *const q){
6987
*task.quit_now = true;
6989
task_context task2 = {
6991
.quit_now=&quit_now,
6994
g_assert_true(add_to_queue(queue, task2));
6996
g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6997
g_assert_true(quit_now);
6999
/* Either read_pipe or write_pipe should be closed already */
7001
bool close_read_pipe = (close(read_pipe) == -1);
7002
close_read_pipe &= (errno == EBADF);
7004
bool close_write_pipe = (close(write_pipe) == -1);
7005
close_write_pipe &= (errno == EBADF);
7006
g_assert_true(close_read_pipe xor close_write_pipe);
7007
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
7010
static void test_setup_signal_handler(__attribute__((unused))
7011
test_fixture *fixture,
7012
__attribute__((unused))
7013
gconstpointer user_data){
7014
/* Save current SIGCHLD action, whatever it is */
7015
struct sigaction expected_sigchld_action;
7016
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
7019
/* Act; i.e. run the setup_signal_handler() function */
7020
struct sigaction actual_old_sigchld_action;
7021
g_assert_true(setup_signal_handler(&actual_old_sigchld_action));
7023
/* Check that the function correctly set "actual_old_sigchld_action"
7024
to the same values as the previously saved
7025
"expected_sigchld_action" */
7026
/* Check member sa_handler */
7027
g_assert_true(actual_old_sigchld_action.sa_handler
7028
== expected_sigchld_action.sa_handler);
7029
/* Check member sa_mask */
7030
for(int signum = 1; signum < NSIG; signum++){
7031
const int expected_old_block_state
7032
= sigismember(&expected_sigchld_action.sa_mask, signum);
7033
g_assert_cmpint(expected_old_block_state, >=, 0);
7034
const int actual_old_block_state
7035
= sigismember(&actual_old_sigchld_action.sa_mask, signum);
7036
g_assert_cmpint(actual_old_block_state, >=, 0);
7037
g_assert_cmpint(actual_old_block_state,
7038
==, expected_old_block_state);
7040
/* Check member sa_flags */
7041
g_assert_true((actual_old_sigchld_action.sa_flags
7042
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
7043
== (expected_sigchld_action.sa_flags
7044
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
7046
/* Retrieve the current signal handler for SIGCHLD as set by
7047
setup_signal_handler() */
7048
struct sigaction actual_new_sigchld_action;
7049
g_assert_cmpint(sigaction(SIGCHLD, NULL,
7050
&actual_new_sigchld_action), ==, 0);
7051
/* Check that the signal handler (member sa_handler) is correctly
7052
set to the "handle_sigchld" function */
7053
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_DFL);
7054
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_IGN);
7055
g_assert_true(actual_new_sigchld_action.sa_handler
7057
/* Check (in member sa_mask) that at least a handful of signals are
7058
actually blocked during the signal handler */
7059
for(int signum = 1; signum < NSIG; signum++){
7060
int actual_new_block_state;
7066
actual_new_block_state
7067
= sigismember(&actual_new_sigchld_action.sa_mask, signum);
7068
g_assert_cmpint(actual_new_block_state, ==, 1);
7070
case SIGKILL: /* non-blockable */
7071
case SIGSTOP: /* non-blockable */
7072
case SIGCHLD: /* always blocked */
7077
/* Check member sa_flags */
7078
g_assert_true((actual_new_sigchld_action.sa_flags
7079
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
7080
== (SA_NOCLDSTOP | SA_RESTART));
7082
/* Restore signal handler */
7083
g_assert_cmpint(sigaction(SIGCHLD, &expected_sigchld_action, NULL),
7087
static void test_restore_signal_handler(__attribute__((unused))
7088
test_fixture *fixture,
7089
__attribute__((unused))
7090
gconstpointer user_data){
7091
/* Save current SIGCHLD action, whatever it is */
7092
struct sigaction expected_sigchld_action;
7093
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
7095
/* Since we haven't established a signal handler yet, there should
7096
not be one established. But another test may have relied on
7097
restore_signal_handler() to restore the signal handler, and if
7098
restore_signal_handler() is buggy (which we should be prepared
7099
for in this test) the signal handler may not have been restored
7100
properly; check for this: */
7101
g_assert_true(expected_sigchld_action.sa_handler != handle_sigchld);
7103
/* Establish a signal handler */
7104
struct sigaction sigchld_action = {
7105
.sa_handler=handle_sigchld,
7106
.sa_flags=SA_RESTART | SA_NOCLDSTOP,
7108
g_assert_cmpint(sigfillset(&sigchld_action.sa_mask), ==, 0);
7109
g_assert_cmpint(sigaction(SIGCHLD, &sigchld_action, NULL), ==, 0);
7111
/* Act; i.e. run the restore_signal_handler() function */
7112
g_assert_true(restore_signal_handler(&expected_sigchld_action));
7114
/* Retrieve the restored signal handler data */
7115
struct sigaction actual_restored_sigchld_action;
7116
g_assert_cmpint(sigaction(SIGCHLD, NULL,
7117
&actual_restored_sigchld_action), ==, 0);
7119
/* Check that the function correctly restored the signal action, as
7120
saved in "actual_restored_sigchld_action", to the same values as
7121
the previously saved "expected_sigchld_action" */
7122
/* Check member sa_handler */
7123
g_assert_true(actual_restored_sigchld_action.sa_handler
7124
== expected_sigchld_action.sa_handler);
7125
/* Check member sa_mask */
7126
for(int signum = 1; signum < NSIG; signum++){
7127
const int expected_old_block_state
7128
= sigismember(&expected_sigchld_action.sa_mask, signum);
7129
g_assert_cmpint(expected_old_block_state, >=, 0);
7130
const int actual_restored_block_state
7131
= sigismember(&actual_restored_sigchld_action.sa_mask, signum);
7132
g_assert_cmpint(actual_restored_block_state, >=, 0);
7133
g_assert_cmpint(actual_restored_block_state,
7134
==, expected_old_block_state);
7136
/* Check member sa_flags */
7137
g_assert_true((actual_restored_sigchld_action.sa_flags
7138
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
7139
== (expected_sigchld_action.sa_flags
7140
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
7143
static void test_block_sigchld(__attribute__((unused))
7144
test_fixture *fixture,
7145
__attribute__((unused))
7146
gconstpointer user_data){
7147
/* Save original signal mask */
7148
sigset_t expected_sigmask;
7149
g_assert_cmpint(pthread_sigmask(-1, NULL, &expected_sigmask),
7152
/* Make sure SIGCHLD is unblocked for this test */
7153
sigset_t sigchld_sigmask;
7154
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
7155
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
7156
g_assert_cmpint(pthread_sigmask(SIG_UNBLOCK, &sigchld_sigmask,
7159
/* Act; i.e. run the block_sigchld() function */
7160
sigset_t actual_old_sigmask;
7161
g_assert_true(block_sigchld(&actual_old_sigmask));
7163
/* Check the actual_old_sigmask; it should be the same as the
7164
previously saved signal mask "expected_sigmask". */
7165
for(int signum = 1; signum < NSIG; signum++){
7166
const int expected_old_block_state
7167
= sigismember(&expected_sigmask, signum);
7168
g_assert_cmpint(expected_old_block_state, >=, 0);
7169
const int actual_old_block_state
7170
= sigismember(&actual_old_sigmask, signum);
7171
g_assert_cmpint(actual_old_block_state, >=, 0);
7172
g_assert_cmpint(actual_old_block_state,
7173
==, expected_old_block_state);
7176
/* Retrieve the newly set signal mask */
7177
sigset_t actual_sigmask;
7178
g_assert_cmpint(pthread_sigmask(-1, NULL, &actual_sigmask), ==, 0);
7180
/* SIGCHLD should be blocked */
7181
g_assert_cmpint(sigismember(&actual_sigmask, SIGCHLD), ==, 1);
7183
/* Restore signal mask */
7184
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &expected_sigmask,
7188
static void test_restore_sigmask(__attribute__((unused))
7189
test_fixture *fixture,
7190
__attribute__((unused))
7191
gconstpointer user_data){
7192
/* Save original signal mask */
7193
sigset_t orig_sigmask;
7194
g_assert_cmpint(pthread_sigmask(-1, NULL, &orig_sigmask), ==, 0);
7196
/* Make sure SIGCHLD is blocked for this test */
7197
sigset_t sigchld_sigmask;
7198
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
7199
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
7200
g_assert_cmpint(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask,
7203
/* Act; i.e. run the restore_sigmask() function */
7204
g_assert_true(restore_sigmask(&orig_sigmask));
7206
/* Retrieve the newly restored signal mask */
7207
sigset_t restored_sigmask;
7208
g_assert_cmpint(pthread_sigmask(-1, NULL, &restored_sigmask),
7211
/* Check the restored_sigmask; it should be the same as the
7212
previously saved signal mask "orig_sigmask". */
7213
for(int signum = 1; signum < NSIG; signum++){
7214
const int orig_block_state = sigismember(&orig_sigmask, signum);
7215
g_assert_cmpint(orig_block_state, >=, 0);
7216
const int restored_block_state = sigismember(&restored_sigmask,
7218
g_assert_cmpint(restored_block_state, >=, 0);
7219
g_assert_cmpint(restored_block_state, ==, orig_block_state);
7222
/* Restore signal mask */
7223
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &orig_sigmask,
7227
static void test_parse_arguments_noargs(__attribute__((unused))
7228
test_fixture *fixture,
7229
__attribute__((unused))
7230
gconstpointer user_data){
7234
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7236
char *agent_directory = NULL;
7237
char *helper_directory = NULL;
7240
char *mandos_argz = NULL;
7241
size_t mandos_argz_length = 0;
7243
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7244
&helper_directory, &user, &group,
7245
&mandos_argz, &mandos_argz_length));
7246
g_assert_null(agent_directory);
7247
g_assert_null(helper_directory);
7248
g_assert_true(user == 0);
7249
g_assert_true(group == 0);
7250
g_assert_null(mandos_argz);
7251
g_assert_true(mandos_argz_length == 0);
7253
for(char **arg = argv; *arg != NULL; arg++){
7258
__attribute__((nonnull))
7259
static bool parse_arguments_devnull(int argc, char *argv[],
7260
const bool exit_failure,
7261
char **agent_directory,
7262
char **helper_directory,
7266
size_t *mandos_argz_length){
7268
FILE *real_stderr = stderr;
7269
FILE *devnull = fopen("/dev/null", "we");
7270
g_assert_nonnull(devnull);
7273
const bool ret = parse_arguments(argc, argv, exit_failure,
7275
helper_directory, user, group,
7276
mandos_argz, mandos_argz_length);
7277
const error_t saved_errno = errno;
7279
stderr = real_stderr;
7280
g_assert_cmpint(fclose(devnull), ==, 0);
7282
errno = saved_errno;
7287
static void test_parse_arguments_invalid(__attribute__((unused))
7288
test_fixture *fixture,
7289
__attribute__((unused))
7290
gconstpointer user_data){
7293
strdup("--invalid"),
7295
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7297
char *agent_directory = NULL;
7298
char *helper_directory = NULL;
7301
char *mandos_argz = NULL;
7302
size_t mandos_argz_length = 0;
7304
g_assert_false(parse_arguments_devnull(argc, argv, false,
7306
&helper_directory, &user,
7307
&group, &mandos_argz,
7308
&mandos_argz_length));
7310
g_assert_true(errno == EINVAL);
7311
g_assert_null(agent_directory);
7312
g_assert_null(helper_directory);
7313
g_assert_null(mandos_argz);
7314
g_assert_true(mandos_argz_length == 0);
7316
for(char **arg = argv; *arg != NULL; arg++){
7321
static void test_parse_arguments_long_dir(__attribute__((unused))
7322
test_fixture *fixture,
7323
__attribute__((unused))
7324
gconstpointer user_data){
7327
strdup("--agent-directory"),
7330
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7332
__attribute__((cleanup(cleanup_string)))
7333
char *agent_directory = NULL;
7334
char *helper_directory = NULL;
7337
__attribute__((cleanup(cleanup_string)))
7338
char *mandos_argz = NULL;
7339
size_t mandos_argz_length = 0;
7341
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7342
&helper_directory, &user, &group,
7343
&mandos_argz, &mandos_argz_length));
7345
g_assert_cmpstr(agent_directory, ==, "/tmp");
7346
g_assert_null(helper_directory);
7347
g_assert_true(user == 0);
7348
g_assert_true(group == 0);
7349
g_assert_null(mandos_argz);
7350
g_assert_true(mandos_argz_length == 0);
7352
for(char **arg = argv; *arg != NULL; arg++){
7357
static void test_parse_arguments_short_dir(__attribute__((unused))
7358
test_fixture *fixture,
7359
__attribute__((unused))
7360
gconstpointer user_data){
7366
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7368
__attribute__((cleanup(cleanup_string)))
7369
char *agent_directory = NULL;
7370
char *helper_directory = NULL;
7373
__attribute__((cleanup(cleanup_string)))
7374
char *mandos_argz = NULL;
7375
size_t mandos_argz_length = 0;
7377
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7378
&helper_directory, &user, &group,
7379
&mandos_argz, &mandos_argz_length));
7381
g_assert_cmpstr(agent_directory, ==, "/tmp");
7382
g_assert_null(helper_directory);
7383
g_assert_true(user == 0);
7384
g_assert_true(group == 0);
7385
g_assert_null(mandos_argz);
7386
g_assert_true(mandos_argz_length == 0);
7388
for(char **arg = argv; *arg != NULL; arg++){
7394
void test_parse_arguments_helper_directory(__attribute__((unused))
7395
test_fixture *fixture,
7396
__attribute__((unused))
7397
gconstpointer user_data){
7400
strdup("--helper-directory"),
7403
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7405
char *agent_directory = NULL;
7406
__attribute__((cleanup(cleanup_string)))
7407
char *helper_directory = NULL;
7410
__attribute__((cleanup(cleanup_string)))
7411
char *mandos_argz = NULL;
7412
size_t mandos_argz_length = 0;
7414
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7415
&helper_directory, &user, &group,
7416
&mandos_argz, &mandos_argz_length));
7418
g_assert_cmpstr(helper_directory, ==, "/tmp");
7419
g_assert_null(agent_directory);
7420
g_assert_true(user == 0);
7421
g_assert_true(group == 0);
7422
g_assert_null(mandos_argz);
7423
g_assert_true(mandos_argz_length == 0);
7425
for(char **arg = argv; *arg != NULL; arg++){
7431
void test_parse_arguments_plugin_helper_dir(__attribute__((unused))
7432
test_fixture *fixture,
7433
__attribute__((unused))
7434
gconstpointer user_data){
7437
strdup("--plugin-helper-dir"),
7440
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7442
char *agent_directory = NULL;
7443
__attribute__((cleanup(cleanup_string)))
7444
char *helper_directory = NULL;
7447
__attribute__((cleanup(cleanup_string)))
7448
char *mandos_argz = NULL;
7449
size_t mandos_argz_length = 0;
7451
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7452
&helper_directory, &user, &group,
7453
&mandos_argz, &mandos_argz_length));
7455
g_assert_cmpstr(helper_directory, ==, "/tmp");
7456
g_assert_null(agent_directory);
7457
g_assert_true(user == 0);
7458
g_assert_true(group == 0);
7459
g_assert_null(mandos_argz);
7460
g_assert_true(mandos_argz_length == 0);
7462
for(char **arg = argv; *arg != NULL; arg++){
7467
static void test_parse_arguments_user(__attribute__((unused))
7468
test_fixture *fixture,
7469
__attribute__((unused))
7470
gconstpointer user_data){
7476
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7478
char *agent_directory = NULL;
7479
__attribute__((cleanup(cleanup_string)))
7480
char *helper_directory = NULL;
7483
__attribute__((cleanup(cleanup_string)))
7484
char *mandos_argz = NULL;
7485
size_t mandos_argz_length = 0;
7487
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7488
&helper_directory, &user, &group,
7489
&mandos_argz, &mandos_argz_length));
7491
g_assert_null(helper_directory);
7492
g_assert_null(agent_directory);
7493
g_assert_cmpuint((unsigned int)user, ==, 1000);
7494
g_assert_true(group == 0);
7495
g_assert_null(mandos_argz);
7496
g_assert_true(mandos_argz_length == 0);
7498
for(char **arg = argv; *arg != NULL; arg++){
7503
static void test_parse_arguments_user_invalid(__attribute__((unused))
7504
test_fixture *fixture,
7505
__attribute__((unused))
7513
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7515
char *agent_directory = NULL;
7516
__attribute__((cleanup(cleanup_string)))
7517
char *helper_directory = NULL;
7520
__attribute__((cleanup(cleanup_string)))
7521
char *mandos_argz = NULL;
7522
size_t mandos_argz_length = 0;
7524
g_assert_false(parse_arguments_devnull(argc, argv, false,
7526
&helper_directory, &user,
7527
&group, &mandos_argz,
7528
&mandos_argz_length));
7530
g_assert_null(helper_directory);
7531
g_assert_null(agent_directory);
7532
g_assert_cmpuint((unsigned int)user, ==, 0);
7533
g_assert_true(group == 0);
7534
g_assert_null(mandos_argz);
7535
g_assert_true(mandos_argz_length == 0);
7537
for(char **arg = argv; *arg != NULL; arg++){
7543
void test_parse_arguments_user_zero_invalid(__attribute__((unused))
7544
test_fixture *fixture,
7545
__attribute__((unused))
7546
gconstpointer user_data){
7552
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7554
char *agent_directory = NULL;
7555
__attribute__((cleanup(cleanup_string)))
7556
char *helper_directory = NULL;
7559
__attribute__((cleanup(cleanup_string)))
7560
char *mandos_argz = NULL;
7561
size_t mandos_argz_length = 0;
7563
g_assert_false(parse_arguments_devnull(argc, argv, false,
7565
&helper_directory, &user,
7566
&group, &mandos_argz,
7567
&mandos_argz_length));
7569
g_assert_null(helper_directory);
7570
g_assert_null(agent_directory);
7571
g_assert_cmpuint((unsigned int)user, ==, 0);
7572
g_assert_true(group == 0);
7573
g_assert_null(mandos_argz);
7574
g_assert_true(mandos_argz_length == 0);
7576
for(char **arg = argv; *arg != NULL; arg++){
7581
static void test_parse_arguments_group(__attribute__((unused))
7582
test_fixture *fixture,
7583
__attribute__((unused))
7584
gconstpointer user_data){
7590
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7592
char *agent_directory = NULL;
7593
__attribute__((cleanup(cleanup_string)))
7594
char *helper_directory = NULL;
7597
__attribute__((cleanup(cleanup_string)))
7598
char *mandos_argz = NULL;
7599
size_t mandos_argz_length = 0;
7601
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7602
&helper_directory, &user, &group,
7603
&mandos_argz, &mandos_argz_length));
7605
g_assert_null(helper_directory);
7606
g_assert_null(agent_directory);
7607
g_assert_true(user == 0);
7608
g_assert_cmpuint((unsigned int)group, ==, 1000);
7609
g_assert_null(mandos_argz);
7610
g_assert_true(mandos_argz_length == 0);
7612
for(char **arg = argv; *arg != NULL; arg++){
7617
static void test_parse_arguments_group_invalid(__attribute__((unused))
7618
test_fixture *fixture,
7619
__attribute__((unused))
7627
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7629
char *agent_directory = NULL;
7630
__attribute__((cleanup(cleanup_string)))
7631
char *helper_directory = NULL;
7634
__attribute__((cleanup(cleanup_string)))
7635
char *mandos_argz = NULL;
7636
size_t mandos_argz_length = 0;
7638
g_assert_false(parse_arguments_devnull(argc, argv, false,
7640
&helper_directory, &user,
7641
&group, &mandos_argz,
7642
&mandos_argz_length));
7644
g_assert_null(helper_directory);
7645
g_assert_null(agent_directory);
7646
g_assert_true(user == 0);
7647
g_assert_true(group == 0);
7648
g_assert_null(mandos_argz);
7649
g_assert_true(mandos_argz_length == 0);
7651
for(char **arg = argv; *arg != NULL; arg++){
7657
void test_parse_arguments_group_zero_invalid(__attribute__((unused))
7658
test_fixture *fixture,
7659
__attribute__((unused))
7660
gconstpointer user_data){
7666
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7668
char *agent_directory = NULL;
7669
__attribute__((cleanup(cleanup_string)))
7670
char *helper_directory = NULL;
7673
__attribute__((cleanup(cleanup_string)))
7674
char *mandos_argz = NULL;
7675
size_t mandos_argz_length = 0;
7677
g_assert_false(parse_arguments_devnull(argc, argv, false,
7679
&helper_directory, &user,
7680
&group, &mandos_argz,
7681
&mandos_argz_length));
7683
g_assert_null(helper_directory);
7684
g_assert_null(agent_directory);
7685
g_assert_cmpuint((unsigned int)group, ==, 0);
7686
g_assert_true(group == 0);
7687
g_assert_null(mandos_argz);
7688
g_assert_true(mandos_argz_length == 0);
7690
for(char **arg = argv; *arg != NULL; arg++){
7695
static void test_parse_arguments_mandos_noargs(__attribute__((unused))
7696
test_fixture *fixture,
7697
__attribute__((unused))
7702
strdup("mandos-client"),
7704
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7706
__attribute__((cleanup(cleanup_string)))
7707
char *agent_directory = NULL;
7708
__attribute__((cleanup(cleanup_string)))
7709
char *helper_directory = NULL;
7712
__attribute__((cleanup(cleanup_string)))
7713
char *mandos_argz = NULL;
7714
size_t mandos_argz_length = 0;
7716
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7717
&helper_directory, &user, &group,
7718
&mandos_argz, &mandos_argz_length));
7720
g_assert_null(agent_directory);
7721
g_assert_null(helper_directory);
7722
g_assert_true(user == 0);
7723
g_assert_true(group == 0);
7724
g_assert_cmpstr(mandos_argz, ==, "mandos-client");
7725
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7726
mandos_argz_length),
7729
for(char **arg = argv; *arg != NULL; arg++){
7734
static void test_parse_arguments_mandos_args(__attribute__((unused))
7735
test_fixture *fixture,
7736
__attribute__((unused))
7737
gconstpointer user_data){
7740
strdup("mandos-client"),
7745
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7747
__attribute__((cleanup(cleanup_string)))
7748
char *agent_directory = NULL;
7749
__attribute__((cleanup(cleanup_string)))
7750
char *helper_directory = NULL;
7753
__attribute__((cleanup(cleanup_string)))
7754
char *mandos_argz = NULL;
7755
size_t mandos_argz_length = 0;
7757
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7758
&helper_directory, &user, &group,
7759
&mandos_argz, &mandos_argz_length));
7761
g_assert_null(agent_directory);
7762
g_assert_null(helper_directory);
7763
g_assert_true(user == 0);
7764
g_assert_true(group == 0);
7765
char *marg = mandos_argz;
7766
g_assert_cmpstr(marg, ==, "mandos-client");
7767
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7768
g_assert_cmpstr(marg, ==, "one");
7769
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7770
g_assert_cmpstr(marg, ==, "two");
7771
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7772
g_assert_cmpstr(marg, ==, "three");
7773
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7774
mandos_argz_length),
7777
for(char **arg = argv; *arg != NULL; arg++){
7782
static void test_parse_arguments_all_args(__attribute__((unused))
7783
test_fixture *fixture,
7784
__attribute__((unused))
7785
gconstpointer user_data){
7788
strdup("--agent-directory"),
7790
strdup("--helper-directory"),
7796
strdup("mandos-client"),
7801
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7803
__attribute__((cleanup(cleanup_string)))
7804
char *agent_directory = NULL;
7805
__attribute__((cleanup(cleanup_string)))
7806
char *helper_directory = NULL;
7809
__attribute__((cleanup(cleanup_string)))
7810
char *mandos_argz = NULL;
7811
size_t mandos_argz_length = 0;
7813
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7814
&helper_directory, &user, &group,
7815
&mandos_argz, &mandos_argz_length));
7817
g_assert_cmpstr(agent_directory, ==, "/tmp");
7818
g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7819
g_assert_true(user == 1);
7820
g_assert_true(group == 2);
7821
char *marg = mandos_argz;
7822
g_assert_cmpstr(marg, ==, "mandos-client");
7823
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7824
g_assert_cmpstr(marg, ==, "one");
7825
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7826
g_assert_cmpstr(marg, ==, "two");
7827
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7828
g_assert_cmpstr(marg, ==, "three");
7829
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7830
mandos_argz_length),
7833
for(char **arg = argv; *arg != NULL; arg++){
7838
static void test_parse_arguments_mixed(__attribute__((unused))
7839
test_fixture *fixture,
7840
__attribute__((unused))
7841
gconstpointer user_data){
7844
strdup("mandos-client"),
7848
strdup("--agent-directory"),
7852
strdup("--helper-directory=/var/tmp"),
7854
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7856
__attribute__((cleanup(cleanup_string)))
7857
char *agent_directory = NULL;
7858
__attribute__((cleanup(cleanup_string)))
7859
char *helper_directory = NULL;
7862
__attribute__((cleanup(cleanup_string)))
7863
char *mandos_argz = NULL;
7864
size_t mandos_argz_length = 0;
7866
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7867
&helper_directory, &user, &group,
7868
&mandos_argz, &mandos_argz_length));
7870
g_assert_cmpstr(agent_directory, ==, "/tmp");
7871
g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7872
g_assert_true(user == 1);
7873
g_assert_true(group == 0);
7874
char *marg = mandos_argz;
7875
g_assert_cmpstr(marg, ==, "mandos-client");
7876
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7877
g_assert_cmpstr(marg, ==, "one");
7878
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7879
g_assert_cmpstr(marg, ==, "two");
7880
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7881
g_assert_cmpstr(marg, ==, "three");
7882
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7883
mandos_argz_length),
7886
for(char **arg = argv; *arg != NULL; arg++){
7891
/* End of tests section */
7893
/* Test boilerplate section; New tests should be added to the test
7894
suite definition here, in the "run_tests" function.
7896
Finally, this section also contains the should_only_run_tests()
7897
function used by main() for deciding if tests should be run or to
7900
__attribute__((cold))
7901
static bool run_tests(int argc, char *argv[]){
7902
g_test_init(&argc, &argv, NULL);
7904
/* A macro to add a test with no setup or teardown functions */
7905
#define test_add(testpath, testfunc) \
7907
g_test_add((testpath), test_fixture, NULL, NULL, \
7908
(testfunc), NULL); \
7911
/* Test the signal-related functions first, since some other tests
7912
depend on these functions in their setups and teardowns */
7913
test_add("/signal-handling/setup", test_setup_signal_handler);
7914
test_add("/signal-handling/restore", test_restore_signal_handler);
7915
test_add("/signal-handling/block", test_block_sigchld);
7916
test_add("/signal-handling/restore-sigmask", test_restore_sigmask);
7918
/* Regular non-signal-related tests; these use no setups or
7920
test_add("/parse_arguments/noargs", test_parse_arguments_noargs);
7921
test_add("/parse_arguments/invalid", test_parse_arguments_invalid);
7922
test_add("/parse_arguments/long-dir",
7923
test_parse_arguments_long_dir);
7924
test_add("/parse_arguments/short-dir",
7925
test_parse_arguments_short_dir);
7926
test_add("/parse_arguments/helper-directory",
7927
test_parse_arguments_helper_directory);
7928
test_add("/parse_arguments/plugin-helper-dir",
7929
test_parse_arguments_plugin_helper_dir);
7930
test_add("/parse_arguments/user", test_parse_arguments_user);
7931
test_add("/parse_arguments/user-invalid",
7932
test_parse_arguments_user_invalid);
7933
test_add("/parse_arguments/user-zero-invalid",
7934
test_parse_arguments_user_zero_invalid);
7935
test_add("/parse_arguments/group", test_parse_arguments_group);
7936
test_add("/parse_arguments/group-invalid",
7937
test_parse_arguments_group_invalid);
7938
test_add("/parse_arguments/group-zero-invalid",
7939
test_parse_arguments_group_zero_invalid);
7940
test_add("/parse_arguments/mandos-noargs",
7941
test_parse_arguments_mandos_noargs);
7942
test_add("/parse_arguments/mandos-args",
7943
test_parse_arguments_mandos_args);
7944
test_add("/parse_arguments/all-args",
7945
test_parse_arguments_all_args);
7946
test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7947
test_add("/queue/create", test_create_queue);
7948
test_add("/queue/add", test_add_to_queue);
7949
test_add("/queue/add/overflow", test_add_to_queue_overflow);
7950
test_add("/queue/has_question/empty",
7951
test_queue_has_question_empty);
7952
test_add("/queue/has_question/false",
7953
test_queue_has_question_false);
7954
test_add("/queue/has_question/true", test_queue_has_question_true);
7955
test_add("/queue/has_question/false2",
7956
test_queue_has_question_false2);
7957
test_add("/queue/has_question/true2",
7958
test_queue_has_question_true2);
7959
test_add("/buffer/cleanup", test_cleanup_buffer);
7960
test_add("/string_set/net-set-contains-nothing",
7961
test_string_set_new_set_contains_nothing);
7962
test_add("/string_set/with-added-string-contains-it",
7963
test_string_set_with_added_string_contains_it);
7964
test_add("/string_set/cleared-does-not-contain-string",
7965
test_string_set_cleared_does_not_contain_str);
7966
test_add("/string_set/swap/one-with-empty",
7967
test_string_set_swap_one_with_empty);
7968
test_add("/string_set/swap/empty-with-one",
7969
test_string_set_swap_empty_with_one);
7970
test_add("/string_set/swap/one-with-one",
7971
test_string_set_swap_one_with_one);
7973
/* A macro to add a test using the setup and teardown functions */
7974
#define test_add_st(path, func) \
7976
g_test_add((path), test_fixture, NULL, test_setup, (func), \
7980
/* Signal-related tests; these use setups and teardowns which
7981
establish, during each test run, a signal handler for, and a
7982
signal mask blocking, the SIGCHLD signal, just like main() */
7983
test_add_st("/wait_for_event/timeout", test_wait_for_event_timeout);
7984
test_add_st("/wait_for_event/event", test_wait_for_event_event);
7985
test_add_st("/wait_for_event/sigchld", test_wait_for_event_sigchld);
7986
test_add_st("/run_queue/zeroes-next-run",
7987
test_run_queue_zeroes_next_run);
7988
test_add_st("/run_queue/clears-cancelled_filenames",
7989
test_run_queue_clears_cancelled_filenames);
7990
test_add_st("/run_queue/skips-cancelled-filenames",
7991
test_run_queue_skips_cancelled_filenames);
7992
test_add_st("/run_queue/one-task", test_run_queue_one_task);
7993
test_add_st("/run_queue/two-tasks", test_run_queue_two_tasks);
7994
test_add_st("/run_queue/two-tasks/quit",
7995
test_run_queue_two_tasks_quit);
7996
test_add_st("/run_queue/two-tasks-cleanup",
7997
test_run_queue_two_tasks_cleanup);
7998
test_add_st("/task-creators/start_mandos_client",
7999
test_start_mandos_client);
8000
test_add_st("/task-creators/start_mandos_client/execv",
8001
test_start_mandos_client_execv);
8002
test_add_st("/task-creators/start_mandos_client/suid/euid",
8003
test_start_mandos_client_suid_euid);
8004
test_add_st("/task-creators/start_mandos_client/suid/egid",
8005
test_start_mandos_client_suid_egid);
8006
test_add_st("/task-creators/start_mandos_client/suid/ruid",
8007
test_start_mandos_client_suid_ruid);
8008
test_add_st("/task-creators/start_mandos_client/suid/rgid",
8009
test_start_mandos_client_suid_rgid);
8010
test_add_st("/task-creators/start_mandos_client/read",
8011
test_start_mandos_client_read);
8012
test_add_st("/task-creators/start_mandos_client/helper-directory",
8013
test_start_mandos_client_helper_directory);
8014
test_add_st("/task-creators/start_mandos_client/sigmask",
8015
test_start_mandos_client_sigmask);
8016
test_add_st("/task/wait_for_mandos_client_exit/badpid",
8017
test_wait_for_mandos_client_exit_badpid);
8018
test_add_st("/task/wait_for_mandos_client_exit/noexit",
8019
test_wait_for_mandos_client_exit_noexit);
8020
test_add_st("/task/wait_for_mandos_client_exit/success",
8021
test_wait_for_mandos_client_exit_success);
8022
test_add_st("/task/wait_for_mandos_client_exit/failure",
8023
test_wait_for_mandos_client_exit_failure);
8024
test_add_st("/task/wait_for_mandos_client_exit/killed",
8025
test_wait_for_mandos_client_exit_killed);
8026
test_add_st("/task/read_mandos_client_output/readerror",
8027
test_read_mandos_client_output_readerror);
8028
test_add_st("/task/read_mandos_client_output/nodata",
8029
test_read_mandos_client_output_nodata);
8030
test_add_st("/task/read_mandos_client_output/eof",
8031
test_read_mandos_client_output_eof);
8032
test_add_st("/task/read_mandos_client_output/once",
8033
test_read_mandos_client_output_once);
8034
test_add_st("/task/read_mandos_client_output/malloc",
8035
test_read_mandos_client_output_malloc);
8036
test_add_st("/task/read_mandos_client_output/append",
8037
test_read_mandos_client_output_append);
8038
test_add_st("/task-creators/add_inotify_dir_watch",
8039
test_add_inotify_dir_watch);
8040
test_add_st("/task-creators/add_inotify_dir_watch/fail",
8041
test_add_inotify_dir_watch_fail);
8042
test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory",
8043
test_add_inotify_dir_watch_nondir);
8044
test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
8045
test_add_inotify_dir_watch_EAGAIN);
8046
test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
8047
test_add_inotify_dir_watch_IN_CLOSE_WRITE);
8048
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
8049
test_add_inotify_dir_watch_IN_MOVED_TO);
8050
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
8051
test_add_inotify_dir_watch_IN_MOVED_FROM);
8052
test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
8053
test_add_inotify_dir_watch_IN_EXCL_UNLINK);
8054
test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
8055
test_add_inotify_dir_watch_IN_DELETE);
8056
test_add_st("/task/read_inotify_event/readerror",
8057
test_read_inotify_event_readerror);
8058
test_add_st("/task/read_inotify_event/bad-epoll",
8059
test_read_inotify_event_bad_epoll);
8060
test_add_st("/task/read_inotify_event/nodata",
8061
test_read_inotify_event_nodata);
8062
test_add_st("/task/read_inotify_event/eof",
8063
test_read_inotify_event_eof);
8064
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE",
8065
test_read_inotify_event_IN_CLOSE_WRITE);
8066
test_add_st("/task/read_inotify_event/IN_MOVED_TO",
8067
test_read_inotify_event_IN_MOVED_TO);
8068
test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
8069
test_read_inotify_event_IN_MOVED_FROM);
8070
test_add_st("/task/read_inotify_event/IN_DELETE",
8071
test_read_inotify_event_IN_DELETE);
8072
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
8073
test_read_inotify_event_IN_CLOSE_WRITE_badname);
8074
test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
8075
test_read_inotify_event_IN_MOVED_TO_badname);
8076
test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
8077
test_read_inotify_event_IN_MOVED_FROM_badname);
8078
test_add_st("/task/read_inotify_event/IN_DELETE/badname",
8079
test_read_inotify_event_IN_DELETE_badname);
8080
test_add_st("/task/open_and_parse_question/ENOENT",
8081
test_open_and_parse_question_ENOENT);
8082
test_add_st("/task/open_and_parse_question/EIO",
8083
test_open_and_parse_question_EIO);
8084
test_add_st("/task/open_and_parse_question/parse-error",
8085
test_open_and_parse_question_parse_error);
8086
test_add_st("/task/open_and_parse_question/nosocket",
8087
test_open_and_parse_question_nosocket);
8088
test_add_st("/task/open_and_parse_question/badsocket",
8089
test_open_and_parse_question_badsocket);
8090
test_add_st("/task/open_and_parse_question/nopid",
8091
test_open_and_parse_question_nopid);
8092
test_add_st("/task/open_and_parse_question/badpid",
8093
test_open_and_parse_question_badpid);
8094
test_add_st("/task/open_and_parse_question/noexist_pid",
8095
test_open_and_parse_question_noexist_pid);
8096
test_add_st("/task/open_and_parse_question/no-notafter",
8097
test_open_and_parse_question_no_notafter);
8098
test_add_st("/task/open_and_parse_question/bad-notafter",
8099
test_open_and_parse_question_bad_notafter);
8100
test_add_st("/task/open_and_parse_question/notafter-0",
8101
test_open_and_parse_question_notafter_0);
8102
test_add_st("/task/open_and_parse_question/notafter-1",
8103
test_open_and_parse_question_notafter_1);
8104
test_add_st("/task/open_and_parse_question/notafter-1-1",
8105
test_open_and_parse_question_notafter_1_1);
8106
test_add_st("/task/open_and_parse_question/notafter-1-2",
8107
test_open_and_parse_question_notafter_1_2);
8108
test_add_st("/task/open_and_parse_question/equal-notafter",
8109
test_open_and_parse_question_equal_notafter);
8110
test_add_st("/task/open_and_parse_question/late-notafter",
8111
test_open_and_parse_question_late_notafter);
8112
test_add_st("/task/cancel_old_question/0-1-2",
8113
test_cancel_old_question_0_1_2);
8114
test_add_st("/task/cancel_old_question/0-2-1",
8115
test_cancel_old_question_0_2_1);
8116
test_add_st("/task/cancel_old_question/1-2-3",
8117
test_cancel_old_question_1_2_3);
8118
test_add_st("/task/cancel_old_question/1-3-2",
8119
test_cancel_old_question_1_3_2);
8120
test_add_st("/task/cancel_old_question/2-1-3",
8121
test_cancel_old_question_2_1_3);
8122
test_add_st("/task/cancel_old_question/2-3-1",
8123
test_cancel_old_question_2_3_1);
8124
test_add_st("/task/cancel_old_question/3-1-2",
8125
test_cancel_old_question_3_1_2);
8126
test_add_st("/task/cancel_old_question/3-2-1",
8127
test_cancel_old_question_3_2_1);
8128
test_add_st("/task/connect_question_socket/name-too-long",
8129
test_connect_question_socket_name_too_long);
8130
test_add_st("/task/connect_question_socket/connect-fail",
8131
test_connect_question_socket_connect_fail);
8132
test_add_st("/task/connect_question_socket/bad-epoll",
8133
test_connect_question_socket_bad_epoll);
8134
test_add_st("/task/connect_question_socket/usable",
8135
test_connect_question_socket_usable);
8136
test_add_st("/task/send_password_to_socket/client-not-exited",
8137
test_send_password_to_socket_client_not_exited);
8138
test_add_st("/task/send_password_to_socket/password-not-read",
8139
test_send_password_to_socket_password_not_read);
8140
test_add_st("/task/send_password_to_socket/EMSGSIZE",
8141
test_send_password_to_socket_EMSGSIZE);
8142
test_add_st("/task/send_password_to_socket/retry",
8143
test_send_password_to_socket_retry);
8144
test_add_st("/task/send_password_to_socket/bad-epoll",
8145
test_send_password_to_socket_bad_epoll);
8146
test_add_st("/task/send_password_to_socket/null-password",
8147
test_send_password_to_socket_null_password);
8148
test_add_st("/task/send_password_to_socket/empty-password",
8149
test_send_password_to_socket_empty_password);
8150
test_add_st("/task/send_password_to_socket/empty-str-password",
8151
test_send_password_to_socket_empty_str_pass);
8152
test_add_st("/task/send_password_to_socket/text-password",
8153
test_send_password_to_socket_text_password);
8154
test_add_st("/task/send_password_to_socket/binary-password",
8155
test_send_password_to_socket_binary_password);
8156
test_add_st("/task/send_password_to_socket/nuls-in-password",
8157
test_send_password_to_socket_nuls_in_password);
8158
test_add_st("/task-creators/add_existing_questions/ENOENT",
8159
test_add_existing_questions_ENOENT);
8160
test_add_st("/task-creators/add_existing_questions/no-questions",
8161
test_add_existing_questions_no_questions);
8162
test_add_st("/task-creators/add_existing_questions/one-question",
8163
test_add_existing_questions_one_question);
8164
test_add_st("/task-creators/add_existing_questions/two-questions",
8165
test_add_existing_questions_two_questions);
8166
test_add_st("/task-creators/add_existing_questions/non-questions",
8167
test_add_existing_questions_non_questions);
8168
test_add_st("/task-creators/add_existing_questions/both-types",
8169
test_add_existing_questions_both_types);
8171
return g_test_run() == 0;
8174
static bool should_only_run_tests(int *argc_p, char **argv_p[]){
8175
GOptionContext *context = g_option_context_new("");
8177
g_option_context_set_help_enabled(context, FALSE);
8178
g_option_context_set_ignore_unknown_options(context, TRUE);
8180
gboolean should_run_tests = FALSE;
8181
GOptionEntry entries[] = {
8182
{ "test", 0, 0, G_OPTION_ARG_NONE,
8183
&should_run_tests, "Run tests", NULL },
8186
g_option_context_add_main_entries(context, entries, NULL);
8188
GError *error = NULL;
8190
if(g_option_context_parse(context, argc_p, argv_p, &error) != TRUE){
8191
g_option_context_free(context);
8192
g_error("Failed to parse options: %s", error->message);
8195
g_option_context_free(context);
8196
return should_run_tests != FALSE;