1
/* -*- coding: utf-8; lexical-binding: t -*- */
3
* Mandos password agent - Simple password agent to run Mandos client
5
* Copyright © 2019-2022 Teddy Hogeborn
6
* Copyright © 2019-2022 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
#if defined(__GNUC__) and __GNUC__ >= 7
1102
#pragma GCC diagnostic push
1103
/* ievent is pointing into a struct which is of sufficient size */
1104
#pragma GCC diagnostic ignored "-Wstringop-overflow"
1106
const ssize_t read_length = read(fd, ievent, ievent_size);
1107
#if defined(__GNUC__) and __GNUC__ >= 7
1108
#pragma GCC diagnostic pop
1110
if(read_length == 0){ /* EOF */
1111
error(0, 0, "Got EOF from inotify fd for directory %s", filename);
1113
cleanup_task(&task);
1116
if(read_length < 0 and errno != EAGAIN){ /* Actual error */
1117
error(0, errno, "Failed to read from inotify fd for directory %s",
1120
cleanup_task(&task);
1123
if(read_length > 0 /* Data has been read */
1124
and fnmatch("ask.*", ievent->name, FNM_FILE_NAME) == 0){
1125
char *question_filename = NULL;
1126
const ssize_t question_filename_length
1127
= asprintf(&question_filename, "%s/%s", filename, ievent->name);
1128
if(question_filename_length < 0){
1129
error(0, errno, "Failed to create file name from directory name"
1130
" %s and file name %s", filename, ievent->name);
1132
if(ievent->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)){
1133
if(not add_to_queue(queue, (task_context){
1134
.func=open_and_parse_question,
1136
.question_filename=question_filename,
1137
.filename=question_filename,
1139
.cancelled_filenames=cancelled_filenames,
1140
.current_time=current_time,
1141
.mandos_client_exited=mandos_client_exited,
1142
.password_is_read=password_is_read,
1144
error(0, errno, "Failed to add open_and_parse_question task"
1145
" for file name %s to queue", filename);
1147
/* Force the added task (open_and_parse_question) to run
1149
queue->next_run = 1;
1151
} else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
1152
if(not string_set_add(cancelled_filenames,
1153
question_filename)){
1154
error(0, errno, "Could not add question %s to"
1155
" cancelled_questions", question_filename);
1157
free(question_filename);
1158
cleanup_task(&task);
1161
free(question_filename);
1166
/* Either data was read, or EAGAIN was indicated, meaning no data
1169
/* Re-add myself to the queue */
1170
if(not add_to_queue(queue, task)){
1171
error(0, errno, "Failed to re-add read_inotify_event(%s) to"
1172
" queue", filename);
1174
cleanup_task(&task);
1178
/* Re-add the fd to the epoll set */
1179
const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1180
&(struct epoll_event)
1181
{ .events=EPOLLIN | EPOLLRDHUP });
1182
if(ret != 0 and errno != EEXIST){
1183
error(0, errno, "Failed to re-add inotify file descriptor %d for"
1184
" directory %s to epoll set", fd, filename);
1185
/* Force the added task (read_inotify_event) to run again, at most
1186
one second from now */
1187
if((queue->next_run == 0)
1188
or (queue->next_run > (*current_time + 1000000))){
1189
queue->next_run = *current_time + 1000000;
1194
__attribute__((nonnull))
1195
void open_and_parse_question(const task_context task,
1196
task_queue *const queue){
1197
__attribute__((cleanup(cleanup_string)))
1198
char *question_filename = task.question_filename;
1199
const int epoll_fd = task.epoll_fd;
1200
buffer *const password = task.password;
1201
string_set *const cancelled_filenames = task.cancelled_filenames;
1202
const mono_microsecs *const current_time = task.current_time;
1203
bool *const mandos_client_exited = task.mandos_client_exited;
1204
bool *const password_is_read = task.password_is_read;
1206
/* We use the GLib "Key-value file parser" functions to parse the
1207
question file. See <https://systemd.io/PASSWORD_AGENTS/> for
1208
specification of contents */
1209
__attribute__((nonnull))
1210
void cleanup_g_key_file(GKeyFile **key_file){
1211
if(*key_file != NULL){
1212
g_key_file_free(*key_file);
1216
__attribute__((cleanup(cleanup_g_key_file)))
1217
GKeyFile *key_file = g_key_file_new();
1218
if(key_file == NULL){
1219
error(0, errno, "Failed g_key_file_new() for \"%s\"",
1223
GError *glib_error = NULL;
1224
if(g_key_file_load_from_file(key_file, question_filename,
1225
G_KEY_FILE_NONE, &glib_error) != TRUE){
1226
/* If a file was removed, we should ignore it, so */
1227
/* only show error message if file actually existed */
1228
if(glib_error->code != G_FILE_ERROR_NOENT){
1229
error(0, 0, "Failed to load question data from file \"%s\": %s",
1230
question_filename, glib_error->message);
1235
__attribute__((cleanup(cleanup_string)))
1236
char *socket_name = g_key_file_get_string(key_file, "Ask",
1239
if(socket_name == NULL){
1240
error(0, 0, "Question file \"%s\" did not contain \"Socket\": %s",
1241
question_filename, glib_error->message);
1245
if(strlen(socket_name) == 0){
1246
error(0, 0, "Question file \"%s\" had empty \"Socket\" value",
1251
const guint64 pid = g_key_file_get_uint64(key_file, "Ask", "PID",
1253
if(glib_error != NULL){
1254
error(0, 0, "Question file \"%s\" contained bad \"PID\": %s",
1255
question_filename, glib_error->message);
1259
if((pid != (guint64)((pid_t)pid))
1260
or (kill((pid_t)pid, 0) != 0)){
1261
error(0, 0, "PID %" PRIuMAX " in question file \"%s\" is bad or"
1262
" does not exist", (uintmax_t)pid, question_filename);
1266
guint64 notafter = g_key_file_get_uint64(key_file, "Ask",
1267
"NotAfter", &glib_error);
1268
if(glib_error != NULL){
1269
if(glib_error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND){
1270
error(0, 0, "Question file \"%s\" contained bad \"NotAfter\":"
1271
" %s", question_filename, glib_error->message);
1276
if(queue->next_run == 0 or (queue->next_run > notafter)){
1277
queue->next_run = notafter;
1279
if(*current_time >= notafter){
1284
const task_context connect_question_socket_task = {
1285
.func=connect_question_socket,
1286
.question_filename=strdup(question_filename),
1289
.filename=strdup(socket_name),
1290
.cancelled_filenames=task.cancelled_filenames,
1291
.mandos_client_exited=mandos_client_exited,
1292
.password_is_read=password_is_read,
1293
.current_time=current_time,
1295
if(connect_question_socket_task.question_filename == NULL
1296
or connect_question_socket_task.filename == NULL
1297
or not add_to_queue(queue, connect_question_socket_task)){
1298
error(0, errno, "Failed to add connect_question_socket for socket"
1299
" %s (from \"%s\") to queue", socket_name,
1301
cleanup_task(&connect_question_socket_task);
1304
/* Force the added task (connect_question_socket) to run
1306
queue->next_run = 1;
1309
char *const dup_filename = strdup(question_filename);
1310
const task_context cancel_old_question_task = {
1311
.func=cancel_old_question,
1312
.question_filename=dup_filename,
1314
.filename=dup_filename,
1315
.cancelled_filenames=cancelled_filenames,
1316
.current_time=current_time,
1318
if(cancel_old_question_task.question_filename == NULL
1319
or not add_to_queue(queue, cancel_old_question_task)){
1320
error(0, errno, "Failed to add cancel_old_question for file "
1321
"\"%s\" to queue", question_filename);
1322
cleanup_task(&cancel_old_question_task);
1328
__attribute__((nonnull))
1329
void cancel_old_question(const task_context task,
1330
task_queue *const queue){
1331
char *const question_filename = task.question_filename;
1332
string_set *const cancelled_filenames = task.cancelled_filenames;
1333
const mono_microsecs notafter = task.notafter;
1334
const mono_microsecs *const current_time = task.current_time;
1336
if(*current_time >= notafter){
1337
if(not string_set_add(cancelled_filenames, question_filename)){
1338
error(0, errno, "Failed to cancel question for file %s",
1341
cleanup_task(&task);
1345
if(not add_to_queue(queue, task)){
1346
error(0, errno, "Failed to add cancel_old_question for file "
1347
"%s to queue", question_filename);
1348
cleanup_task(&task);
1352
if((queue->next_run == 0) or (queue->next_run > notafter)){
1353
queue->next_run = notafter;
1357
__attribute__((nonnull))
1358
void connect_question_socket(const task_context task,
1359
task_queue *const queue){
1360
char *const question_filename = task.question_filename;
1361
char *const filename = task.filename;
1362
const int epoll_fd = task.epoll_fd;
1363
buffer *const password = task.password;
1364
string_set *const cancelled_filenames = task.cancelled_filenames;
1365
bool *const mandos_client_exited = task.mandos_client_exited;
1366
bool *const password_is_read = task.password_is_read;
1367
const mono_microsecs *const current_time = task.current_time;
1369
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
1371
if(sizeof(sock_name.sun_path) <= strlen(filename)){
1372
error(0, 0, "Socket filename is larger than"
1373
" sizeof(sockaddr_un.sun_path); %" PRIuMAX ": \"%s\"",
1374
(uintmax_t)sizeof(sock_name.sun_path), filename);
1375
if(not string_set_add(cancelled_filenames, question_filename)){
1376
error(0, errno, "Failed to cancel question for file %s",
1379
cleanup_task(&task);
1383
const int fd = socket(PF_LOCAL, SOCK_DGRAM
1384
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
1387
"Failed to create socket(PF_LOCAL, SOCK_DGRAM, 0)");
1388
if(not add_to_queue(queue, task)){
1389
error(0, errno, "Failed to add connect_question_socket for file"
1390
" \"%s\" and socket \"%s\" to queue", question_filename,
1392
cleanup_task(&task);
1394
/* Force the added task (connect_question_socket) to run
1396
queue->next_run = 1;
1401
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
1402
if(connect(fd, (struct sockaddr *)&sock_name,
1403
(socklen_t)SUN_LEN(&sock_name)) != 0){
1404
error(0, errno, "Failed to connect socket to \"%s\"", filename);
1405
if(not add_to_queue(queue, task)){
1406
error(0, errno, "Failed to add connect_question_socket for file"
1407
" \"%s\" and socket \"%s\" to queue", question_filename,
1409
cleanup_task(&task);
1411
/* Force the added task (connect_question_socket) to run again,
1412
at most one second from now */
1413
if((queue->next_run == 0)
1414
or (queue->next_run > (*current_time + 1000000))){
1415
queue->next_run = *current_time + 1000000;
1421
/* Not necessary, but we can try, and merely warn on failure */
1422
if(shutdown(fd, SHUT_RD) != 0){
1423
error(0, errno, "Failed to shutdown reading from socket \"%s\"",
1427
/* Add the fd to the epoll set */
1428
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1429
&(struct epoll_event){ .events=EPOLLOUT })
1431
error(0, errno, "Failed to add inotify file descriptor %d for"
1432
" socket %s to epoll set", fd, filename);
1433
if(not add_to_queue(queue, task)){
1434
error(0, errno, "Failed to add connect_question_socket for file"
1435
" \"%s\" and socket \"%s\" to queue", question_filename,
1437
cleanup_task(&task);
1439
/* Force the added task (connect_question_socket) to run again,
1440
at most one second from now */
1441
if((queue->next_run == 0)
1442
or (queue->next_run > (*current_time + 1000000))){
1443
queue->next_run = *current_time + 1000000;
1449
/* add task send_password_to_socket to queue */
1450
const task_context send_password_to_socket_task = {
1451
.func=send_password_to_socket,
1452
.question_filename=question_filename,
1457
.cancelled_filenames=cancelled_filenames,
1458
.mandos_client_exited=mandos_client_exited,
1459
.password_is_read=password_is_read,
1460
.current_time=current_time,
1463
if(not add_to_queue(queue, send_password_to_socket_task)){
1464
error(0, errno, "Failed to add send_password_to_socket for"
1465
" file \"%s\" and socket \"%s\" to queue",
1466
question_filename, filename);
1467
cleanup_task(&send_password_to_socket_task);
1471
__attribute__((nonnull))
1472
void send_password_to_socket(const task_context task,
1473
task_queue *const queue){
1474
char *const question_filename=task.question_filename;
1475
char *const filename=task.filename;
1476
const int epoll_fd=task.epoll_fd;
1477
const int fd=task.fd;
1478
buffer *const password=task.password;
1479
string_set *const cancelled_filenames=task.cancelled_filenames;
1480
bool *const mandos_client_exited = task.mandos_client_exited;
1481
bool *const password_is_read = task.password_is_read;
1482
const mono_microsecs *const current_time = task.current_time;
1484
if(*mandos_client_exited and *password_is_read){
1486
const size_t send_buffer_length = password->length + 2;
1487
char *send_buffer = malloc(send_buffer_length);
1488
if(send_buffer == NULL){
1489
error(0, errno, "Failed to allocate send_buffer");
1491
#if defined(__GNUC__) and __GNUC__ >= 5
1492
#pragma GCC diagnostic push
1493
/* mlock() does not access the memory */
1494
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
1496
if(mlock(send_buffer, send_buffer_length) != 0){
1497
#if defined(__GNUC__) and __GNUC__ >= 5
1498
#pragma GCC diagnostic pop
1500
/* Warn but do not treat as fatal error */
1501
if(errno != EPERM and errno != ENOMEM){
1502
error(0, errno, "Failed to lock memory for password"
1506
/* “[…] send a single datagram to the socket consisting of the
1507
password string either prefixed with "+" or with "-"
1508
depending on whether the password entry was successful or
1509
not. You may but don't have to include a final NUL byte in
1512
— <https://systemd.io/PASSWORD_AGENTS/> (Tue, 15 Sep 2020
1515
send_buffer[0] = '+'; /* Prefix with "+" */
1516
/* Always add an extra NUL */
1517
send_buffer[password->length + 1] = '\0';
1518
if(password->length > 0){
1519
memcpy(send_buffer + 1, password->data, password->length);
1522
ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1524
const error_t saved_errno = (ssret < 0) ? errno : 0;
1525
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1526
explicit_bzero(send_buffer, send_buffer_length);
1528
memset(send_buffer, '\0', send_buffer_length);
1530
if(munlock(send_buffer, send_buffer_length) != 0){
1531
error(0, errno, "Failed to unlock memory of send buffer");
1534
if(ssret < 0 or ssret < (ssize_t)send_buffer_length){
1535
switch(saved_errno){
1548
error(0, saved_errno, "Password of size %" PRIuMAX
1549
" is too big", (uintmax_t)password->length);
1553
__attribute__((fallthrough));
1556
if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1557
error(0, 0, "Password only partially sent to socket %s: %"
1558
PRIuMAX " out of %" PRIuMAX " bytes sent", filename,
1559
(uintmax_t)ssret, (uintmax_t)send_buffer_length);
1564
__attribute__((fallthrough));
1567
error(0, saved_errno, "Failed to send() to socket %s",
1569
if(not string_set_add(cancelled_filenames,
1570
question_filename)){
1571
error(0, errno, "Failed to cancel question for file %s",
1574
cleanup_task(&task);
1579
cleanup_task(&task);
1585
/* We failed or are not ready yet; retry later */
1587
if(not add_to_queue(queue, task)){
1588
error(0, errno, "Failed to add send_password_to_socket for"
1589
" file %s and socket %s to queue", question_filename,
1591
cleanup_task(&task);
1594
/* Add the fd to the epoll set */
1595
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1596
&(struct epoll_event){ .events=EPOLLOUT })
1598
error(0, errno, "Failed to add socket file descriptor %d for"
1599
" socket %s to epoll set", fd, filename);
1600
/* Force the added task (send_password_to_socket) to run again, at
1601
most one second from now */
1602
if((queue->next_run == 0)
1603
or (queue->next_run > (*current_time + 1000000))){
1604
queue->next_run = *current_time + 1000000;
1609
__attribute__((warn_unused_result))
1610
bool add_existing_questions(task_queue *const queue,
1612
buffer *const password,
1613
string_set *cancelled_filenames,
1614
const mono_microsecs *const current_time,
1615
bool *const mandos_client_exited,
1616
bool *const password_is_read,
1617
const char *const dirname){
1618
__attribute__((cleanup(cleanup_string)))
1619
char *dir_pattern = NULL;
1620
const int ret = asprintf(&dir_pattern, "%s/ask.*", dirname);
1621
if(ret < 0 or dir_pattern == NULL){
1622
error(0, errno, "Could not create glob pattern for directory %s",
1626
__attribute__((cleanup(globfree)))
1627
glob_t question_filenames = {};
1628
switch(glob(dir_pattern, GLOB_ERR | GLOB_NOSORT | GLOB_MARK,
1629
NULL, &question_filenames)){
1632
error(0, errno, "Failed to open directory %s", dirname);
1635
error(0, errno, "There are no question files in %s", dirname);
1638
error(0, errno, "Could not allocate memory for question file"
1639
" names in %s", dirname);
1643
__attribute__((fallthrough));
1646
for(size_t i = 0; i < question_filenames.gl_pathc; i++){
1647
char *const question_filename = strdup(question_filenames
1649
const task_context task = {
1650
.func=open_and_parse_question,
1652
.question_filename=question_filename,
1653
.filename=question_filename,
1655
.cancelled_filenames=cancelled_filenames,
1656
.current_time=current_time,
1657
.mandos_client_exited=mandos_client_exited,
1658
.password_is_read=password_is_read,
1661
if(question_filename == NULL
1662
or not add_to_queue(queue, task)){
1663
error(0, errno, "Failed to add open_and_parse_question for"
1664
" file %s to queue",
1665
question_filenames.gl_pathv[i]);
1666
free(question_filename);
1668
queue->next_run = 1;
1675
__attribute__((nonnull, warn_unused_result))
1676
bool wait_for_event(const int epoll_fd,
1677
const mono_microsecs queue_next_run,
1678
const mono_microsecs current_time){
1679
__attribute__((const))
1680
int milliseconds_to_wait(const mono_microsecs currtime,
1681
const mono_microsecs nextrun){
1682
if(currtime >= nextrun){
1685
const uintmax_t wait_time_ms = (nextrun - currtime) / 1000;
1686
if(wait_time_ms > (uintmax_t)INT_MAX){
1689
return (int)wait_time_ms;
1692
const int wait_time_ms = milliseconds_to_wait(current_time,
1695
/* Prepare unblocking of SIGCHLD during epoll_pwait */
1696
sigset_t temporary_unblocked_sigmask;
1697
/* Get current signal mask */
1698
if(pthread_sigmask(-1, NULL, &temporary_unblocked_sigmask) != 0){
1701
/* Remove SIGCHLD from the signal mask */
1702
if(sigdelset(&temporary_unblocked_sigmask, SIGCHLD) != 0){
1705
struct epoll_event events[8]; /* Ignored */
1706
int ret = epoll_pwait(epoll_fd, events,
1707
sizeof(events) / sizeof(struct epoll_event),
1708
queue_next_run == 0 ? -1 : (int)wait_time_ms,
1709
&temporary_unblocked_sigmask);
1710
if(ret < 0 and errno != EINTR){
1711
error(0, errno, "Failed epoll_pwait(epfd=%d, ..., timeout=%d,"
1713
queue_next_run == 0 ? -1 : (int)wait_time_ms);
1716
return clear_all_fds_from_epoll_set(epoll_fd);
1719
bool clear_all_fds_from_epoll_set(const int epoll_fd){
1720
/* Create a new empty epoll set */
1721
__attribute__((cleanup(cleanup_close)))
1722
const int new_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
1723
if(new_epoll_fd < 0){
1726
/* dup3() the new epoll set fd over the old one, replacing it */
1727
if(dup3(new_epoll_fd, epoll_fd, O_CLOEXEC) < 0){
1733
__attribute__((nonnull, warn_unused_result))
1734
bool run_queue(task_queue **const queue,
1735
string_set *const cancelled_filenames,
1736
bool *const quit_now){
1738
task_queue *new_queue = create_queue();
1739
if(new_queue == NULL){
1743
__attribute__((cleanup(string_set_clear)))
1744
string_set old_cancelled_filenames = {};
1745
string_set_swap(cancelled_filenames, &old_cancelled_filenames);
1747
/* Declare i outside the for loop, since we might need i after the
1748
loop in case we aborted in the middle */
1750
for(i=0; i < (*queue)->length and not *quit_now; i++){
1751
task_context *const task = &((*queue)->tasks[i]);
1752
const char *const question_filename = task->question_filename;
1753
/* Skip any task referencing a cancelled question filename */
1754
if(question_filename != NULL
1755
and string_set_contains(old_cancelled_filenames,
1756
question_filename)){
1760
task->func(*task, new_queue);
1764
/* we might be in the middle of the queue, so clean up any
1765
remaining tasks in the current queue */
1766
for(; i < (*queue)->length; i++){
1767
cleanup_task(&((*queue)->tasks[i]));
1781
/* End of regular code section */
1783
/* Start of tests section; here are the tests for the above code */
1785
/* This "fixture" data structure is used by the test setup and
1786
teardown functions */
1788
struct sigaction orig_sigaction;
1789
sigset_t orig_sigmask;
1792
static void test_setup(test_fixture *fixture,
1793
__attribute__((unused))
1794
gconstpointer user_data){
1795
g_assert_true(setup_signal_handler(&fixture->orig_sigaction));
1796
g_assert_true(block_sigchld(&fixture->orig_sigmask));
1799
static void test_teardown(test_fixture *fixture,
1800
__attribute__((unused))
1801
gconstpointer user_data){
1802
g_assert_true(restore_signal_handler(&fixture->orig_sigaction));
1803
g_assert_true(restore_sigmask(&fixture->orig_sigmask));
1806
/* Utility function used by tests to search queue for matching task */
1807
__attribute__((pure, nonnull, warn_unused_result))
1808
static task_context *find_matching_task(const task_queue *const queue,
1809
const task_context task){
1810
/* The argument "task" structure is a pattern to match; 0 in any
1811
member means any value matches, otherwise the value must match.
1812
The filename strings are compared by strcmp(), not by pointer. */
1813
for(size_t i = 0; i < queue->length; i++){
1814
task_context *const current_task = queue->tasks+i;
1815
/* Check all members of task_context, if set to a non-zero value.
1816
If a member does not match, continue to next task in queue */
1818
/* task_func *const func */
1819
if(task.func != NULL and current_task->func != task.func){
1822
/* char *const question_filename; */
1823
if(task.question_filename != NULL
1824
and (current_task->question_filename == NULL
1825
or strcmp(current_task->question_filename,
1826
task.question_filename) != 0)){
1829
/* const pid_t pid; */
1830
if(task.pid != 0 and current_task->pid != task.pid){
1833
/* const int epoll_fd; */
1834
if(task.epoll_fd != 0
1835
and current_task->epoll_fd != task.epoll_fd){
1838
/* bool *const quit_now; */
1839
if(task.quit_now != NULL
1840
and current_task->quit_now != task.quit_now){
1844
if(task.fd != 0 and current_task->fd != task.fd){
1847
/* bool *const mandos_client_exited; */
1848
if(task.mandos_client_exited != NULL
1849
and current_task->mandos_client_exited
1850
!= task.mandos_client_exited){
1853
/* buffer *const password; */
1854
if(task.password != NULL
1855
and current_task->password != task.password){
1858
/* bool *const password_is_read; */
1859
if(task.password_is_read != NULL
1860
and current_task->password_is_read != task.password_is_read){
1863
/* char *filename; */
1864
if(task.filename != NULL
1865
and (current_task->filename == NULL
1866
or strcmp(current_task->filename, task.filename) != 0)){
1869
/* string_set *const cancelled_filenames; */
1870
if(task.cancelled_filenames != NULL
1871
and current_task->cancelled_filenames
1872
!= task.cancelled_filenames){
1875
/* const mono_microsecs notafter; */
1876
if(task.notafter != 0
1877
and current_task->notafter != task.notafter){
1880
/* const mono_microsecs *const current_time; */
1881
if(task.current_time != NULL
1882
and current_task->current_time != task.current_time){
1885
/* Current task matches all members; return it */
1886
return current_task;
1888
/* No task in queue matches passed pattern task */
1892
static void test_create_queue(__attribute__((unused))
1893
test_fixture *fixture,
1894
__attribute__((unused))
1895
gconstpointer user_data){
1896
__attribute__((cleanup(cleanup_queue)))
1897
task_queue *const queue = create_queue();
1898
g_assert_nonnull(queue);
1899
g_assert_null(queue->tasks);
1900
g_assert_true(queue->length == 0);
1901
g_assert_true(queue->next_run == 0);
1904
static task_func dummy_func;
1906
static void test_add_to_queue(__attribute__((unused))
1907
test_fixture *fixture,
1908
__attribute__((unused))
1909
gconstpointer user_data){
1910
__attribute__((cleanup(cleanup_queue)))
1911
task_queue *queue = create_queue();
1912
g_assert_nonnull(queue);
1914
g_assert_true(add_to_queue(queue,
1915
(task_context){ .func=dummy_func }));
1916
g_assert_true(queue->length == 1);
1917
g_assert_nonnull(queue->tasks);
1918
g_assert_true(queue->tasks[0].func == dummy_func);
1921
static void test_add_to_queue_overflow(__attribute__((unused))
1922
test_fixture *fixture,
1923
__attribute__((unused))
1924
gconstpointer user_data){
1925
__attribute__((cleanup(cleanup_queue)))
1926
task_queue *queue = create_queue();
1927
g_assert_nonnull(queue);
1928
g_assert_true(queue->length == 0);
1929
queue->length = SIZE_MAX / sizeof(task_context); /* fake max size */
1931
FILE *real_stderr = stderr;
1932
FILE *devnull = fopen("/dev/null", "we");
1933
g_assert_nonnull(devnull);
1935
const bool ret = add_to_queue(queue,
1936
(task_context){ .func=dummy_func });
1937
g_assert_true(errno == ENOMEM);
1938
g_assert_false(ret);
1939
stderr = real_stderr;
1940
g_assert_cmpint(fclose(devnull), ==, 0);
1941
queue->length = 0; /* Restore real size */
1944
static void dummy_func(__attribute__((unused))
1945
const task_context task,
1946
__attribute__((unused))
1947
task_queue *const queue){
1950
static void test_queue_has_question_empty(__attribute__((unused))
1951
test_fixture *fixture,
1952
__attribute__((unused))
1953
gconstpointer user_data){
1954
__attribute__((cleanup(cleanup_queue)))
1955
task_queue *queue = create_queue();
1956
g_assert_nonnull(queue);
1957
g_assert_false(queue_has_question(queue));
1960
static void test_queue_has_question_false(__attribute__((unused))
1961
test_fixture *fixture,
1962
__attribute__((unused))
1963
gconstpointer user_data){
1964
__attribute__((cleanup(cleanup_queue)))
1965
task_queue *queue = create_queue();
1966
g_assert_nonnull(queue);
1967
g_assert_true(add_to_queue(queue,
1968
(task_context){ .func=dummy_func }));
1969
g_assert_false(queue_has_question(queue));
1972
static void test_queue_has_question_true(__attribute__((unused))
1973
test_fixture *fixture,
1974
__attribute__((unused))
1975
gconstpointer user_data){
1976
__attribute__((cleanup(cleanup_queue)))
1977
task_queue *queue = create_queue();
1978
g_assert_nonnull(queue);
1979
char *const question_filename
1980
= strdup("/nonexistent/question_filename");
1981
g_assert_nonnull(question_filename);
1982
task_context task = {
1984
.question_filename=question_filename,
1986
g_assert_true(add_to_queue(queue, task));
1987
g_assert_true(queue_has_question(queue));
1990
static void test_queue_has_question_false2(__attribute__((unused))
1991
test_fixture *fixture,
1992
__attribute__((unused))
1993
gconstpointer user_data){
1994
__attribute__((cleanup(cleanup_queue)))
1995
task_queue *queue = create_queue();
1996
g_assert_nonnull(queue);
1997
task_context task = { .func=dummy_func };
1998
g_assert_true(add_to_queue(queue, task));
1999
g_assert_true(add_to_queue(queue, task));
2000
g_assert_cmpint((int)queue->length, ==, 2);
2001
g_assert_false(queue_has_question(queue));
2004
static void test_queue_has_question_true2(__attribute__((unused))
2005
test_fixture *fixture,
2006
__attribute__((unused))
2007
gconstpointer user_data){
2008
__attribute__((cleanup(cleanup_queue)))
2009
task_queue *queue = create_queue();
2010
g_assert_nonnull(queue);
2011
task_context task1 = { .func=dummy_func };
2012
g_assert_true(add_to_queue(queue, task1));
2013
char *const question_filename
2014
= strdup("/nonexistent/question_filename");
2015
g_assert_nonnull(question_filename);
2016
task_context task2 = {
2018
.question_filename=question_filename,
2020
g_assert_true(add_to_queue(queue, task2));
2021
g_assert_cmpint((int)queue->length, ==, 2);
2022
g_assert_true(queue_has_question(queue));
2025
static void test_cleanup_buffer(__attribute__((unused))
2026
test_fixture *fixture,
2027
__attribute__((unused))
2028
gconstpointer user_data){
2031
const size_t buffersize = 10;
2033
buf.data = malloc(buffersize);
2034
g_assert_nonnull(buf.data);
2035
if(mlock(buf.data, buffersize) != 0){
2036
g_assert_true(errno == EPERM or errno == ENOMEM);
2039
cleanup_buffer(&buf);
2040
g_assert_null(buf.data);
2044
void test_string_set_new_set_contains_nothing(__attribute__((unused))
2045
test_fixture *fixture,
2046
__attribute__((unused))
2049
__attribute__((cleanup(string_set_clear)))
2050
string_set set = {};
2051
g_assert_false(string_set_contains(set, "")); /* Empty string */
2052
g_assert_false(string_set_contains(set, "test_string"));
2056
test_string_set_with_added_string_contains_it(__attribute__((unused))
2057
test_fixture *fixture,
2058
__attribute__((unused))
2061
__attribute__((cleanup(string_set_clear)))
2062
string_set set = {};
2063
g_assert_true(string_set_add(&set, "test_string"));
2064
g_assert_true(string_set_contains(set, "test_string"));
2068
test_string_set_cleared_does_not_contain_str(__attribute__((unused))
2069
test_fixture *fixture,
2070
__attribute__((unused))
2071
gconstpointer user_data){
2072
__attribute__((cleanup(string_set_clear)))
2073
string_set set = {};
2074
g_assert_true(string_set_add(&set, "test_string"));
2075
string_set_clear(&set);
2076
g_assert_false(string_set_contains(set, "test_string"));
2080
void test_string_set_swap_one_with_empty(__attribute__((unused))
2081
test_fixture *fixture,
2082
__attribute__((unused))
2083
gconstpointer user_data){
2084
__attribute__((cleanup(string_set_clear)))
2085
string_set set1 = {};
2086
__attribute__((cleanup(string_set_clear)))
2087
string_set set2 = {};
2088
g_assert_true(string_set_add(&set1, "test_string1"));
2089
string_set_swap(&set1, &set2);
2090
g_assert_false(string_set_contains(set1, "test_string1"));
2091
g_assert_true(string_set_contains(set2, "test_string1"));
2095
void test_string_set_swap_empty_with_one(__attribute__((unused))
2096
test_fixture *fixture,
2097
__attribute__((unused))
2098
gconstpointer user_data){
2099
__attribute__((cleanup(string_set_clear)))
2100
string_set set1 = {};
2101
__attribute__((cleanup(string_set_clear)))
2102
string_set set2 = {};
2103
g_assert_true(string_set_add(&set2, "test_string2"));
2104
string_set_swap(&set1, &set2);
2105
g_assert_true(string_set_contains(set1, "test_string2"));
2106
g_assert_false(string_set_contains(set2, "test_string2"));
2109
static void test_string_set_swap_one_with_one(__attribute__((unused))
2110
test_fixture *fixture,
2111
__attribute__((unused))
2114
__attribute__((cleanup(string_set_clear)))
2115
string_set set1 = {};
2116
__attribute__((cleanup(string_set_clear)))
2117
string_set set2 = {};
2118
g_assert_true(string_set_add(&set1, "test_string1"));
2119
g_assert_true(string_set_add(&set2, "test_string2"));
2120
string_set_swap(&set1, &set2);
2121
g_assert_false(string_set_contains(set1, "test_string1"));
2122
g_assert_true(string_set_contains(set1, "test_string2"));
2123
g_assert_false(string_set_contains(set2, "test_string2"));
2124
g_assert_true(string_set_contains(set2, "test_string1"));
2127
static bool fd_has_cloexec_and_nonblock(const int);
2129
static bool epoll_set_contains(int, int, uint32_t);
2131
static void test_start_mandos_client(test_fixture *fixture,
2132
__attribute__((unused))
2133
gconstpointer user_data){
2135
bool mandos_client_exited = false;
2136
bool quit_now = false;
2137
__attribute__((cleanup(cleanup_close)))
2138
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2139
g_assert_cmpint(epoll_fd, >=, 0);
2140
__attribute__((cleanup(cleanup_queue)))
2141
task_queue *queue = create_queue();
2142
g_assert_nonnull(queue);
2143
buffer password = {};
2144
bool password_is_read = false;
2145
const char helper_directory[] = "/nonexistent";
2146
const char *const argv[] = { "/bin/true", NULL };
2148
g_assert_true(start_mandos_client(queue, epoll_fd,
2149
&mandos_client_exited, &quit_now,
2150
&password, &password_is_read,
2151
&fixture->orig_sigaction,
2152
fixture->orig_sigmask,
2153
helper_directory, 0, 0, argv));
2155
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
2157
const task_context *const added_wait_task
2158
= find_matching_task(queue, (task_context){
2159
.func=wait_for_mandos_client_exit,
2160
.mandos_client_exited=&mandos_client_exited,
2161
.quit_now=&quit_now,
2163
g_assert_nonnull(added_wait_task);
2164
g_assert_cmpint(added_wait_task->pid, >, 0);
2165
g_assert_cmpint(kill(added_wait_task->pid, SIGKILL), ==, 0);
2166
waitpid(added_wait_task->pid, NULL, 0);
2168
const task_context *const added_read_task
2169
= find_matching_task(queue, (task_context){
2170
.func=read_mandos_client_output,
2172
.password=&password,
2173
.password_is_read=&password_is_read,
2174
.quit_now=&quit_now,
2176
g_assert_nonnull(added_read_task);
2177
g_assert_cmpint(added_read_task->fd, >, 2);
2178
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
2179
g_assert_true(epoll_set_contains(epoll_fd, added_read_task->fd,
2180
EPOLLIN | EPOLLRDHUP));
2183
static bool fd_has_cloexec_and_nonblock(const int fd){
2184
const int socket_fd_flags = fcntl(fd, F_GETFD, 0);
2185
const int socket_file_flags = fcntl(fd, F_GETFL, 0);
2186
return ((socket_fd_flags >= 0)
2187
and (socket_fd_flags & FD_CLOEXEC)
2188
and (socket_file_flags >= 0)
2189
and (socket_file_flags & O_NONBLOCK));
2192
__attribute__((const))
2193
bool is_privileged(void){
2194
uid_t user = getuid() + 1;
2195
if(user == 0){ /* Overflow check */
2198
gid_t group = getuid() + 1;
2199
if(group == 0){ /* Overflow check */
2202
const pid_t pid = fork();
2203
if(pid == 0){ /* Child */
2204
if(setresgid((uid_t)-1, group, group) == -1){
2206
error(EXIT_FAILURE, errno, "Failed to setresgid(-1, %" PRIuMAX
2207
", %" PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
2211
if(setresuid((uid_t)-1, user, user) == -1){
2213
error(EXIT_FAILURE, errno, "Failed to setresuid(-1, %" PRIuMAX
2214
", %" PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
2221
error(EXIT_FAILURE, errno, "Failed to fork()");
2225
waitpid(pid, &status, 0);
2226
if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2232
static bool epoll_set_contains(int epoll_fd, int fd, uint32_t events){
2233
/* Only scan for events in this eventmask */
2234
const uint32_t eventmask = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
2235
__attribute__((cleanup(cleanup_string)))
2236
char *fdinfo_name = NULL;
2237
int ret = asprintf(&fdinfo_name, "/proc/self/fdinfo/%d", epoll_fd);
2238
g_assert_cmpint(ret, >, 0);
2239
g_assert_nonnull(fdinfo_name);
2241
FILE *fdinfo = fopen(fdinfo_name, "r");
2242
g_assert_nonnull(fdinfo);
2243
uint32_t reported_events;
2248
if(getline(&line.data, &line.allocated, fdinfo) < 0){
2251
/* See proc(5) for format of /proc/PID/fdinfo/FD for epoll fd's */
2252
if(sscanf(line.data, "tfd: %d events: %" SCNx32 " ",
2253
&found_fd, &reported_events) == 2){
2258
} while(not feof(fdinfo) and not ferror(fdinfo));
2259
g_assert_cmpint(fclose(fdinfo), ==, 0);
2266
/* Don't check events if none are given */
2269
return (reported_events & eventmask) == (events & eventmask);
2272
static void test_start_mandos_client_execv(test_fixture *fixture,
2273
__attribute__((unused))
2274
gconstpointer user_data){
2275
bool mandos_client_exited = false;
2276
bool quit_now = false;
2277
__attribute__((cleanup(cleanup_close)))
2278
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2279
g_assert_cmpint(epoll_fd, >=, 0);
2280
__attribute__((cleanup(cleanup_queue)))
2281
task_queue *queue = create_queue();
2282
g_assert_nonnull(queue);
2283
__attribute__((cleanup(cleanup_buffer)))
2284
buffer password = {};
2285
const char helper_directory[] = "/nonexistent";
2286
/* Can't execv("/", ...), so this should fail */
2287
const char *const argv[] = { "/", NULL };
2290
__attribute__((cleanup(cleanup_close)))
2291
const int devnull_fd = open("/dev/null",
2292
O_WRONLY | O_CLOEXEC | O_NOCTTY);
2293
g_assert_cmpint(devnull_fd, >=, 0);
2294
__attribute__((cleanup(cleanup_close)))
2295
const int real_stderr_fd = dup(STDERR_FILENO);
2296
g_assert_cmpint(real_stderr_fd, >=, 0);
2297
dup2(devnull_fd, STDERR_FILENO);
2299
const bool success = start_mandos_client(queue, epoll_fd,
2300
&mandos_client_exited,
2304
&fixture->orig_sigaction,
2305
fixture->orig_sigmask,
2306
helper_directory, 0, 0,
2308
dup2(real_stderr_fd, STDERR_FILENO);
2309
g_assert_true(success);
2311
g_assert_cmpuint((unsigned int)queue->length, ==, 2);
2313
struct timespec starttime, currtime;
2314
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2316
queue->next_run = 0;
2317
string_set cancelled_filenames = {};
2320
__attribute__((cleanup(cleanup_close)))
2321
const int devnull_fd = open("/dev/null",
2322
O_WRONLY | O_CLOEXEC | O_NOCTTY);
2323
g_assert_cmpint(devnull_fd, >=, 0);
2324
__attribute__((cleanup(cleanup_close)))
2325
const int real_stderr_fd = dup(STDERR_FILENO);
2326
g_assert_cmpint(real_stderr_fd, >=, 0);
2327
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2328
dup2(devnull_fd, STDERR_FILENO);
2329
const bool success = run_queue(&queue, &cancelled_filenames,
2331
dup2(real_stderr_fd, STDERR_FILENO);
2336
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2337
} while(((queue->length) > 0)
2339
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2341
g_assert_true(quit_now);
2342
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2343
g_assert_true(mandos_client_exited);
2346
static void test_start_mandos_client_suid_euid(test_fixture *fixture,
2347
__attribute__((unused))
2350
if(not is_privileged()){
2351
g_test_skip("Not privileged");
2355
bool mandos_client_exited = false;
2356
bool quit_now = false;
2357
__attribute__((cleanup(cleanup_close)))
2358
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2359
g_assert_cmpint(epoll_fd, >=, 0);
2360
__attribute__((cleanup(cleanup_queue)))
2361
task_queue *queue = create_queue();
2362
g_assert_nonnull(queue);
2363
__attribute__((cleanup(cleanup_buffer)))
2364
buffer password = {};
2365
bool password_is_read = false;
2366
const char helper_directory[] = "/nonexistent";
2367
const char *const argv[] = { "/usr/bin/id", "--user", NULL };
2371
const bool success = start_mandos_client(queue, epoll_fd,
2372
&mandos_client_exited,
2373
&quit_now, &password,
2375
&fixture->orig_sigaction,
2376
fixture->orig_sigmask,
2377
helper_directory, user,
2379
g_assert_true(success);
2380
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2382
struct timespec starttime, currtime;
2383
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2385
queue->next_run = 0;
2386
string_set cancelled_filenames = {};
2387
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2388
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2389
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2390
} while(((queue->length) > 0)
2392
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2394
g_assert_false(quit_now);
2395
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2396
g_assert_true(mandos_client_exited);
2398
g_assert_true(password_is_read);
2399
g_assert_nonnull(password.data);
2402
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2404
g_assert_true((uid_t)id == id);
2406
g_assert_cmpuint((unsigned int)id, ==, 0);
2409
static void test_start_mandos_client_suid_egid(test_fixture *fixture,
2410
__attribute__((unused))
2413
if(not is_privileged()){
2414
g_test_skip("Not privileged");
2418
bool mandos_client_exited = false;
2419
bool quit_now = false;
2420
__attribute__((cleanup(cleanup_close)))
2421
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2422
g_assert_cmpint(epoll_fd, >=, 0);
2423
__attribute__((cleanup(cleanup_queue)))
2424
task_queue *queue = create_queue();
2425
g_assert_nonnull(queue);
2426
__attribute__((cleanup(cleanup_buffer)))
2427
buffer password = {};
2428
bool password_is_read = false;
2429
const char helper_directory[] = "/nonexistent";
2430
const char *const argv[] = { "/usr/bin/id", "--group", NULL };
2434
const bool success = start_mandos_client(queue, epoll_fd,
2435
&mandos_client_exited,
2436
&quit_now, &password,
2438
&fixture->orig_sigaction,
2439
fixture->orig_sigmask,
2440
helper_directory, user,
2442
g_assert_true(success);
2443
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2445
struct timespec starttime, currtime;
2446
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2448
queue->next_run = 0;
2449
string_set cancelled_filenames = {};
2450
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2451
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2452
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2453
} while(((queue->length) > 0)
2455
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2457
g_assert_false(quit_now);
2458
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2459
g_assert_true(mandos_client_exited);
2461
g_assert_true(password_is_read);
2462
g_assert_nonnull(password.data);
2465
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2467
g_assert_true((gid_t)id == id);
2469
g_assert_cmpuint((unsigned int)id, ==, 0);
2472
static void test_start_mandos_client_suid_ruid(test_fixture *fixture,
2473
__attribute__((unused))
2476
if(not is_privileged()){
2477
g_test_skip("Not privileged");
2481
bool mandos_client_exited = false;
2482
bool quit_now = false;
2483
__attribute__((cleanup(cleanup_close)))
2484
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2485
g_assert_cmpint(epoll_fd, >=, 0);
2486
__attribute__((cleanup(cleanup_queue)))
2487
task_queue *queue = create_queue();
2488
g_assert_nonnull(queue);
2489
__attribute__((cleanup(cleanup_buffer)))
2490
buffer password = {};
2491
bool password_is_read = false;
2492
const char helper_directory[] = "/nonexistent";
2493
const char *const argv[] = { "/usr/bin/id", "--user", "--real",
2498
const bool success = start_mandos_client(queue, epoll_fd,
2499
&mandos_client_exited,
2500
&quit_now, &password,
2502
&fixture->orig_sigaction,
2503
fixture->orig_sigmask,
2504
helper_directory, user,
2506
g_assert_true(success);
2507
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2509
struct timespec starttime, currtime;
2510
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2512
queue->next_run = 0;
2513
string_set cancelled_filenames = {};
2514
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2515
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2516
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2517
} while(((queue->length) > 0)
2519
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2521
g_assert_false(quit_now);
2522
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2523
g_assert_true(mandos_client_exited);
2525
g_assert_true(password_is_read);
2526
g_assert_nonnull(password.data);
2529
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2531
g_assert_true((uid_t)id == id);
2533
g_assert_cmpuint((unsigned int)id, ==, user);
2536
static void test_start_mandos_client_suid_rgid(test_fixture *fixture,
2537
__attribute__((unused))
2540
if(not is_privileged()){
2541
g_test_skip("Not privileged");
2545
bool mandos_client_exited = false;
2546
bool quit_now = false;
2547
__attribute__((cleanup(cleanup_close)))
2548
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2549
g_assert_cmpint(epoll_fd, >=, 0);
2550
__attribute__((cleanup(cleanup_queue)))
2551
task_queue *queue = create_queue();
2552
g_assert_nonnull(queue);
2553
__attribute__((cleanup(cleanup_buffer)))
2554
buffer password = {};
2555
bool password_is_read = false;
2556
const char helper_directory[] = "/nonexistent";
2557
const char *const argv[] = { "/usr/bin/id", "--group", "--real",
2562
const bool success = start_mandos_client(queue, epoll_fd,
2563
&mandos_client_exited,
2564
&quit_now, &password,
2566
&fixture->orig_sigaction,
2567
fixture->orig_sigmask,
2568
helper_directory, user,
2570
g_assert_true(success);
2571
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2573
struct timespec starttime, currtime;
2574
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2576
queue->next_run = 0;
2577
string_set cancelled_filenames = {};
2578
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2579
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2580
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2581
} while(((queue->length) > 0)
2583
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2585
g_assert_false(quit_now);
2586
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2587
g_assert_true(mandos_client_exited);
2589
g_assert_true(password_is_read);
2590
g_assert_nonnull(password.data);
2593
g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2595
g_assert_true((gid_t)id == id);
2597
g_assert_cmpuint((unsigned int)id, ==, group);
2600
static void test_start_mandos_client_read(test_fixture *fixture,
2601
__attribute__((unused))
2602
gconstpointer user_data){
2603
bool mandos_client_exited = false;
2604
bool quit_now = false;
2605
__attribute__((cleanup(cleanup_close)))
2606
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2607
g_assert_cmpint(epoll_fd, >=, 0);
2608
__attribute__((cleanup(cleanup_queue)))
2609
task_queue *queue = create_queue();
2610
g_assert_nonnull(queue);
2611
__attribute__((cleanup(cleanup_buffer)))
2612
buffer password = {};
2613
bool password_is_read = false;
2614
const char dummy_test_password[] = "dummy test password";
2615
const char helper_directory[] = "/nonexistent";
2616
const char *const argv[] = { "/bin/echo", "-n", dummy_test_password,
2619
const bool success = start_mandos_client(queue, epoll_fd,
2620
&mandos_client_exited,
2621
&quit_now, &password,
2623
&fixture->orig_sigaction,
2624
fixture->orig_sigmask,
2625
helper_directory, 0, 0,
2627
g_assert_true(success);
2628
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2630
struct timespec starttime, currtime;
2631
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2633
queue->next_run = 0;
2634
string_set cancelled_filenames = {};
2635
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2636
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2637
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2638
} while(((queue->length) > 0)
2640
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2642
g_assert_false(quit_now);
2643
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2644
g_assert_true(mandos_client_exited);
2646
g_assert_true(password_is_read);
2647
g_assert_cmpint((int)password.length, ==,
2648
sizeof(dummy_test_password)-1);
2649
g_assert_nonnull(password.data);
2650
g_assert_cmpint(memcmp(dummy_test_password, password.data,
2651
sizeof(dummy_test_password)-1), ==, 0);
2655
void test_start_mandos_client_helper_directory(test_fixture *fixture,
2656
__attribute__((unused))
2659
bool mandos_client_exited = false;
2660
bool quit_now = false;
2661
__attribute__((cleanup(cleanup_close)))
2662
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2663
g_assert_cmpint(epoll_fd, >=, 0);
2664
__attribute__((cleanup(cleanup_queue)))
2665
task_queue *queue = create_queue();
2666
g_assert_nonnull(queue);
2667
__attribute__((cleanup(cleanup_buffer)))
2668
buffer password = {};
2669
bool password_is_read = false;
2670
const char helper_directory[] = "/nonexistent";
2671
const char *const argv[] = { "/bin/sh", "-c",
2672
"printf %s \"${MANDOSPLUGINHELPERDIR}\"", NULL };
2674
const bool success = start_mandos_client(queue, epoll_fd,
2675
&mandos_client_exited,
2676
&quit_now, &password,
2678
&fixture->orig_sigaction,
2679
fixture->orig_sigmask,
2680
helper_directory, 0, 0,
2682
g_assert_true(success);
2683
g_assert_cmpuint((unsigned int)queue->length, >, 0);
2685
struct timespec starttime, currtime;
2686
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2688
queue->next_run = 0;
2689
string_set cancelled_filenames = {};
2690
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2691
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2692
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2693
} while(((queue->length) > 0)
2695
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2697
g_assert_false(quit_now);
2698
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2699
g_assert_true(mandos_client_exited);
2701
g_assert_true(password_is_read);
2702
g_assert_cmpint((int)password.length, ==,
2703
sizeof(helper_directory)-1);
2704
g_assert_nonnull(password.data);
2705
g_assert_cmpint(memcmp(helper_directory, password.data,
2706
sizeof(helper_directory)-1), ==, 0);
2709
__attribute__((nonnull, warn_unused_result))
2710
static bool proc_status_sigblk_to_sigset(const char *const,
2713
static void test_start_mandos_client_sigmask(test_fixture *fixture,
2714
__attribute__((unused))
2715
gconstpointer user_data){
2716
bool mandos_client_exited = false;
2717
bool quit_now = false;
2718
__attribute__((cleanup(cleanup_close)))
2719
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2720
g_assert_cmpint(epoll_fd, >=, 0);
2721
__attribute__((cleanup(cleanup_queue)))
2722
task_queue *queue = create_queue();
2723
g_assert_nonnull(queue);
2724
__attribute__((cleanup(cleanup_buffer)))
2725
buffer password = {};
2726
bool password_is_read = false;
2727
const char helper_directory[] = "/nonexistent";
2728
/* see proc(5) for format of /proc/self/status */
2729
const char *const argv[] = { "/usr/bin/awk",
2730
"$1==\"SigBlk:\"{ print $2 }", "/proc/self/status", NULL };
2732
g_assert_true(start_mandos_client(queue, epoll_fd,
2733
&mandos_client_exited, &quit_now,
2734
&password, &password_is_read,
2735
&fixture->orig_sigaction,
2736
fixture->orig_sigmask,
2737
helper_directory, 0, 0, argv));
2739
struct timespec starttime, currtime;
2740
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2742
queue->next_run = 0;
2743
string_set cancelled_filenames = {};
2744
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2745
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2746
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2747
} while((not (mandos_client_exited and password_is_read))
2749
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2750
g_assert_true(mandos_client_exited);
2751
g_assert_true(password_is_read);
2753
sigset_t parsed_sigmask;
2754
g_assert_true(proc_status_sigblk_to_sigset(password.data,
2757
for(int signum = 1; signum < NSIG; signum++){
2758
const bool has_signal = sigismember(&parsed_sigmask, signum);
2759
if(sigismember(&fixture->orig_sigmask, signum)){
2760
g_assert_true(has_signal);
2762
g_assert_false(has_signal);
2767
__attribute__((nonnull, warn_unused_result))
2768
static bool proc_status_sigblk_to_sigset(const char *const sigblk,
2769
sigset_t *const sigmask){
2770
/* parse /proc/PID/status SigBlk value and convert to a sigset_t */
2771
uintmax_t scanned_sigmask;
2772
if(sscanf(sigblk, "%" SCNxMAX " ", &scanned_sigmask) != 1){
2775
if(sigemptyset(sigmask) != 0){
2778
for(int signum = 1; signum < NSIG; signum++){
2779
if(scanned_sigmask & ((uintmax_t)1 << (signum-1))){
2780
if(sigaddset(sigmask, signum) != 0){
2788
static void run_task_with_stderr_to_dev_null(const task_context task,
2789
task_queue *const queue);
2792
void test_wait_for_mandos_client_exit_badpid(__attribute__((unused))
2793
test_fixture *fixture,
2794
__attribute__((unused))
2795
gconstpointer user_data){
2797
bool mandos_client_exited = false;
2798
bool quit_now = false;
2800
__attribute__((cleanup(cleanup_queue)))
2801
task_queue *queue = create_queue();
2802
g_assert_nonnull(queue);
2803
const task_context task = {
2804
.func=wait_for_mandos_client_exit,
2806
.mandos_client_exited=&mandos_client_exited,
2807
.quit_now=&quit_now,
2809
run_task_with_stderr_to_dev_null(task, queue);
2811
g_assert_false(mandos_client_exited);
2812
g_assert_true(quit_now);
2813
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2816
static void run_task_with_stderr_to_dev_null(const task_context task,
2817
task_queue *const queue){
2818
FILE *real_stderr = stderr;
2819
FILE *devnull = fopen("/dev/null", "we");
2820
g_assert_nonnull(devnull);
2823
task.func(task, queue);
2824
stderr = real_stderr;
2826
g_assert_cmpint(fclose(devnull), ==, 0);
2830
void test_wait_for_mandos_client_exit_noexit(test_fixture *fixture,
2831
__attribute__((unused))
2832
gconstpointer user_data){
2833
bool mandos_client_exited = false;
2834
bool quit_now = false;
2836
pid_t create_eternal_process(void){
2837
const pid_t pid = fork();
2838
if(pid == 0){ /* Child */
2839
if(not restore_signal_handler(&fixture->orig_sigaction)){
2840
_exit(EXIT_FAILURE);
2842
if(not restore_sigmask(&fixture->orig_sigmask)){
2843
_exit(EXIT_FAILURE);
2851
pid_t pid = create_eternal_process();
2852
g_assert_true(pid != -1);
2854
__attribute__((cleanup(cleanup_queue)))
2855
task_queue *queue = create_queue();
2856
g_assert_nonnull(queue);
2857
const task_context task = {
2858
.func=wait_for_mandos_client_exit,
2860
.mandos_client_exited=&mandos_client_exited,
2861
.quit_now=&quit_now,
2863
task.func(task, queue);
2865
g_assert_false(mandos_client_exited);
2866
g_assert_false(quit_now);
2867
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
2869
g_assert_nonnull(find_matching_task(queue, (task_context){
2870
.func=wait_for_mandos_client_exit,
2872
.mandos_client_exited=&mandos_client_exited,
2873
.quit_now=&quit_now,
2878
void test_wait_for_mandos_client_exit_success(test_fixture *fixture,
2879
__attribute__((unused))
2882
bool mandos_client_exited = false;
2883
bool quit_now = false;
2885
pid_t create_successful_process(void){
2886
const pid_t pid = fork();
2887
if(pid == 0){ /* Child */
2888
if(not restore_signal_handler(&fixture->orig_sigaction)){
2889
_exit(EXIT_FAILURE);
2891
if(not restore_sigmask(&fixture->orig_sigmask)){
2892
_exit(EXIT_FAILURE);
2898
const pid_t pid = create_successful_process();
2899
g_assert_true(pid != -1);
2901
__attribute__((cleanup(cleanup_queue)))
2902
task_queue *queue = create_queue();
2903
g_assert_nonnull(queue);
2904
const task_context initial_task = {
2905
.func=wait_for_mandos_client_exit,
2907
.mandos_client_exited=&mandos_client_exited,
2908
.quit_now=&quit_now,
2910
g_assert_true(add_to_queue(queue, initial_task));
2912
struct timespec starttime, currtime;
2913
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2914
__attribute__((cleanup(cleanup_close)))
2915
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2917
queue->next_run = 0;
2918
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2919
g_assert_true(run_queue(&queue, (string_set[]){{}}, &quit_now));
2920
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2921
} while((not mandos_client_exited)
2923
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2925
g_assert_true(mandos_client_exited);
2926
g_assert_false(quit_now);
2927
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2931
void test_wait_for_mandos_client_exit_failure(test_fixture *fixture,
2932
__attribute__((unused))
2935
bool mandos_client_exited = false;
2936
bool quit_now = false;
2938
pid_t create_failing_process(void){
2939
const pid_t pid = fork();
2940
if(pid == 0){ /* Child */
2941
if(not restore_signal_handler(&fixture->orig_sigaction)){
2942
_exit(EXIT_FAILURE);
2944
if(not restore_sigmask(&fixture->orig_sigmask)){
2945
_exit(EXIT_FAILURE);
2951
const pid_t pid = create_failing_process();
2952
g_assert_true(pid != -1);
2954
__attribute__((cleanup(string_set_clear)))
2955
string_set cancelled_filenames = {};
2956
__attribute__((cleanup(cleanup_close)))
2957
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2958
g_assert_cmpint(epoll_fd, >=, 0);
2959
__attribute__((cleanup(cleanup_queue)))
2960
task_queue *queue = create_queue();
2961
g_assert_nonnull(queue);
2962
g_assert_true(add_to_queue(queue, (task_context){
2963
.func=wait_for_mandos_client_exit,
2965
.mandos_client_exited=&mandos_client_exited,
2966
.quit_now=&quit_now,
2969
g_assert_true(sigismember(&fixture->orig_sigmask, SIGCHLD) == 0);
2971
__attribute__((cleanup(cleanup_close)))
2972
const int devnull_fd = open("/dev/null",
2973
O_WRONLY | O_CLOEXEC | O_NOCTTY);
2974
g_assert_cmpint(devnull_fd, >=, 0);
2975
__attribute__((cleanup(cleanup_close)))
2976
const int real_stderr_fd = dup(STDERR_FILENO);
2977
g_assert_cmpint(real_stderr_fd, >=, 0);
2979
struct timespec starttime, currtime;
2980
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2982
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2983
dup2(devnull_fd, STDERR_FILENO);
2984
const bool success = run_queue(&queue, &cancelled_filenames,
2986
dup2(real_stderr_fd, STDERR_FILENO);
2991
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2992
} while((not mandos_client_exited)
2994
and ((currtime.tv_sec - starttime.tv_sec) < 10));
2996
g_assert_true(quit_now);
2997
g_assert_true(mandos_client_exited);
2998
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3002
void test_wait_for_mandos_client_exit_killed(test_fixture *fixture,
3003
__attribute__((unused))
3004
gconstpointer user_data){
3005
bool mandos_client_exited = false;
3006
bool quit_now = false;
3008
pid_t create_killed_process(void){
3009
const pid_t pid = fork();
3010
if(pid == 0){ /* Child */
3011
if(not restore_signal_handler(&fixture->orig_sigaction)){
3012
_exit(EXIT_FAILURE);
3014
if(not restore_sigmask(&fixture->orig_sigmask)){
3015
_exit(EXIT_FAILURE);
3024
const pid_t pid = create_killed_process();
3025
g_assert_true(pid != -1);
3027
__attribute__((cleanup(string_set_clear)))
3028
string_set cancelled_filenames = {};
3029
__attribute__((cleanup(cleanup_close)))
3030
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3031
g_assert_cmpint(epoll_fd, >=, 0);
3032
__attribute__((cleanup(cleanup_queue)))
3033
task_queue *queue = create_queue();
3034
g_assert_nonnull(queue);
3035
g_assert_true(add_to_queue(queue, (task_context){
3036
.func=wait_for_mandos_client_exit,
3038
.mandos_client_exited=&mandos_client_exited,
3039
.quit_now=&quit_now,
3042
__attribute__((cleanup(cleanup_close)))
3043
const int devnull_fd = open("/dev/null",
3044
O_WRONLY | O_CLOEXEC, O_NOCTTY);
3045
g_assert_cmpint(devnull_fd, >=, 0);
3046
__attribute__((cleanup(cleanup_close)))
3047
const int real_stderr_fd = dup(STDERR_FILENO);
3048
g_assert_cmpint(real_stderr_fd, >=, 0);
3050
struct timespec starttime, currtime;
3051
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
3053
g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
3054
dup2(devnull_fd, STDERR_FILENO);
3055
const bool success = run_queue(&queue, &cancelled_filenames,
3057
dup2(real_stderr_fd, STDERR_FILENO);
3062
g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
3063
} while((not mandos_client_exited)
3065
and ((currtime.tv_sec - starttime.tv_sec) < 10));
3067
g_assert_true(mandos_client_exited);
3068
g_assert_true(quit_now);
3069
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3072
static bool epoll_set_does_not_contain(int, int);
3075
void test_read_mandos_client_output_readerror(__attribute__((unused))
3076
test_fixture *fixture,
3077
__attribute__((unused))
3080
__attribute__((cleanup(cleanup_close)))
3081
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3082
g_assert_cmpint(epoll_fd, >=, 0);
3084
__attribute__((cleanup(cleanup_buffer)))
3085
buffer password = {};
3087
/* Reading /proc/self/mem from offset 0 will always give EIO */
3088
const int fd = open("/proc/self/mem",
3089
O_RDONLY | O_CLOEXEC | O_NOCTTY);
3091
bool password_is_read = false;
3092
bool quit_now = false;
3093
__attribute__((cleanup(cleanup_queue)))
3094
task_queue *queue = create_queue();
3095
g_assert_nonnull(queue);
3097
task_context task = {
3098
.func=read_mandos_client_output,
3101
.password=&password,
3102
.password_is_read=&password_is_read,
3103
.quit_now=&quit_now,
3105
run_task_with_stderr_to_dev_null(task, queue);
3106
g_assert_false(password_is_read);
3107
g_assert_cmpint((int)password.length, ==, 0);
3108
g_assert_true(quit_now);
3109
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3111
g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
3113
g_assert_cmpint(close(fd), ==, -1);
3116
static bool epoll_set_does_not_contain(int epoll_fd, int fd){
3117
return not epoll_set_contains(epoll_fd, fd, 0);
3121
void test_read_mandos_client_output_nodata(__attribute__((unused))
3122
test_fixture *fixture,
3123
__attribute__((unused))
3124
gconstpointer user_data){
3125
__attribute__((cleanup(cleanup_close)))
3126
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3127
g_assert_cmpint(epoll_fd, >=, 0);
3130
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3132
__attribute__((cleanup(cleanup_buffer)))
3133
buffer password = {};
3135
bool password_is_read = false;
3136
bool quit_now = false;
3137
__attribute__((cleanup(cleanup_queue)))
3138
task_queue *queue = create_queue();
3139
g_assert_nonnull(queue);
3141
task_context task = {
3142
.func=read_mandos_client_output,
3145
.password=&password,
3146
.password_is_read=&password_is_read,
3147
.quit_now=&quit_now,
3149
task.func(task, queue);
3150
g_assert_false(password_is_read);
3151
g_assert_cmpint((int)password.length, ==, 0);
3152
g_assert_false(quit_now);
3153
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3155
g_assert_nonnull(find_matching_task(queue, (task_context){
3156
.func=read_mandos_client_output,
3159
.password=&password,
3160
.password_is_read=&password_is_read,
3161
.quit_now=&quit_now,
3164
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3165
EPOLLIN | EPOLLRDHUP));
3167
g_assert_cmpint(close(pipefds[1]), ==, 0);
3170
static void test_read_mandos_client_output_eof(__attribute__((unused))
3171
test_fixture *fixture,
3172
__attribute__((unused))
3175
__attribute__((cleanup(cleanup_close)))
3176
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3177
g_assert_cmpint(epoll_fd, >=, 0);
3180
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3181
g_assert_cmpint(close(pipefds[1]), ==, 0);
3183
__attribute__((cleanup(cleanup_buffer)))
3184
buffer password = {};
3186
bool password_is_read = false;
3187
bool quit_now = false;
3188
__attribute__((cleanup(cleanup_queue)))
3189
task_queue *queue = create_queue();
3190
g_assert_nonnull(queue);
3192
task_context task = {
3193
.func=read_mandos_client_output,
3196
.password=&password,
3197
.password_is_read=&password_is_read,
3198
.quit_now=&quit_now,
3200
task.func(task, queue);
3201
g_assert_true(password_is_read);
3202
g_assert_cmpint((int)password.length, ==, 0);
3203
g_assert_false(quit_now);
3204
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3206
g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
3208
g_assert_cmpint(close(pipefds[0]), ==, -1);
3212
void test_read_mandos_client_output_once(__attribute__((unused))
3213
test_fixture *fixture,
3214
__attribute__((unused))
3215
gconstpointer user_data){
3216
__attribute__((cleanup(cleanup_close)))
3217
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3218
g_assert_cmpint(epoll_fd, >=, 0);
3221
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3223
const char dummy_test_password[] = "dummy test password";
3224
/* Start with a pre-allocated buffer */
3225
__attribute__((cleanup(cleanup_buffer)))
3227
.data=malloc(sizeof(dummy_test_password)),
3229
.allocated=sizeof(dummy_test_password),
3231
g_assert_nonnull(password.data);
3232
if(mlock(password.data, password.allocated) != 0){
3233
g_assert_true(errno == EPERM or errno == ENOMEM);
3236
bool password_is_read = false;
3237
bool quit_now = false;
3238
__attribute__((cleanup(cleanup_queue)))
3239
task_queue *queue = create_queue();
3240
g_assert_nonnull(queue);
3242
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3243
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3244
sizeof(dummy_test_password)),
3245
==, (int)sizeof(dummy_test_password));
3247
task_context task = {
3248
.func=read_mandos_client_output,
3251
.password=&password,
3252
.password_is_read=&password_is_read,
3253
.quit_now=&quit_now,
3255
task.func(task, queue);
3257
g_assert_false(password_is_read);
3258
g_assert_cmpint((int)password.length, ==,
3259
(int)sizeof(dummy_test_password));
3260
g_assert_nonnull(password.data);
3261
g_assert_cmpint(memcmp(password.data, dummy_test_password,
3262
sizeof(dummy_test_password)), ==, 0);
3264
g_assert_false(quit_now);
3265
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3267
g_assert_nonnull(find_matching_task(queue, (task_context){
3268
.func=read_mandos_client_output,
3271
.password=&password,
3272
.password_is_read=&password_is_read,
3273
.quit_now=&quit_now,
3276
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3277
EPOLLIN | EPOLLRDHUP));
3279
g_assert_cmpint(close(pipefds[1]), ==, 0);
3283
void test_read_mandos_client_output_malloc(__attribute__((unused))
3284
test_fixture *fixture,
3285
__attribute__((unused))
3286
gconstpointer user_data){
3287
__attribute__((cleanup(cleanup_close)))
3288
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3289
g_assert_cmpint(epoll_fd, >=, 0);
3292
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3294
const char dummy_test_password[] = "dummy test password";
3295
/* Start with an empty buffer */
3296
__attribute__((cleanup(cleanup_buffer)))
3297
buffer password = {};
3299
bool password_is_read = false;
3300
bool quit_now = false;
3301
__attribute__((cleanup(cleanup_queue)))
3302
task_queue *queue = create_queue();
3303
g_assert_nonnull(queue);
3305
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3306
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3307
sizeof(dummy_test_password)),
3308
==, (int)sizeof(dummy_test_password));
3310
task_context task = {
3311
.func=read_mandos_client_output,
3314
.password=&password,
3315
.password_is_read=&password_is_read,
3316
.quit_now=&quit_now,
3318
task.func(task, queue);
3320
g_assert_false(password_is_read);
3321
g_assert_cmpint((int)password.length, ==,
3322
(int)sizeof(dummy_test_password));
3323
g_assert_nonnull(password.data);
3324
g_assert_cmpint(memcmp(password.data, dummy_test_password,
3325
sizeof(dummy_test_password)), ==, 0);
3327
g_assert_false(quit_now);
3328
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3330
g_assert_nonnull(find_matching_task(queue, (task_context){
3331
.func=read_mandos_client_output,
3334
.password=&password,
3335
.password_is_read=&password_is_read,
3336
.quit_now=&quit_now,
3339
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3340
EPOLLIN | EPOLLRDHUP));
3342
g_assert_cmpint(close(pipefds[1]), ==, 0);
3346
void test_read_mandos_client_output_append(__attribute__((unused))
3347
test_fixture *fixture,
3348
__attribute__((unused))
3349
gconstpointer user_data){
3350
__attribute__((cleanup(cleanup_close)))
3351
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3352
g_assert_cmpint(epoll_fd, >=, 0);
3355
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3357
const char dummy_test_password[] = "dummy test password";
3358
__attribute__((cleanup(cleanup_buffer)))
3360
.data=malloc(PIPE_BUF),
3362
.allocated=PIPE_BUF,
3364
g_assert_nonnull(password.data);
3365
if(mlock(password.data, password.allocated) != 0){
3366
g_assert_true(errno == EPERM or errno == ENOMEM);
3369
memset(password.data, 'x', PIPE_BUF);
3370
char password_expected[PIPE_BUF];
3371
memcpy(password_expected, password.data, PIPE_BUF);
3373
bool password_is_read = false;
3374
bool quit_now = false;
3375
__attribute__((cleanup(cleanup_queue)))
3376
task_queue *queue = create_queue();
3377
g_assert_nonnull(queue);
3379
g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3380
g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3381
sizeof(dummy_test_password)),
3382
==, (int)sizeof(dummy_test_password));
3384
task_context task = {
3385
.func=read_mandos_client_output,
3388
.password=&password,
3389
.password_is_read=&password_is_read,
3390
.quit_now=&quit_now,
3392
task.func(task, queue);
3394
g_assert_false(password_is_read);
3395
g_assert_cmpint((int)password.length, ==,
3396
PIPE_BUF + sizeof(dummy_test_password));
3397
g_assert_nonnull(password.data);
3398
g_assert_cmpint(memcmp(password_expected, password.data, PIPE_BUF),
3400
g_assert_cmpint(memcmp(password.data + PIPE_BUF,
3401
dummy_test_password,
3402
sizeof(dummy_test_password)), ==, 0);
3403
g_assert_false(quit_now);
3404
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3406
g_assert_nonnull(find_matching_task(queue, (task_context){
3407
.func=read_mandos_client_output,
3410
.password=&password,
3411
.password_is_read=&password_is_read,
3412
.quit_now=&quit_now,
3415
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3416
EPOLLIN | EPOLLRDHUP));
3419
static char *make_temporary_directory(void);
3421
static void test_add_inotify_dir_watch(__attribute__((unused))
3422
test_fixture *fixture,
3423
__attribute__((unused))
3424
gconstpointer user_data){
3425
__attribute__((cleanup(cleanup_close)))
3426
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3427
g_assert_cmpint(epoll_fd, >=, 0);
3428
__attribute__((cleanup(cleanup_queue)))
3429
task_queue *queue = create_queue();
3430
g_assert_nonnull(queue);
3431
__attribute__((cleanup(string_set_clear)))
3432
string_set cancelled_filenames = {};
3433
const mono_microsecs current_time = 0;
3435
bool quit_now = false;
3436
buffer password = {};
3437
bool mandos_client_exited = false;
3438
bool password_is_read = false;
3440
__attribute__((cleanup(cleanup_string)))
3441
char *tempdir = make_temporary_directory();
3442
g_assert_nonnull(tempdir);
3444
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3446
&cancelled_filenames,
3448
&mandos_client_exited,
3449
&password_is_read));
3451
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3453
const task_context *const added_read_task
3454
= find_matching_task(queue, (task_context){
3455
.func=read_inotify_event,
3457
.quit_now=&quit_now,
3458
.password=&password,
3460
.cancelled_filenames=&cancelled_filenames,
3461
.current_time=¤t_time,
3462
.mandos_client_exited=&mandos_client_exited,
3463
.password_is_read=&password_is_read,
3465
g_assert_nonnull(added_read_task);
3467
g_assert_cmpint(added_read_task->fd, >, 2);
3468
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3469
g_assert_true(epoll_set_contains(added_read_task->epoll_fd,
3470
added_read_task->fd,
3471
EPOLLIN | EPOLLRDHUP));
3473
g_assert_cmpint(rmdir(tempdir), ==, 0);
3476
static char *make_temporary_directory(void){
3477
char *name = strdup("/tmp/mandosXXXXXX");
3478
g_assert_nonnull(name);
3479
char *result = mkdtemp(name);
3486
static void test_add_inotify_dir_watch_fail(__attribute__((unused))
3487
test_fixture *fixture,
3488
__attribute__((unused))
3489
gconstpointer user_data){
3490
__attribute__((cleanup(cleanup_close)))
3491
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3492
g_assert_cmpint(epoll_fd, >=, 0);
3493
__attribute__((cleanup(cleanup_queue)))
3494
task_queue *queue = create_queue();
3495
g_assert_nonnull(queue);
3496
__attribute__((cleanup(string_set_clear)))
3497
string_set cancelled_filenames = {};
3498
const mono_microsecs current_time = 0;
3500
bool quit_now = false;
3501
buffer password = {};
3502
bool mandos_client_exited = false;
3503
bool password_is_read = false;
3505
const char nonexistent_dir[] = "/nonexistent";
3507
FILE *real_stderr = stderr;
3508
FILE *devnull = fopen("/dev/null", "we");
3509
g_assert_nonnull(devnull);
3511
g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3512
&password, nonexistent_dir,
3513
&cancelled_filenames,
3515
&mandos_client_exited,
3516
&password_is_read));
3517
stderr = real_stderr;
3518
g_assert_cmpint(fclose(devnull), ==, 0);
3520
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3523
static void test_add_inotify_dir_watch_nondir(__attribute__((unused))
3524
test_fixture *fixture,
3525
__attribute__((unused))
3528
__attribute__((cleanup(cleanup_close)))
3529
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3530
g_assert_cmpint(epoll_fd, >=, 0);
3531
__attribute__((cleanup(cleanup_queue)))
3532
task_queue *queue = create_queue();
3533
g_assert_nonnull(queue);
3534
__attribute__((cleanup(string_set_clear)))
3535
string_set cancelled_filenames = {};
3536
const mono_microsecs current_time = 0;
3538
bool quit_now = false;
3539
buffer password = {};
3540
bool mandos_client_exited = false;
3541
bool password_is_read = false;
3543
const char not_a_directory[] = "/dev/tty";
3545
FILE *real_stderr = stderr;
3546
FILE *devnull = fopen("/dev/null", "we");
3547
g_assert_nonnull(devnull);
3549
g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3550
&password, not_a_directory,
3551
&cancelled_filenames,
3553
&mandos_client_exited,
3554
&password_is_read));
3555
stderr = real_stderr;
3556
g_assert_cmpint(fclose(devnull), ==, 0);
3558
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3561
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3562
test_fixture *fixture,
3563
__attribute__((unused))
3566
__attribute__((cleanup(cleanup_close)))
3567
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3568
g_assert_cmpint(epoll_fd, >=, 0);
3569
__attribute__((cleanup(cleanup_queue)))
3570
task_queue *queue = create_queue();
3571
g_assert_nonnull(queue);
3572
__attribute__((cleanup(string_set_clear)))
3573
string_set cancelled_filenames = {};
3574
const mono_microsecs current_time = 0;
3576
bool quit_now = false;
3577
buffer password = {};
3578
bool mandos_client_exited = false;
3579
bool password_is_read = false;
3581
__attribute__((cleanup(cleanup_string)))
3582
char *tempdir = make_temporary_directory();
3583
g_assert_nonnull(tempdir);
3585
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3587
&cancelled_filenames,
3589
&mandos_client_exited,
3590
&password_is_read));
3592
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3594
const task_context *const added_read_task
3595
= find_matching_task(queue,
3596
(task_context){ .func=read_inotify_event });
3597
g_assert_nonnull(added_read_task);
3599
g_assert_cmpint(added_read_task->fd, >, 2);
3600
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3602
/* "sufficient to read at least one event." - inotify(7) */
3603
const size_t ievent_size = (sizeof(struct inotify_event)
3605
struct inotify_event *ievent = malloc(ievent_size);
3606
g_assert_nonnull(ievent);
3608
g_assert_cmpint(read(added_read_task->fd, ievent, ievent_size), ==,
3610
g_assert_cmpint(errno, ==, EAGAIN);
3614
g_assert_cmpint(rmdir(tempdir), ==, 0);
3617
static char *make_temporary_file_in_directory(const char
3621
void test_add_inotify_dir_watch_IN_CLOSE_WRITE(__attribute__((unused))
3622
test_fixture *fixture,
3623
__attribute__((unused))
3626
__attribute__((cleanup(cleanup_close)))
3627
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3628
g_assert_cmpint(epoll_fd, >=, 0);
3629
__attribute__((cleanup(cleanup_queue)))
3630
task_queue *queue = create_queue();
3631
g_assert_nonnull(queue);
3632
__attribute__((cleanup(string_set_clear)))
3633
string_set cancelled_filenames = {};
3634
const mono_microsecs current_time = 0;
3636
bool quit_now = false;
3637
buffer password = {};
3638
bool mandos_client_exited = false;
3639
bool password_is_read = false;
3641
__attribute__((cleanup(cleanup_string)))
3642
char *tempdir = make_temporary_directory();
3643
g_assert_nonnull(tempdir);
3645
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3647
&cancelled_filenames,
3649
&mandos_client_exited,
3650
&password_is_read));
3652
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3654
const task_context *const added_read_task
3655
= find_matching_task(queue,
3656
(task_context){ .func=read_inotify_event });
3657
g_assert_nonnull(added_read_task);
3659
g_assert_cmpint(added_read_task->fd, >, 2);
3660
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3662
__attribute__((cleanup(cleanup_string)))
3663
char *filename = make_temporary_file_in_directory(tempdir);
3664
g_assert_nonnull(filename);
3666
/* "sufficient to read at least one event." - inotify(7) */
3667
const size_t ievent_size = (sizeof(struct inotify_event)
3669
struct inotify_event *ievent = malloc(ievent_size);
3670
g_assert_nonnull(ievent);
3672
ssize_t read_size = 0;
3673
read_size = read(added_read_task->fd, ievent, ievent_size);
3675
g_assert_cmpint((int)read_size, >, 0);
3676
g_assert_true(ievent->mask & IN_CLOSE_WRITE);
3677
g_assert_cmpstr(ievent->name, ==, basename(filename));
3681
g_assert_cmpint(unlink(filename), ==, 0);
3682
g_assert_cmpint(rmdir(tempdir), ==, 0);
3685
static char *make_temporary_prefixed_file_in_directory(const char
3689
char *filename = NULL;
3690
g_assert_cmpint(asprintf(&filename, "%s/%sXXXXXX", dir, prefix),
3692
g_assert_nonnull(filename);
3693
const int fd = mkostemp(filename, O_CLOEXEC);
3694
g_assert_cmpint(fd, >=, 0);
3695
g_assert_cmpint(close(fd), ==, 0);
3699
static char *make_temporary_file_in_directory(const char
3701
return make_temporary_prefixed_file_in_directory("temp", dir);
3705
void test_add_inotify_dir_watch_IN_MOVED_TO(__attribute__((unused))
3706
test_fixture *fixture,
3707
__attribute__((unused))
3708
gconstpointer user_data){
3709
__attribute__((cleanup(cleanup_close)))
3710
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3711
g_assert_cmpint(epoll_fd, >=, 0);
3712
__attribute__((cleanup(cleanup_queue)))
3713
task_queue *queue = create_queue();
3714
g_assert_nonnull(queue);
3715
__attribute__((cleanup(string_set_clear)))
3716
string_set cancelled_filenames = {};
3717
const mono_microsecs current_time = 0;
3719
bool quit_now = false;
3720
buffer password = {};
3721
bool mandos_client_exited = false;
3722
bool password_is_read = false;
3724
__attribute__((cleanup(cleanup_string)))
3725
char *watchdir = make_temporary_directory();
3726
g_assert_nonnull(watchdir);
3728
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3729
&password, watchdir,
3730
&cancelled_filenames,
3732
&mandos_client_exited,
3733
&password_is_read));
3735
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3737
const task_context *const added_read_task
3738
= find_matching_task(queue,
3739
(task_context){ .func=read_inotify_event });
3740
g_assert_nonnull(added_read_task);
3742
g_assert_cmpint(added_read_task->fd, >, 2);
3743
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3745
char *sourcedir = make_temporary_directory();
3746
g_assert_nonnull(sourcedir);
3748
__attribute__((cleanup(cleanup_string)))
3749
char *filename = make_temporary_file_in_directory(sourcedir);
3750
g_assert_nonnull(filename);
3752
__attribute__((cleanup(cleanup_string)))
3753
char *targetfilename = NULL;
3754
g_assert_cmpint(asprintf(&targetfilename, "%s/%s", watchdir,
3755
basename(filename)), >, 0);
3756
g_assert_nonnull(targetfilename);
3758
g_assert_cmpint(rename(filename, targetfilename), ==, 0);
3759
g_assert_cmpint(rmdir(sourcedir), ==, 0);
3762
/* "sufficient to read at least one event." - inotify(7) */
3763
const size_t ievent_size = (sizeof(struct inotify_event)
3765
struct inotify_event *ievent = malloc(ievent_size);
3766
g_assert_nonnull(ievent);
3768
ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3770
g_assert_cmpint((int)read_size, >, 0);
3771
g_assert_true(ievent->mask & IN_MOVED_TO);
3772
g_assert_cmpstr(ievent->name, ==, basename(targetfilename));
3776
g_assert_cmpint(unlink(targetfilename), ==, 0);
3777
g_assert_cmpint(rmdir(watchdir), ==, 0);
3781
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
3782
test_fixture *fixture,
3783
__attribute__((unused))
3786
__attribute__((cleanup(cleanup_close)))
3787
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3788
g_assert_cmpint(epoll_fd, >=, 0);
3789
__attribute__((cleanup(cleanup_queue)))
3790
task_queue *queue = create_queue();
3791
g_assert_nonnull(queue);
3792
__attribute__((cleanup(string_set_clear)))
3793
string_set cancelled_filenames = {};
3794
const mono_microsecs current_time = 0;
3796
bool quit_now = false;
3797
buffer password = {};
3798
bool mandos_client_exited = false;
3799
bool password_is_read = false;
3801
__attribute__((cleanup(cleanup_string)))
3802
char *tempdir = make_temporary_directory();
3803
g_assert_nonnull(tempdir);
3805
__attribute__((cleanup(cleanup_string)))
3806
char *tempfilename = make_temporary_file_in_directory(tempdir);
3807
g_assert_nonnull(tempfilename);
3809
__attribute__((cleanup(cleanup_string)))
3810
char *targetdir = make_temporary_directory();
3811
g_assert_nonnull(targetdir);
3813
__attribute__((cleanup(cleanup_string)))
3814
char *targetfilename = NULL;
3815
g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
3816
basename(tempfilename)), >, 0);
3817
g_assert_nonnull(targetfilename);
3819
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3821
&cancelled_filenames,
3823
&mandos_client_exited,
3824
&password_is_read));
3826
g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
3828
const task_context *const added_read_task
3829
= find_matching_task(queue,
3830
(task_context){ .func=read_inotify_event });
3831
g_assert_nonnull(added_read_task);
3833
/* "sufficient to read at least one event." - inotify(7) */
3834
const size_t ievent_size = (sizeof(struct inotify_event)
3836
struct inotify_event *ievent = malloc(ievent_size);
3837
g_assert_nonnull(ievent);
3839
ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3841
g_assert_cmpint((int)read_size, >, 0);
3842
g_assert_true(ievent->mask & IN_MOVED_FROM);
3843
g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
3847
g_assert_cmpint(unlink(targetfilename), ==, 0);
3848
g_assert_cmpint(rmdir(targetdir), ==, 0);
3849
g_assert_cmpint(rmdir(tempdir), ==, 0);
3853
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3854
test_fixture *fixture,
3855
__attribute__((unused))
3856
gconstpointer user_data){
3857
__attribute__((cleanup(cleanup_close)))
3858
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3859
g_assert_cmpint(epoll_fd, >=, 0);
3860
__attribute__((cleanup(cleanup_queue)))
3861
task_queue *queue = create_queue();
3862
g_assert_nonnull(queue);
3863
__attribute__((cleanup(string_set_clear)))
3864
string_set cancelled_filenames = {};
3865
const mono_microsecs current_time = 0;
3867
bool quit_now = false;
3868
buffer password = {};
3869
bool mandos_client_exited = false;
3870
bool password_is_read = false;
3872
__attribute__((cleanup(cleanup_string)))
3873
char *tempdir = make_temporary_directory();
3874
g_assert_nonnull(tempdir);
3876
__attribute__((cleanup(cleanup_string)))
3877
char *tempfile = make_temporary_file_in_directory(tempdir);
3878
g_assert_nonnull(tempfile);
3880
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3882
&cancelled_filenames,
3884
&mandos_client_exited,
3885
&password_is_read));
3886
g_assert_cmpint(unlink(tempfile), ==, 0);
3888
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3890
const task_context *const added_read_task
3891
= find_matching_task(queue,
3892
(task_context){ .func=read_inotify_event });
3893
g_assert_nonnull(added_read_task);
3895
g_assert_cmpint(added_read_task->fd, >, 2);
3896
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3898
/* "sufficient to read at least one event." - inotify(7) */
3899
const size_t ievent_size = (sizeof(struct inotify_event)
3901
struct inotify_event *ievent = malloc(ievent_size);
3902
g_assert_nonnull(ievent);
3904
ssize_t read_size = 0;
3905
read_size = read(added_read_task->fd, ievent, ievent_size);
3907
g_assert_cmpint((int)read_size, >, 0);
3908
g_assert_true(ievent->mask & IN_DELETE);
3909
g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3913
g_assert_cmpint(rmdir(tempdir), ==, 0);
3917
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
3918
test_fixture *fixture,
3919
__attribute__((unused))
3922
__attribute__((cleanup(cleanup_close)))
3923
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3924
g_assert_cmpint(epoll_fd, >=, 0);
3925
__attribute__((cleanup(cleanup_queue)))
3926
task_queue *queue = create_queue();
3927
g_assert_nonnull(queue);
3928
__attribute__((cleanup(string_set_clear)))
3929
string_set cancelled_filenames = {};
3930
const mono_microsecs current_time = 0;
3932
bool quit_now = false;
3933
buffer password = {};
3934
bool mandos_client_exited = false;
3935
bool password_is_read = false;
3937
__attribute__((cleanup(cleanup_string)))
3938
char *tempdir = make_temporary_directory();
3939
g_assert_nonnull(tempdir);
3941
__attribute__((cleanup(cleanup_string)))
3942
char *tempfile = make_temporary_file_in_directory(tempdir);
3943
g_assert_nonnull(tempfile);
3944
int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
3946
g_assert_cmpint(tempfile_fd, >, 2);
3948
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3950
&cancelled_filenames,
3952
&mandos_client_exited,
3953
&password_is_read));
3954
g_assert_cmpint(unlink(tempfile), ==, 0);
3956
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3958
const task_context *const added_read_task
3959
= find_matching_task(queue,
3960
(task_context){ .func=read_inotify_event });
3961
g_assert_nonnull(added_read_task);
3963
g_assert_cmpint(added_read_task->fd, >, 2);
3964
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3966
/* "sufficient to read at least one event." - inotify(7) */
3967
const size_t ievent_size = (sizeof(struct inotify_event)
3969
struct inotify_event *ievent = malloc(ievent_size);
3970
g_assert_nonnull(ievent);
3972
ssize_t read_size = 0;
3973
read_size = read(added_read_task->fd, ievent, ievent_size);
3975
g_assert_cmpint((int)read_size, >, 0);
3976
g_assert_true(ievent->mask & IN_DELETE);
3977
g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3979
g_assert_cmpint(close(tempfile_fd), ==, 0);
3981
/* IN_EXCL_UNLINK should make the closing of the previously unlinked
3982
file not appear as an ievent, so we should not see it now. */
3983
read_size = read(added_read_task->fd, ievent, ievent_size);
3984
g_assert_cmpint((int)read_size, ==, -1);
3985
g_assert_true(errno == EAGAIN);
3989
g_assert_cmpint(rmdir(tempdir), ==, 0);
3992
static void test_read_inotify_event_readerror(__attribute__((unused))
3993
test_fixture *fixture,
3994
__attribute__((unused))
3997
__attribute__((cleanup(cleanup_close)))
3998
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3999
g_assert_cmpint(epoll_fd, >=, 0);
4000
const mono_microsecs current_time = 0;
4002
/* Reading /proc/self/mem from offset 0 will always result in EIO */
4003
const int fd = open("/proc/self/mem",
4004
O_RDONLY | O_CLOEXEC | O_NOCTTY);
4006
bool quit_now = false;
4007
__attribute__((cleanup(cleanup_queue)))
4008
task_queue *queue = create_queue();
4009
g_assert_nonnull(queue);
4011
task_context task = {
4012
.func=read_inotify_event,
4015
.quit_now=&quit_now,
4016
.filename=strdup("/nonexistent"),
4017
.cancelled_filenames = &(string_set){},
4019
.current_time=¤t_time,
4021
g_assert_nonnull(task.filename);
4022
run_task_with_stderr_to_dev_null(task, queue);
4023
g_assert_true(quit_now);
4024
g_assert_true(queue->next_run == 0);
4025
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4027
g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
4029
g_assert_cmpint(close(fd), ==, -1);
4032
static void test_read_inotify_event_bad_epoll(__attribute__((unused))
4033
test_fixture *fixture,
4034
__attribute__((unused))
4037
const mono_microsecs current_time = 17;
4040
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4041
const int epoll_fd = pipefds[0]; /* This will obviously fail */
4043
bool quit_now = false;
4044
buffer password = {};
4045
bool mandos_client_exited = false;
4046
bool password_is_read = false;
4047
__attribute__((cleanup(cleanup_queue)))
4048
task_queue *queue = create_queue();
4049
g_assert_nonnull(queue);
4051
task_context task = {
4052
.func=read_inotify_event,
4055
.quit_now=&quit_now,
4056
.password=&password,
4057
.filename=strdup("/nonexistent"),
4058
.cancelled_filenames = &(string_set){},
4060
.current_time=¤t_time,
4061
.mandos_client_exited=&mandos_client_exited,
4062
.password_is_read=&password_is_read,
4064
g_assert_nonnull(task.filename);
4065
run_task_with_stderr_to_dev_null(task, queue);
4067
g_assert_nonnull(find_matching_task(queue, task));
4068
g_assert_true(queue->next_run == 1000000 + current_time);
4070
g_assert_cmpint(close(pipefds[0]), ==, 0);
4071
g_assert_cmpint(close(pipefds[1]), ==, 0);
4074
static void test_read_inotify_event_nodata(__attribute__((unused))
4075
test_fixture *fixture,
4076
__attribute__((unused))
4077
gconstpointer user_data){
4078
__attribute__((cleanup(cleanup_close)))
4079
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4080
g_assert_cmpint(epoll_fd, >=, 0);
4081
const mono_microsecs current_time = 0;
4084
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4086
bool quit_now = false;
4087
buffer password = {};
4088
bool mandos_client_exited = false;
4089
bool password_is_read = false;
4090
__attribute__((cleanup(cleanup_queue)))
4091
task_queue *queue = create_queue();
4092
g_assert_nonnull(queue);
4094
task_context task = {
4095
.func=read_inotify_event,
4098
.quit_now=&quit_now,
4099
.password=&password,
4100
.filename=strdup("/nonexistent"),
4101
.cancelled_filenames = &(string_set){},
4103
.current_time=¤t_time,
4104
.mandos_client_exited=&mandos_client_exited,
4105
.password_is_read=&password_is_read,
4107
g_assert_nonnull(task.filename);
4108
task.func(task, queue);
4109
g_assert_false(quit_now);
4110
g_assert_true(queue->next_run == 0);
4111
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4113
g_assert_nonnull(find_matching_task(queue, (task_context){
4114
.func=read_inotify_event,
4117
.quit_now=&quit_now,
4118
.password=&password,
4119
.filename=task.filename,
4120
.cancelled_filenames=task.cancelled_filenames,
4121
.current_time=¤t_time,
4122
.mandos_client_exited=&mandos_client_exited,
4123
.password_is_read=&password_is_read,
4126
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4127
EPOLLIN | EPOLLRDHUP));
4129
g_assert_cmpint(close(pipefds[1]), ==, 0);
4132
static void test_read_inotify_event_eof(__attribute__((unused))
4133
test_fixture *fixture,
4134
__attribute__((unused))
4135
gconstpointer user_data){
4136
__attribute__((cleanup(cleanup_close)))
4137
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4138
g_assert_cmpint(epoll_fd, >=, 0);
4139
const mono_microsecs current_time = 0;
4142
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4143
g_assert_cmpint(close(pipefds[1]), ==, 0);
4145
bool quit_now = false;
4146
buffer password = {};
4147
__attribute__((cleanup(cleanup_queue)))
4148
task_queue *queue = create_queue();
4149
g_assert_nonnull(queue);
4151
task_context task = {
4152
.func=read_inotify_event,
4155
.quit_now=&quit_now,
4156
.password=&password,
4157
.filename=strdup("/nonexistent"),
4158
.cancelled_filenames = &(string_set){},
4160
.current_time=¤t_time,
4162
run_task_with_stderr_to_dev_null(task, queue);
4163
g_assert_true(quit_now);
4164
g_assert_true(queue->next_run == 0);
4165
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4167
g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
4169
g_assert_cmpint(close(pipefds[0]), ==, -1);
4173
void test_read_inotify_event_IN_CLOSE_WRITE(__attribute__((unused))
4174
test_fixture *fixture,
4175
__attribute__((unused))
4176
gconstpointer user_data){
4177
__attribute__((cleanup(cleanup_close)))
4178
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4179
g_assert_cmpint(epoll_fd, >=, 0);
4180
const mono_microsecs current_time = 0;
4183
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4185
/* "sufficient to read at least one event." - inotify(7) */
4186
const size_t ievent_max_size = (sizeof(struct inotify_event)
4188
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4190
struct inotify_event event;
4191
char name_buffer[NAME_MAX + 1];
4193
struct inotify_event *const ievent = &ievent_buffer.event;
4195
const char dummy_file_name[] = "ask.dummy_file_name";
4196
ievent->mask = IN_CLOSE_WRITE;
4197
ievent->len = sizeof(dummy_file_name);
4198
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4199
const size_t ievent_size = (sizeof(struct inotify_event)
4200
+ sizeof(dummy_file_name));
4201
#if defined(__GNUC__) and __GNUC__ >= 11
4202
#pragma GCC diagnostic push
4203
/* ievent is pointing into a struct which is of sufficient size */
4204
#pragma GCC diagnostic ignored "-Wstringop-overread"
4206
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4208
#if defined(__GNUC__) and __GNUC__ >= 11
4209
#pragma GCC diagnostic pop
4211
g_assert_cmpint(close(pipefds[1]), ==, 0);
4213
bool quit_now = false;
4214
buffer password = {};
4215
bool mandos_client_exited = false;
4216
bool password_is_read = false;
4217
__attribute__((cleanup(cleanup_queue)))
4218
task_queue *queue = create_queue();
4219
g_assert_nonnull(queue);
4221
task_context task = {
4222
.func=read_inotify_event,
4225
.quit_now=&quit_now,
4226
.password=&password,
4227
.filename=strdup("/nonexistent"),
4228
.cancelled_filenames = &(string_set){},
4230
.current_time=¤t_time,
4231
.mandos_client_exited=&mandos_client_exited,
4232
.password_is_read=&password_is_read,
4234
task.func(task, queue);
4235
g_assert_false(quit_now);
4236
g_assert_true(queue->next_run != 0);
4237
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4239
g_assert_nonnull(find_matching_task(queue, (task_context){
4240
.func=read_inotify_event,
4243
.quit_now=&quit_now,
4244
.password=&password,
4245
.filename=task.filename,
4246
.cancelled_filenames=task.cancelled_filenames,
4247
.current_time=¤t_time,
4248
.mandos_client_exited=&mandos_client_exited,
4249
.password_is_read=&password_is_read,
4252
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4253
EPOLLIN | EPOLLRDHUP));
4255
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4257
__attribute__((cleanup(cleanup_string)))
4258
char *filename = NULL;
4259
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4260
dummy_file_name), >, 0);
4261
g_assert_nonnull(filename);
4262
g_assert_nonnull(find_matching_task(queue, (task_context){
4263
.func=open_and_parse_question,
4266
.question_filename=filename,
4267
.password=&password,
4268
.cancelled_filenames=task.cancelled_filenames,
4269
.current_time=¤t_time,
4270
.mandos_client_exited=&mandos_client_exited,
4271
.password_is_read=&password_is_read,
4276
void test_read_inotify_event_IN_MOVED_TO(__attribute__((unused))
4277
test_fixture *fixture,
4278
__attribute__((unused))
4279
gconstpointer user_data){
4280
__attribute__((cleanup(cleanup_close)))
4281
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4282
g_assert_cmpint(epoll_fd, >=, 0);
4283
const mono_microsecs current_time = 0;
4286
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4288
/* "sufficient to read at least one event." - inotify(7) */
4289
const size_t ievent_max_size = (sizeof(struct inotify_event)
4291
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4293
struct inotify_event event;
4294
char name_buffer[NAME_MAX + 1];
4296
struct inotify_event *const ievent = &ievent_buffer.event;
4298
const char dummy_file_name[] = "ask.dummy_file_name";
4299
ievent->mask = IN_MOVED_TO;
4300
ievent->len = sizeof(dummy_file_name);
4301
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4302
const size_t ievent_size = (sizeof(struct inotify_event)
4303
+ sizeof(dummy_file_name));
4304
#if defined(__GNUC__) and __GNUC__ >= 11
4305
#pragma GCC diagnostic push
4306
/* ievent is pointing into a struct which is of sufficient size */
4307
#pragma GCC diagnostic ignored "-Wstringop-overread"
4309
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4311
#if defined(__GNUC__) and __GNUC__ >= 11
4312
#pragma GCC diagnostic pop
4314
g_assert_cmpint(close(pipefds[1]), ==, 0);
4316
bool quit_now = false;
4317
buffer password = {};
4318
bool mandos_client_exited = false;
4319
bool password_is_read = false;
4320
__attribute__((cleanup(cleanup_queue)))
4321
task_queue *queue = create_queue();
4322
g_assert_nonnull(queue);
4324
task_context task = {
4325
.func=read_inotify_event,
4328
.quit_now=&quit_now,
4329
.password=&password,
4330
.filename=strdup("/nonexistent"),
4331
.cancelled_filenames = &(string_set){},
4333
.current_time=¤t_time,
4334
.mandos_client_exited=&mandos_client_exited,
4335
.password_is_read=&password_is_read,
4337
task.func(task, queue);
4338
g_assert_false(quit_now);
4339
g_assert_true(queue->next_run != 0);
4340
g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4342
g_assert_nonnull(find_matching_task(queue, (task_context){
4343
.func=read_inotify_event,
4346
.quit_now=&quit_now,
4347
.password=&password,
4348
.filename=task.filename,
4349
.cancelled_filenames=task.cancelled_filenames,
4350
.current_time=¤t_time,
4351
.mandos_client_exited=&mandos_client_exited,
4352
.password_is_read=&password_is_read,
4355
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4356
EPOLLIN | EPOLLRDHUP));
4358
g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4360
__attribute__((cleanup(cleanup_string)))
4361
char *filename = NULL;
4362
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4363
dummy_file_name), >, 0);
4364
g_assert_nonnull(filename);
4365
g_assert_nonnull(find_matching_task(queue, (task_context){
4366
.func=open_and_parse_question,
4369
.question_filename=filename,
4370
.password=&password,
4371
.cancelled_filenames=task.cancelled_filenames,
4372
.current_time=¤t_time,
4373
.mandos_client_exited=&mandos_client_exited,
4374
.password_is_read=&password_is_read,
4379
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
4380
test_fixture *fixture,
4381
__attribute__((unused))
4382
gconstpointer user_data){
4383
__attribute__((cleanup(cleanup_close)))
4384
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4385
g_assert_cmpint(epoll_fd, >=, 0);
4386
__attribute__((cleanup(string_set_clear)))
4387
string_set cancelled_filenames = {};
4388
const mono_microsecs current_time = 0;
4391
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4393
/* "sufficient to read at least one event." - inotify(7) */
4394
const size_t ievent_max_size = (sizeof(struct inotify_event)
4396
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4398
struct inotify_event event;
4399
char name_buffer[NAME_MAX + 1];
4401
struct inotify_event *const ievent = &ievent_buffer.event;
4403
const char dummy_file_name[] = "ask.dummy_file_name";
4404
ievent->mask = IN_MOVED_FROM;
4405
ievent->len = sizeof(dummy_file_name);
4406
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4407
const size_t ievent_size = (sizeof(struct inotify_event)
4408
+ sizeof(dummy_file_name));
4409
#if defined(__GNUC__) and __GNUC__ >= 11
4410
#pragma GCC diagnostic push
4411
/* ievent is pointing into a struct which is of sufficient size */
4412
#pragma GCC diagnostic ignored "-Wstringop-overread"
4414
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4416
#if defined(__GNUC__) and __GNUC__ >= 11
4417
#pragma GCC diagnostic pop
4419
g_assert_cmpint(close(pipefds[1]), ==, 0);
4421
bool quit_now = false;
4422
buffer password = {};
4423
bool mandos_client_exited = false;
4424
bool password_is_read = false;
4425
__attribute__((cleanup(cleanup_queue)))
4426
task_queue *queue = create_queue();
4427
g_assert_nonnull(queue);
4429
task_context task = {
4430
.func=read_inotify_event,
4433
.quit_now=&quit_now,
4434
.password=&password,
4435
.filename=strdup("/nonexistent"),
4436
.cancelled_filenames=&cancelled_filenames,
4437
.current_time=¤t_time,
4438
.mandos_client_exited=&mandos_client_exited,
4439
.password_is_read=&password_is_read,
4441
task.func(task, queue);
4442
g_assert_false(quit_now);
4443
g_assert_true(queue->next_run == 0);
4444
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4446
g_assert_nonnull(find_matching_task(queue, (task_context){
4447
.func=read_inotify_event,
4450
.quit_now=&quit_now,
4451
.password=&password,
4452
.filename=task.filename,
4453
.cancelled_filenames=&cancelled_filenames,
4454
.current_time=¤t_time,
4455
.mandos_client_exited=&mandos_client_exited,
4456
.password_is_read=&password_is_read,
4459
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4460
EPOLLIN | EPOLLRDHUP));
4462
__attribute__((cleanup(cleanup_string)))
4463
char *filename = NULL;
4464
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4465
dummy_file_name), >, 0);
4466
g_assert_nonnull(filename);
4467
g_assert_true(string_set_contains(*task.cancelled_filenames,
4471
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4472
test_fixture *fixture,
4473
__attribute__((unused))
4476
__attribute__((cleanup(cleanup_close)))
4477
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4478
g_assert_cmpint(epoll_fd, >=, 0);
4479
__attribute__((cleanup(string_set_clear)))
4480
string_set cancelled_filenames = {};
4481
const mono_microsecs current_time = 0;
4484
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4486
/* "sufficient to read at least one event." - inotify(7) */
4487
const size_t ievent_max_size = (sizeof(struct inotify_event)
4489
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4491
struct inotify_event event;
4492
char name_buffer[NAME_MAX + 1];
4494
struct inotify_event *const ievent = &ievent_buffer.event;
4496
const char dummy_file_name[] = "ask.dummy_file_name";
4497
ievent->mask = IN_DELETE;
4498
ievent->len = sizeof(dummy_file_name);
4499
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4500
const size_t ievent_size = (sizeof(struct inotify_event)
4501
+ sizeof(dummy_file_name));
4502
#if defined(__GNUC__) and __GNUC__ >= 11
4503
#pragma GCC diagnostic push
4504
/* ievent is pointing into a struct which is of sufficient size */
4505
#pragma GCC diagnostic ignored "-Wstringop-overread"
4507
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4509
#if defined(__GNUC__) and __GNUC__ >= 11
4510
#pragma GCC diagnostic pop
4512
g_assert_cmpint(close(pipefds[1]), ==, 0);
4514
bool quit_now = false;
4515
buffer password = {};
4516
bool mandos_client_exited = false;
4517
bool password_is_read = false;
4518
__attribute__((cleanup(cleanup_queue)))
4519
task_queue *queue = create_queue();
4520
g_assert_nonnull(queue);
4522
task_context task = {
4523
.func=read_inotify_event,
4526
.quit_now=&quit_now,
4527
.password=&password,
4528
.filename=strdup("/nonexistent"),
4529
.cancelled_filenames=&cancelled_filenames,
4530
.current_time=¤t_time,
4531
.mandos_client_exited=&mandos_client_exited,
4532
.password_is_read=&password_is_read,
4534
task.func(task, queue);
4535
g_assert_false(quit_now);
4536
g_assert_true(queue->next_run == 0);
4537
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4539
g_assert_nonnull(find_matching_task(queue, (task_context){
4540
.func=read_inotify_event,
4543
.quit_now=&quit_now,
4544
.password=&password,
4545
.filename=task.filename,
4546
.cancelled_filenames=&cancelled_filenames,
4547
.current_time=¤t_time,
4548
.mandos_client_exited=&mandos_client_exited,
4549
.password_is_read=&password_is_read,
4552
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4553
EPOLLIN | EPOLLRDHUP));
4555
__attribute__((cleanup(cleanup_string)))
4556
char *filename = NULL;
4557
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4558
dummy_file_name), >, 0);
4559
g_assert_nonnull(filename);
4560
g_assert_true(string_set_contains(*task.cancelled_filenames,
4565
test_read_inotify_event_IN_CLOSE_WRITE_badname(__attribute__((unused))
4566
test_fixture *fixture,
4567
__attribute__((unused))
4570
__attribute__((cleanup(cleanup_close)))
4571
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4572
g_assert_cmpint(epoll_fd, >=, 0);
4573
const mono_microsecs current_time = 0;
4576
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4578
/* "sufficient to read at least one event." - inotify(7) */
4579
const size_t ievent_max_size = (sizeof(struct inotify_event)
4581
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4583
struct inotify_event event;
4584
char name_buffer[NAME_MAX + 1];
4586
struct inotify_event *const ievent = &ievent_buffer.event;
4588
const char dummy_file_name[] = "ignored.dummy_file_name";
4589
ievent->mask = IN_CLOSE_WRITE;
4590
ievent->len = sizeof(dummy_file_name);
4591
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4592
const size_t ievent_size = (sizeof(struct inotify_event)
4593
+ sizeof(dummy_file_name));
4594
#if defined(__GNUC__) and __GNUC__ >= 11
4595
#pragma GCC diagnostic push
4596
/* ievent is pointing into a struct which is of sufficient size */
4597
#pragma GCC diagnostic ignored "-Wstringop-overread"
4599
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4601
#if defined(__GNUC__) and __GNUC__ >= 11
4602
#pragma GCC diagnostic pop
4604
g_assert_cmpint(close(pipefds[1]), ==, 0);
4606
bool quit_now = false;
4607
buffer password = {};
4608
bool mandos_client_exited = false;
4609
bool password_is_read = false;
4610
__attribute__((cleanup(cleanup_queue)))
4611
task_queue *queue = create_queue();
4612
g_assert_nonnull(queue);
4614
task_context task = {
4615
.func=read_inotify_event,
4618
.quit_now=&quit_now,
4619
.password=&password,
4620
.filename=strdup("/nonexistent"),
4621
.cancelled_filenames = &(string_set){},
4623
.current_time=¤t_time,
4624
.mandos_client_exited=&mandos_client_exited,
4625
.password_is_read=&password_is_read,
4627
task.func(task, queue);
4628
g_assert_false(quit_now);
4629
g_assert_true(queue->next_run == 0);
4630
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4632
g_assert_nonnull(find_matching_task(queue, (task_context){
4633
.func=read_inotify_event,
4636
.quit_now=&quit_now,
4637
.password=&password,
4638
.filename=task.filename,
4639
.cancelled_filenames=task.cancelled_filenames,
4640
.current_time=¤t_time,
4641
.mandos_client_exited=&mandos_client_exited,
4642
.password_is_read=&password_is_read,
4645
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4646
EPOLLIN | EPOLLRDHUP));
4650
test_read_inotify_event_IN_MOVED_TO_badname(__attribute__((unused))
4651
test_fixture *fixture,
4652
__attribute__((unused))
4653
gconstpointer user_data){
4654
__attribute__((cleanup(cleanup_close)))
4655
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4656
g_assert_cmpint(epoll_fd, >=, 0);
4657
const mono_microsecs current_time = 0;
4660
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4662
/* "sufficient to read at least one event." - inotify(7) */
4663
const size_t ievent_max_size = (sizeof(struct inotify_event)
4665
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4667
struct inotify_event event;
4668
char name_buffer[NAME_MAX + 1];
4670
struct inotify_event *const ievent = &ievent_buffer.event;
4672
const char dummy_file_name[] = "ignored.dummy_file_name";
4673
ievent->mask = IN_MOVED_TO;
4674
ievent->len = sizeof(dummy_file_name);
4675
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4676
const size_t ievent_size = (sizeof(struct inotify_event)
4677
+ sizeof(dummy_file_name));
4678
#if defined(__GNUC__) and __GNUC__ >= 11
4679
#pragma GCC diagnostic push
4680
/* ievent is pointing into a struct which is of sufficient size */
4681
#pragma GCC diagnostic ignored "-Wstringop-overread"
4683
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4685
#if defined(__GNUC__) and __GNUC__ >= 11
4686
#pragma GCC diagnostic pop
4688
g_assert_cmpint(close(pipefds[1]), ==, 0);
4690
bool quit_now = false;
4691
buffer password = {};
4692
bool mandos_client_exited = false;
4693
bool password_is_read = false;
4694
__attribute__((cleanup(cleanup_queue)))
4695
task_queue *queue = create_queue();
4696
g_assert_nonnull(queue);
4698
task_context task = {
4699
.func=read_inotify_event,
4702
.quit_now=&quit_now,
4703
.password=&password,
4704
.filename=strdup("/nonexistent"),
4705
.cancelled_filenames = &(string_set){},
4707
.current_time=¤t_time,
4708
.mandos_client_exited=&mandos_client_exited,
4709
.password_is_read=&password_is_read,
4711
task.func(task, queue);
4712
g_assert_false(quit_now);
4713
g_assert_true(queue->next_run == 0);
4714
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4716
g_assert_nonnull(find_matching_task(queue, (task_context){
4717
.func=read_inotify_event,
4720
.quit_now=&quit_now,
4721
.password=&password,
4722
.filename=task.filename,
4723
.cancelled_filenames=task.cancelled_filenames,
4724
.current_time=¤t_time,
4725
.mandos_client_exited=&mandos_client_exited,
4726
.password_is_read=&password_is_read,
4729
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4730
EPOLLIN | EPOLLRDHUP));
4734
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
4735
test_fixture *fixture,
4736
__attribute__((unused))
4739
__attribute__((cleanup(cleanup_close)))
4740
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4741
g_assert_cmpint(epoll_fd, >=, 0);
4742
__attribute__((cleanup(string_set_clear)))
4743
string_set cancelled_filenames = {};
4744
const mono_microsecs current_time = 0;
4747
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4749
/* "sufficient to read at least one event." - inotify(7) */
4750
const size_t ievent_max_size = (sizeof(struct inotify_event)
4752
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4754
struct inotify_event event;
4755
char name_buffer[NAME_MAX + 1];
4757
struct inotify_event *const ievent = &ievent_buffer.event;
4759
const char dummy_file_name[] = "ignored.dummy_file_name";
4760
ievent->mask = IN_MOVED_FROM;
4761
ievent->len = sizeof(dummy_file_name);
4762
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4763
const size_t ievent_size = (sizeof(struct inotify_event)
4764
+ sizeof(dummy_file_name));
4765
#if defined(__GNUC__) and __GNUC__ >= 11
4766
#pragma GCC diagnostic push
4767
/* ievent is pointing into a struct which is of sufficient size */
4768
#pragma GCC diagnostic ignored "-Wstringop-overread"
4770
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4772
#if defined(__GNUC__) and __GNUC__ >= 11
4773
#pragma GCC diagnostic pop
4775
g_assert_cmpint(close(pipefds[1]), ==, 0);
4777
bool quit_now = false;
4778
buffer password = {};
4779
bool mandos_client_exited = false;
4780
bool password_is_read = false;
4781
__attribute__((cleanup(cleanup_queue)))
4782
task_queue *queue = create_queue();
4783
g_assert_nonnull(queue);
4785
task_context task = {
4786
.func=read_inotify_event,
4789
.quit_now=&quit_now,
4790
.password=&password,
4791
.filename=strdup("/nonexistent"),
4792
.cancelled_filenames=&cancelled_filenames,
4793
.current_time=¤t_time,
4794
.mandos_client_exited=&mandos_client_exited,
4795
.password_is_read=&password_is_read,
4797
task.func(task, queue);
4798
g_assert_false(quit_now);
4799
g_assert_true(queue->next_run == 0);
4800
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4802
g_assert_nonnull(find_matching_task(queue, (task_context){
4803
.func=read_inotify_event,
4806
.quit_now=&quit_now,
4807
.password=&password,
4808
.filename=task.filename,
4809
.cancelled_filenames=&cancelled_filenames,
4810
.current_time=¤t_time,
4811
.mandos_client_exited=&mandos_client_exited,
4812
.password_is_read=&password_is_read,
4815
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4816
EPOLLIN | EPOLLRDHUP));
4818
__attribute__((cleanup(cleanup_string)))
4819
char *filename = NULL;
4820
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4821
dummy_file_name), >, 0);
4822
g_assert_nonnull(filename);
4823
g_assert_false(string_set_contains(cancelled_filenames, filename));
4827
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4828
test_fixture *fixture,
4829
__attribute__((unused))
4832
__attribute__((cleanup(cleanup_close)))
4833
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4834
g_assert_cmpint(epoll_fd, >=, 0);
4835
__attribute__((cleanup(string_set_clear)))
4836
string_set cancelled_filenames = {};
4837
const mono_microsecs current_time = 0;
4840
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4842
/* "sufficient to read at least one event." - inotify(7) */
4843
const size_t ievent_max_size = (sizeof(struct inotify_event)
4845
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4847
struct inotify_event event;
4848
char name_buffer[NAME_MAX + 1];
4850
struct inotify_event *const ievent = &ievent_buffer.event;
4852
const char dummy_file_name[] = "ignored.dummy_file_name";
4853
ievent->mask = IN_DELETE;
4854
ievent->len = sizeof(dummy_file_name);
4855
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4856
const size_t ievent_size = (sizeof(struct inotify_event)
4857
+ sizeof(dummy_file_name));
4858
#if defined(__GNUC__) and __GNUC__ >= 11
4859
#pragma GCC diagnostic push
4860
/* ievent is pointing into a struct which is of sufficient size */
4861
#pragma GCC diagnostic ignored "-Wstringop-overread"
4863
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4865
#if defined(__GNUC__) and __GNUC__ >= 11
4866
#pragma GCC diagnostic pop
4868
g_assert_cmpint(close(pipefds[1]), ==, 0);
4870
bool quit_now = false;
4871
buffer password = {};
4872
bool mandos_client_exited = false;
4873
bool password_is_read = false;
4874
__attribute__((cleanup(cleanup_queue)))
4875
task_queue *queue = create_queue();
4876
g_assert_nonnull(queue);
4878
task_context task = {
4879
.func=read_inotify_event,
4882
.quit_now=&quit_now,
4883
.password=&password,
4884
.filename=strdup("/nonexistent"),
4885
.cancelled_filenames=&cancelled_filenames,
4886
.current_time=¤t_time,
4887
.mandos_client_exited=&mandos_client_exited,
4888
.password_is_read=&password_is_read,
4890
task.func(task, queue);
4891
g_assert_false(quit_now);
4892
g_assert_true(queue->next_run == 0);
4893
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4895
g_assert_nonnull(find_matching_task(queue, (task_context){
4896
.func=read_inotify_event,
4899
.quit_now=&quit_now,
4900
.password=&password,
4901
.filename=task.filename,
4902
.cancelled_filenames=&cancelled_filenames,
4903
.current_time=¤t_time,
4904
.mandos_client_exited=&mandos_client_exited,
4905
.password_is_read=&password_is_read,
4908
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4909
EPOLLIN | EPOLLRDHUP));
4911
__attribute__((cleanup(cleanup_string)))
4912
char *filename = NULL;
4913
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4914
dummy_file_name), >, 0);
4915
g_assert_nonnull(filename);
4916
g_assert_false(string_set_contains(cancelled_filenames, filename));
4920
void test_open_and_parse_question_ENOENT(__attribute__((unused))
4921
test_fixture *fixture,
4922
__attribute__((unused))
4923
gconstpointer user_data){
4924
__attribute__((cleanup(cleanup_close)))
4925
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4926
g_assert_cmpint(epoll_fd, >=, 0);
4927
__attribute__((cleanup(string_set_clear)))
4928
string_set cancelled_filenames = {};
4929
bool mandos_client_exited = false;
4930
bool password_is_read = false;
4931
__attribute__((cleanup(cleanup_queue)))
4932
task_queue *queue = create_queue();
4933
g_assert_nonnull(queue);
4935
char *const filename = strdup("/nonexistent");
4936
g_assert_nonnull(filename);
4937
task_context task = {
4938
.func=open_and_parse_question,
4939
.question_filename=filename,
4941
.password=(buffer[]){{}},
4943
.cancelled_filenames=&cancelled_filenames,
4944
.current_time=(mono_microsecs[]){0},
4945
.mandos_client_exited=&mandos_client_exited,
4946
.password_is_read=&password_is_read,
4948
task.func(task, queue);
4949
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4952
static void test_open_and_parse_question_EIO(__attribute__((unused))
4953
test_fixture *fixture,
4954
__attribute__((unused))
4955
gconstpointer user_data){
4956
__attribute__((cleanup(cleanup_close)))
4957
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4958
g_assert_cmpint(epoll_fd, >=, 0);
4959
__attribute__((cleanup(string_set_clear)))
4960
string_set cancelled_filenames = {};
4961
buffer password = {};
4962
bool mandos_client_exited = false;
4963
bool password_is_read = false;
4964
__attribute__((cleanup(cleanup_queue)))
4965
task_queue *queue = create_queue();
4966
g_assert_nonnull(queue);
4967
const mono_microsecs current_time = 0;
4969
char *filename = strdup("/proc/self/mem");
4970
task_context task = {
4971
.func=open_and_parse_question,
4972
.question_filename=filename,
4974
.password=&password,
4976
.cancelled_filenames=&cancelled_filenames,
4977
.current_time=¤t_time,
4978
.mandos_client_exited=&mandos_client_exited,
4979
.password_is_read=&password_is_read,
4981
run_task_with_stderr_to_dev_null(task, queue);
4982
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4986
test_open_and_parse_question_parse_error(__attribute__((unused))
4987
test_fixture *fixture,
4988
__attribute__((unused))
4989
gconstpointer user_data){
4990
__attribute__((cleanup(cleanup_close)))
4991
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4992
g_assert_cmpint(epoll_fd, >=, 0);
4993
__attribute__((cleanup(string_set_clear)))
4994
string_set cancelled_filenames = {};
4995
__attribute__((cleanup(cleanup_queue)))
4996
task_queue *queue = create_queue();
4997
g_assert_nonnull(queue);
4999
__attribute__((cleanup(cleanup_string)))
5000
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5001
g_assert_nonnull(tempfilename);
5002
int tempfile = mkostemp(tempfilename, O_CLOEXEC);
5003
g_assert_cmpint(tempfile, >, 0);
5004
const char bad_data[] = "this is bad syntax\n";
5005
g_assert_cmpint(write(tempfile, bad_data, sizeof(bad_data)),
5006
==, sizeof(bad_data));
5007
g_assert_cmpint(close(tempfile), ==, 0);
5009
char *const filename = strdup(tempfilename);
5010
g_assert_nonnull(filename);
5011
task_context task = {
5012
.func=open_and_parse_question,
5013
.question_filename=filename,
5015
.password=(buffer[]){{}},
5017
.cancelled_filenames=&cancelled_filenames,
5018
.current_time=(mono_microsecs[]){0},
5019
.mandos_client_exited=(bool[]){false},
5020
.password_is_read=(bool[]){false},
5022
run_task_with_stderr_to_dev_null(task, queue);
5024
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5026
g_assert_cmpint(unlink(tempfilename), ==, 0);
5030
void test_open_and_parse_question_nosocket(__attribute__((unused))
5031
test_fixture *fixture,
5032
__attribute__((unused))
5033
gconstpointer user_data){
5034
__attribute__((cleanup(cleanup_close)))
5035
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5036
g_assert_cmpint(epoll_fd, >=, 0);
5037
__attribute__((cleanup(string_set_clear)))
5038
string_set cancelled_filenames = {};
5039
__attribute__((cleanup(cleanup_queue)))
5040
task_queue *queue = create_queue();
5041
g_assert_nonnull(queue);
5043
__attribute__((cleanup(cleanup_string)))
5044
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5045
g_assert_nonnull(tempfilename);
5046
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5047
g_assert_cmpint(questionfile, >, 0);
5048
FILE *qf = fdopen(questionfile, "w");
5049
g_assert_cmpint(fprintf(qf, "[Ask]\nPID=1\n"), >, 0);
5050
g_assert_cmpint(fclose(qf), ==, 0);
5052
char *const filename = strdup(tempfilename);
5053
g_assert_nonnull(filename);
5054
task_context task = {
5055
.func=open_and_parse_question,
5056
.question_filename=filename,
5058
.password=(buffer[]){{}},
5060
.cancelled_filenames=&cancelled_filenames,
5061
.current_time=(mono_microsecs[]){0},
5062
.mandos_client_exited=(bool[]){false},
5063
.password_is_read=(bool[]){false},
5065
run_task_with_stderr_to_dev_null(task, queue);
5066
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5068
g_assert_cmpint(unlink(tempfilename), ==, 0);
5072
void test_open_and_parse_question_badsocket(__attribute__((unused))
5073
test_fixture *fixture,
5074
__attribute__((unused))
5075
gconstpointer user_data){
5076
__attribute__((cleanup(cleanup_close)))
5077
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5078
g_assert_cmpint(epoll_fd, >=, 0);
5079
__attribute__((cleanup(string_set_clear)))
5080
string_set cancelled_filenames = {};
5081
__attribute__((cleanup(cleanup_queue)))
5082
task_queue *queue = create_queue();
5083
g_assert_nonnull(queue);
5085
__attribute__((cleanup(cleanup_string)))
5086
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5087
g_assert_nonnull(tempfilename);
5088
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5089
g_assert_cmpint(questionfile, >, 0);
5090
FILE *qf = fdopen(questionfile, "w");
5091
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=\nPID=1\n"), >, 0);
5092
g_assert_cmpint(fclose(qf), ==, 0);
5094
char *const filename = strdup(tempfilename);
5095
g_assert_nonnull(filename);
5096
task_context task = {
5097
.func=open_and_parse_question,
5098
.question_filename=filename,
5100
.password=(buffer[]){{}},
5102
.cancelled_filenames=&cancelled_filenames,
5103
.current_time=(mono_microsecs[]){0},
5104
.mandos_client_exited=(bool[]){false},
5105
.password_is_read=(bool[]){false},
5107
run_task_with_stderr_to_dev_null(task, queue);
5108
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5110
g_assert_cmpint(unlink(tempfilename), ==, 0);
5114
void test_open_and_parse_question_nopid(__attribute__((unused))
5115
test_fixture *fixture,
5116
__attribute__((unused))
5117
gconstpointer user_data){
5118
__attribute__((cleanup(cleanup_close)))
5119
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5120
g_assert_cmpint(epoll_fd, >=, 0);
5121
__attribute__((cleanup(string_set_clear)))
5122
string_set cancelled_filenames = {};
5123
__attribute__((cleanup(cleanup_queue)))
5124
task_queue *queue = create_queue();
5125
g_assert_nonnull(queue);
5127
__attribute__((cleanup(cleanup_string)))
5128
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5129
g_assert_nonnull(tempfilename);
5130
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5131
g_assert_cmpint(questionfile, >, 0);
5132
FILE *qf = fdopen(questionfile, "w");
5133
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\n"), >, 0);
5134
g_assert_cmpint(fclose(qf), ==, 0);
5136
char *const filename = strdup(tempfilename);
5137
g_assert_nonnull(filename);
5138
task_context task = {
5139
.func=open_and_parse_question,
5140
.question_filename=filename,
5142
.password=(buffer[]){{}},
5144
.cancelled_filenames=&cancelled_filenames,
5145
.current_time=(mono_microsecs[]){0},
5146
.mandos_client_exited=(bool[]){false},
5147
.password_is_read=(bool[]){false},
5149
run_task_with_stderr_to_dev_null(task, queue);
5150
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5152
g_assert_cmpint(unlink(tempfilename), ==, 0);
5156
void test_open_and_parse_question_badpid(__attribute__((unused))
5157
test_fixture *fixture,
5158
__attribute__((unused))
5159
gconstpointer user_data){
5160
__attribute__((cleanup(cleanup_close)))
5161
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5162
g_assert_cmpint(epoll_fd, >=, 0);
5163
__attribute__((cleanup(string_set_clear)))
5164
string_set cancelled_filenames = {};
5165
__attribute__((cleanup(cleanup_queue)))
5166
task_queue *queue = create_queue();
5167
g_assert_nonnull(queue);
5169
__attribute__((cleanup(cleanup_string)))
5170
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5171
g_assert_nonnull(tempfilename);
5172
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5173
g_assert_cmpint(questionfile, >, 0);
5174
FILE *qf = fdopen(questionfile, "w");
5175
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=\n"),
5177
g_assert_cmpint(fclose(qf), ==, 0);
5179
char *const filename = strdup(tempfilename);
5180
g_assert_nonnull(filename);
5181
task_context task = {
5182
.func=open_and_parse_question,
5183
.question_filename=filename,
5185
.password=(buffer[]){{}},
5187
.cancelled_filenames=&cancelled_filenames,
5188
.current_time=(mono_microsecs[]){0},
5189
.mandos_client_exited=(bool[]){false},
5190
.password_is_read=(bool[]){false},
5192
run_task_with_stderr_to_dev_null(task, queue);
5193
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5195
g_assert_cmpint(unlink(tempfilename), ==, 0);
5199
test_open_and_parse_question_noexist_pid(__attribute__((unused))
5200
test_fixture *fixture,
5201
__attribute__((unused))
5202
gconstpointer user_data){
5203
__attribute__((cleanup(cleanup_close)))
5204
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5205
g_assert_cmpint(epoll_fd, >=, 0);
5206
__attribute__((cleanup(string_set_clear)))
5207
string_set cancelled_filenames = {};
5208
buffer password = {};
5209
bool mandos_client_exited = false;
5210
bool password_is_read = false;
5211
__attribute__((cleanup(cleanup_queue)))
5212
task_queue *queue = create_queue();
5213
g_assert_nonnull(queue);
5214
const mono_microsecs current_time = 0;
5216
/* Find value of sysctl kernel.pid_max */
5217
uintmax_t pid_max = 0;
5218
FILE *sysctl_pid_max = fopen("/proc/sys/kernel/pid_max", "r");
5219
g_assert_nonnull(sysctl_pid_max);
5220
g_assert_cmpint(fscanf(sysctl_pid_max, "%" PRIuMAX, &pid_max),
5222
g_assert_cmpint(fclose(sysctl_pid_max), ==, 0);
5224
pid_t nonexisting_pid = ((pid_t)pid_max)+1;
5225
g_assert_true(nonexisting_pid > 0); /* Overflow check */
5227
__attribute__((cleanup(cleanup_string)))
5228
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5229
g_assert_nonnull(tempfilename);
5230
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5231
g_assert_cmpint(questionfile, >, 0);
5232
FILE *qf = fdopen(questionfile, "w");
5233
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5234
PRIuMAX"\n", (uintmax_t)nonexisting_pid),
5236
g_assert_cmpint(fclose(qf), ==, 0);
5238
char *const question_filename = strdup(tempfilename);
5239
g_assert_nonnull(question_filename);
5240
task_context task = {
5241
.func=open_and_parse_question,
5242
.question_filename=question_filename,
5244
.password=&password,
5245
.filename=question_filename,
5246
.cancelled_filenames=&cancelled_filenames,
5247
.current_time=¤t_time,
5248
.mandos_client_exited=&mandos_client_exited,
5249
.password_is_read=&password_is_read,
5251
run_task_with_stderr_to_dev_null(task, queue);
5252
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5254
g_assert_cmpint(unlink(tempfilename), ==, 0);
5258
test_open_and_parse_question_no_notafter(__attribute__((unused))
5259
test_fixture *fixture,
5260
__attribute__((unused))
5261
gconstpointer user_data){
5262
__attribute__((cleanup(cleanup_close)))
5263
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5264
g_assert_cmpint(epoll_fd, >=, 0);
5265
__attribute__((cleanup(string_set_clear)))
5266
string_set cancelled_filenames = {};
5267
buffer password = {};
5268
bool mandos_client_exited = false;
5269
bool password_is_read = false;
5270
__attribute__((cleanup(cleanup_queue)))
5271
task_queue *queue = create_queue();
5272
g_assert_nonnull(queue);
5273
const mono_microsecs current_time = 0;
5275
__attribute__((cleanup(cleanup_string)))
5276
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5277
g_assert_nonnull(tempfilename);
5278
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5279
g_assert_cmpint(questionfile, >, 0);
5280
FILE *qf = fdopen(questionfile, "w");
5281
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5282
PRIuMAX "\n", (uintmax_t)getpid()), >, 0);
5283
g_assert_cmpint(fclose(qf), ==, 0);
5285
char *const filename = strdup(tempfilename);
5286
g_assert_nonnull(filename);
5287
task_context task = {
5288
.func=open_and_parse_question,
5289
.question_filename=filename,
5291
.password=&password,
5293
.cancelled_filenames=&cancelled_filenames,
5294
.current_time=¤t_time,
5295
.mandos_client_exited=&mandos_client_exited,
5296
.password_is_read=&password_is_read,
5298
task.func(task, queue);
5299
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5301
__attribute__((cleanup(cleanup_string)))
5302
char *socket_filename = strdup("/nonexistent");
5303
g_assert_nonnull(socket_filename);
5304
g_assert_nonnull(find_matching_task(queue, (task_context){
5305
.func=connect_question_socket,
5306
.question_filename=tempfilename,
5307
.filename=socket_filename,
5309
.password=&password,
5310
.current_time=¤t_time,
5311
.mandos_client_exited=&mandos_client_exited,
5312
.password_is_read=&password_is_read,
5315
g_assert_true(queue->next_run != 0);
5317
g_assert_cmpint(unlink(tempfilename), ==, 0);
5321
test_open_and_parse_question_bad_notafter(__attribute__((unused))
5322
test_fixture *fixture,
5323
__attribute__((unused))
5324
gconstpointer user_data){
5325
__attribute__((cleanup(cleanup_close)))
5326
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5327
g_assert_cmpint(epoll_fd, >=, 0);
5328
__attribute__((cleanup(string_set_clear)))
5329
string_set cancelled_filenames = {};
5330
buffer password = {};
5331
bool mandos_client_exited = false;
5332
bool password_is_read = false;
5333
__attribute__((cleanup(cleanup_queue)))
5334
task_queue *queue = create_queue();
5335
g_assert_nonnull(queue);
5336
const mono_microsecs current_time = 0;
5338
__attribute__((cleanup(cleanup_string)))
5339
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5340
g_assert_nonnull(tempfilename);
5341
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5342
g_assert_cmpint(questionfile, >, 0);
5343
FILE *qf = fdopen(questionfile, "w");
5344
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5345
PRIuMAX "\nNotAfter=\n",
5346
(uintmax_t)getpid()), >, 0);
5347
g_assert_cmpint(fclose(qf), ==, 0);
5349
char *const filename = strdup(tempfilename);
5350
g_assert_nonnull(filename);
5351
task_context task = {
5352
.func=open_and_parse_question,
5353
.question_filename=filename,
5355
.password=&password,
5357
.cancelled_filenames=&cancelled_filenames,
5358
.current_time=¤t_time,
5359
.mandos_client_exited=&mandos_client_exited,
5360
.password_is_read=&password_is_read,
5362
run_task_with_stderr_to_dev_null(task, queue);
5363
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5365
__attribute__((cleanup(cleanup_string)))
5366
char *socket_filename = strdup("/nonexistent");
5367
g_assert_nonnull(find_matching_task(queue, (task_context){
5368
.func=connect_question_socket,
5369
.question_filename=tempfilename,
5370
.filename=socket_filename,
5372
.password=&password,
5373
.current_time=¤t_time,
5374
.mandos_client_exited=&mandos_client_exited,
5375
.password_is_read=&password_is_read,
5377
g_assert_true(queue->next_run != 0);
5379
g_assert_cmpint(unlink(tempfilename), ==, 0);
5383
void assert_open_and_parse_question_with_notafter(const mono_microsecs
5385
const mono_microsecs
5387
const mono_microsecs
5389
__attribute__((cleanup(cleanup_close)))
5390
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5391
g_assert_cmpint(epoll_fd, >=, 0);
5392
__attribute__((cleanup(string_set_clear)))
5393
string_set cancelled_filenames = {};
5394
buffer password = {};
5395
bool mandos_client_exited = false;
5396
bool password_is_read = false;
5397
__attribute__((cleanup(cleanup_queue)))
5398
task_queue *queue = create_queue();
5399
g_assert_nonnull(queue);
5400
queue->next_run = next_queue_run;
5402
__attribute__((cleanup(cleanup_string)))
5403
char *tempfilename = strdup("/tmp/mandosXXXXXX");
5404
g_assert_nonnull(tempfilename);
5405
int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5406
g_assert_cmpint(questionfile, >, 0);
5407
FILE *qf = fdopen(questionfile, "w");
5408
g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5409
PRIuMAX "\nNotAfter=%" PRIuMAX "\n",
5410
(uintmax_t)getpid(), notafter), >, 0);
5411
g_assert_cmpint(fclose(qf), ==, 0);
5413
char *const filename = strdup(tempfilename);
5414
g_assert_nonnull(filename);
5415
task_context task = {
5416
.func=open_and_parse_question,
5417
.question_filename=filename,
5419
.password=&password,
5421
.cancelled_filenames=&cancelled_filenames,
5422
.current_time=¤t_time,
5423
.mandos_client_exited=&mandos_client_exited,
5424
.password_is_read=&password_is_read,
5426
task.func(task, queue);
5428
if(queue->length >= 1){
5429
__attribute__((cleanup(cleanup_string)))
5430
char *socket_filename = strdup("/nonexistent");
5431
g_assert_nonnull(find_matching_task(queue, (task_context){
5432
.func=connect_question_socket,
5433
.filename=socket_filename,
5435
.password=&password,
5436
.current_time=¤t_time,
5437
.cancelled_filenames=&cancelled_filenames,
5438
.mandos_client_exited=&mandos_client_exited,
5439
.password_is_read=&password_is_read,
5441
g_assert_true(queue->next_run != 0);
5445
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5446
} else if(current_time >= notafter) {
5447
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5449
g_assert_nonnull(find_matching_task(queue, (task_context){
5450
.func=cancel_old_question,
5451
.question_filename=tempfilename,
5452
.filename=tempfilename,
5454
.cancelled_filenames=&cancelled_filenames,
5455
.current_time=¤t_time,
5458
g_assert_true(queue->next_run == 1);
5460
g_assert_cmpint(unlink(tempfilename), ==, 0);
5464
test_open_and_parse_question_notafter_0(__attribute__((unused))
5465
test_fixture *fixture,
5466
__attribute__((unused))
5467
gconstpointer user_data){
5468
/* current_time, notafter, next_queue_run */
5469
assert_open_and_parse_question_with_notafter(0, 0, 0);
5473
test_open_and_parse_question_notafter_1(__attribute__((unused))
5474
test_fixture *fixture,
5475
__attribute__((unused))
5476
gconstpointer user_data){
5477
/* current_time, notafter, next_queue_run */
5478
assert_open_and_parse_question_with_notafter(0, 1, 0);
5482
test_open_and_parse_question_notafter_1_1(__attribute__((unused))
5483
test_fixture *fixture,
5484
__attribute__((unused))
5485
gconstpointer user_data){
5486
/* current_time, notafter, next_queue_run */
5487
assert_open_and_parse_question_with_notafter(0, 1, 1);
5491
test_open_and_parse_question_notafter_1_2(__attribute__((unused))
5492
test_fixture *fixture,
5493
__attribute__((unused))
5494
gconstpointer user_data){
5495
/* current_time, notafter, next_queue_run */
5496
assert_open_and_parse_question_with_notafter(0, 1, 2);
5500
test_open_and_parse_question_equal_notafter(__attribute__((unused))
5501
test_fixture *fixture,
5502
__attribute__((unused))
5503
gconstpointer user_data){
5504
/* current_time, notafter, next_queue_run */
5505
assert_open_and_parse_question_with_notafter(1, 1, 0);
5509
test_open_and_parse_question_late_notafter(__attribute__((unused))
5510
test_fixture *fixture,
5511
__attribute__((unused))
5512
gconstpointer user_data){
5513
/* current_time, notafter, next_queue_run */
5514
assert_open_and_parse_question_with_notafter(2, 1, 0);
5517
static void assert_cancel_old_question_param(const mono_microsecs
5519
const mono_microsecs
5521
const mono_microsecs
5523
const mono_microsecs
5525
__attribute__((cleanup(string_set_clear)))
5526
string_set cancelled_filenames = {};
5527
__attribute__((cleanup(cleanup_queue)))
5528
task_queue *queue = create_queue();
5529
g_assert_nonnull(queue);
5530
queue->next_run = next_queue_run;
5532
char *const question_filename = strdup("/nonexistent");
5533
g_assert_nonnull(question_filename);
5534
task_context task = {
5535
.func=cancel_old_question,
5536
.question_filename=question_filename,
5537
.filename=question_filename,
5539
.cancelled_filenames=&cancelled_filenames,
5540
.current_time=¤t_time,
5542
task.func(task, queue);
5544
if(current_time >= notafter){
5545
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5546
g_assert_true(string_set_contains(cancelled_filenames,
5549
g_assert_nonnull(find_matching_task(queue, (task_context){
5550
.func=cancel_old_question,
5551
.question_filename=question_filename,
5552
.filename=question_filename,
5554
.cancelled_filenames=&cancelled_filenames,
5555
.current_time=¤t_time,
5558
g_assert_false(string_set_contains(cancelled_filenames,
5559
question_filename));
5561
g_assert_cmpuint((unsigned int)queue->next_run, ==,
5562
(unsigned int)next_set_to);
5565
static void test_cancel_old_question_0_1_2(__attribute__((unused))
5566
test_fixture *fixture,
5567
__attribute__((unused))
5568
gconstpointer user_data){
5569
/* next_queue_run unset,
5570
cancellation should happen because time has come,
5571
next_queue_run should be unchanged */
5572
/* next_queue_run, notafter, current_time, next_set_to */
5573
assert_cancel_old_question_param(0, 1, 2, 0);
5576
static void test_cancel_old_question_0_2_1(__attribute__((unused))
5577
test_fixture *fixture,
5578
__attribute__((unused))
5579
gconstpointer user_data){
5580
/* If next_queue_run is 0, meaning unset, and notafter is 2,
5581
and current_time is not yet notafter or greater,
5582
update value of next_queue_run to value of notafter */
5583
/* next_queue_run, notafter, current_time, next_set_to */
5584
assert_cancel_old_question_param(0, 2, 1, 2);
5587
static void test_cancel_old_question_1_2_3(__attribute__((unused))
5588
test_fixture *fixture,
5589
__attribute__((unused))
5590
gconstpointer user_data){
5591
/* next_queue_run 1,
5592
cancellation should happen because time has come,
5593
next_queue_run should be unchanged */
5594
/* next_queue_run, notafter, current_time, next_set_to */
5595
assert_cancel_old_question_param(1, 2, 3, 1);
5598
static void test_cancel_old_question_1_3_2(__attribute__((unused))
5599
test_fixture *fixture,
5600
__attribute__((unused))
5601
gconstpointer user_data){
5602
/* If next_queue_run is set,
5603
and current_time is not yet notafter or greater,
5604
and notafter is larger than next_queue_run
5605
next_queue_run should be unchanged */
5606
/* next_queue_run, notafter, current_time, next_set_to */
5607
assert_cancel_old_question_param(1, 3, 2, 1);
5610
static void test_cancel_old_question_2_1_3(__attribute__((unused))
5611
test_fixture *fixture,
5612
__attribute__((unused))
5613
gconstpointer user_data){
5614
/* next_queue_run 2,
5615
cancellation should happen because time has come,
5616
next_queue_run should be unchanged */
5617
/* next_queue_run, notafter, current_time, next_set_to */
5618
assert_cancel_old_question_param(2, 1, 3, 2);
5621
static void test_cancel_old_question_2_3_1(__attribute__((unused))
5622
test_fixture *fixture,
5623
__attribute__((unused))
5624
gconstpointer user_data){
5625
/* If next_queue_run is set,
5626
and current_time is not yet notafter or greater,
5627
and notafter is larger than next_queue_run
5628
next_queue_run should be unchanged */
5629
/* next_queue_run, notafter, current_time, next_set_to */
5630
assert_cancel_old_question_param(2, 3, 1, 2);
5633
static void test_cancel_old_question_3_1_2(__attribute__((unused))
5634
test_fixture *fixture,
5635
__attribute__((unused))
5636
gconstpointer user_data){
5637
/* next_queue_run 3,
5638
cancellation should happen because time has come,
5639
next_queue_run should be unchanged */
5640
/* next_queue_run, notafter, current_time, next_set_to */
5641
assert_cancel_old_question_param(3, 1, 2, 3);
5644
static void test_cancel_old_question_3_2_1(__attribute__((unused))
5645
test_fixture *fixture,
5646
__attribute__((unused))
5647
gconstpointer user_data){
5648
/* If next_queue_run is set,
5649
and current_time is not yet notafter or greater,
5650
and notafter is smaller than next_queue_run
5651
update value of next_queue_run to value of notafter */
5652
/* next_queue_run, notafter, current_time, next_set_to */
5653
assert_cancel_old_question_param(3, 2, 1, 2);
5657
test_connect_question_socket_name_too_long(__attribute__((unused))
5658
test_fixture *fixture,
5659
__attribute__((unused))
5660
gconstpointer user_data){
5661
__attribute__((cleanup(cleanup_close)))
5662
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5663
g_assert_cmpint(epoll_fd, >=, 0);
5664
const char question_filename[] = "/nonexistent/question";
5665
__attribute__((cleanup(string_set_clear)))
5666
string_set cancelled_filenames = {};
5667
__attribute__((cleanup(cleanup_queue)))
5668
task_queue *queue = create_queue();
5669
g_assert_nonnull(queue);
5670
__attribute__((cleanup(cleanup_string)))
5671
char *tempdir = make_temporary_directory();
5672
g_assert_nonnull(tempdir);
5673
struct sockaddr_un unix_socket = { .sun_family=AF_LOCAL };
5674
char socket_name[sizeof(unix_socket.sun_path)];
5675
memset(socket_name, 'x', sizeof(socket_name));
5676
socket_name[sizeof(socket_name)-1] = '\0';
5677
char *filename = NULL;
5678
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5680
g_assert_nonnull(filename);
5682
task_context task = {
5683
.func=connect_question_socket,
5684
.question_filename=strdup(question_filename),
5686
.password=(buffer[]){{}},
5688
.cancelled_filenames=&cancelled_filenames,
5689
.mandos_client_exited=(bool[]){false},
5690
.password_is_read=(bool[]){false},
5691
.current_time=(mono_microsecs[]){0},
5693
g_assert_nonnull(task.question_filename);
5694
run_task_with_stderr_to_dev_null(task, queue);
5696
g_assert_true(string_set_contains(cancelled_filenames,
5697
question_filename));
5698
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5699
g_assert_true(queue->next_run == 0);
5701
g_assert_cmpint(rmdir(tempdir), ==, 0);
5705
void test_connect_question_socket_connect_fail(__attribute__((unused))
5706
test_fixture *fixture,
5707
__attribute__((unused))
5710
__attribute__((cleanup(cleanup_close)))
5711
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5712
g_assert_cmpint(epoll_fd, >=, 0);
5713
const char question_filename[] = "/nonexistent/question";
5714
__attribute__((cleanup(string_set_clear)))
5715
string_set cancelled_filenames = {};
5716
const mono_microsecs current_time = 3;
5717
__attribute__((cleanup(cleanup_queue)))
5718
task_queue *queue = create_queue();
5719
g_assert_nonnull(queue);
5720
__attribute__((cleanup(cleanup_string)))
5721
char *tempdir = make_temporary_directory();
5722
g_assert_nonnull(tempdir);
5723
char socket_name[] = "nonexistent";
5724
char *filename = NULL;
5725
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5727
g_assert_nonnull(filename);
5729
task_context task = {
5730
.func=connect_question_socket,
5731
.question_filename=strdup(question_filename),
5733
.password=(buffer[]){{}},
5735
.cancelled_filenames=&cancelled_filenames,
5736
.mandos_client_exited=(bool[]){false},
5737
.password_is_read=(bool[]){false},
5738
.current_time=¤t_time,
5740
g_assert_nonnull(task.question_filename);
5741
run_task_with_stderr_to_dev_null(task, queue);
5743
g_assert_nonnull(find_matching_task(queue, task));
5745
g_assert_false(string_set_contains(cancelled_filenames,
5746
question_filename));
5747
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5748
g_assert_true(queue->next_run == 1000000 + current_time);
5750
g_assert_cmpint(rmdir(tempdir), ==, 0);
5754
void test_connect_question_socket_bad_epoll(__attribute__((unused))
5755
test_fixture *fixture,
5756
__attribute__((unused))
5757
gconstpointer user_data){
5758
__attribute__((cleanup(cleanup_close)))
5759
const int epoll_fd = open("/dev/null",
5760
O_WRONLY | O_CLOEXEC | O_NOCTTY);
5761
__attribute__((cleanup(cleanup_string)))
5762
char *const question_filename = strdup("/nonexistent/question");
5763
g_assert_nonnull(question_filename);
5764
__attribute__((cleanup(string_set_clear)))
5765
string_set cancelled_filenames = {};
5766
const mono_microsecs current_time = 5;
5767
__attribute__((cleanup(cleanup_queue)))
5768
task_queue *queue = create_queue();
5769
g_assert_nonnull(queue);
5770
__attribute__((cleanup(cleanup_string)))
5771
char *tempdir = make_temporary_directory();
5772
g_assert_nonnull(tempdir);
5773
__attribute__((cleanup(cleanup_close)))
5774
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5775
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5776
g_assert_cmpint(sock_fd, >=, 0);
5777
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5778
const char socket_name[] = "socket_name";
5779
__attribute__((cleanup(cleanup_string)))
5780
char *filename = NULL;
5781
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5783
g_assert_nonnull(filename);
5784
g_assert_cmpint((int)strlen(filename), <,
5785
(int)sizeof(sock_name.sun_path));
5786
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5787
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5788
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5789
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5790
task_context task = {
5791
.func=connect_question_socket,
5792
.question_filename=strdup(question_filename),
5794
.password=(buffer[]){{}},
5795
.filename=strdup(filename),
5796
.cancelled_filenames=&cancelled_filenames,
5797
.mandos_client_exited=(bool[]){false},
5798
.password_is_read=(bool[]){false},
5799
.current_time=¤t_time,
5801
g_assert_nonnull(task.question_filename);
5802
run_task_with_stderr_to_dev_null(task, queue);
5804
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5805
const task_context *const added_task
5806
= find_matching_task(queue, task);
5807
g_assert_nonnull(added_task);
5808
g_assert_true(queue->next_run == 1000000 + current_time);
5810
g_assert_cmpint(unlink(filename), ==, 0);
5811
g_assert_cmpint(rmdir(tempdir), ==, 0);
5815
void test_connect_question_socket_usable(__attribute__((unused))
5816
test_fixture *fixture,
5817
__attribute__((unused))
5818
gconstpointer user_data){
5819
__attribute__((cleanup(cleanup_close)))
5820
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5821
g_assert_cmpint(epoll_fd, >=, 0);
5822
__attribute__((cleanup(cleanup_string)))
5823
char *const question_filename = strdup("/nonexistent/question");
5824
g_assert_nonnull(question_filename);
5825
__attribute__((cleanup(string_set_clear)))
5826
string_set cancelled_filenames = {};
5827
buffer password = {};
5828
bool mandos_client_exited = false;
5829
bool password_is_read = false;
5830
const mono_microsecs current_time = 0;
5831
__attribute__((cleanup(cleanup_queue)))
5832
task_queue *queue = create_queue();
5833
g_assert_nonnull(queue);
5834
__attribute__((cleanup(cleanup_string)))
5835
char *tempdir = make_temporary_directory();
5836
g_assert_nonnull(tempdir);
5837
__attribute__((cleanup(cleanup_close)))
5838
const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5839
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5840
g_assert_cmpint(sock_fd, >=, 0);
5841
struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5842
const char socket_name[] = "socket_name";
5843
__attribute__((cleanup(cleanup_string)))
5844
char *filename = NULL;
5845
g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5847
g_assert_nonnull(filename);
5848
g_assert_cmpint((int)strlen(filename), <,
5849
(int)sizeof(sock_name.sun_path));
5850
strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5851
sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5852
g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5853
(socklen_t)SUN_LEN(&sock_name)), >=, 0);
5854
task_context task = {
5855
.func=connect_question_socket,
5856
.question_filename=strdup(question_filename),
5858
.password=&password,
5859
.filename=strdup(filename),
5860
.cancelled_filenames=&cancelled_filenames,
5861
.mandos_client_exited=&mandos_client_exited,
5862
.password_is_read=&password_is_read,
5863
.current_time=¤t_time,
5865
g_assert_nonnull(task.question_filename);
5866
task.func(task, queue);
5868
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5869
const task_context *const added_task
5870
= find_matching_task(queue, (task_context){
5871
.func=send_password_to_socket,
5872
.question_filename=question_filename,
5875
.password=&password,
5876
.cancelled_filenames=&cancelled_filenames,
5877
.mandos_client_exited=&mandos_client_exited,
5878
.password_is_read=&password_is_read,
5879
.current_time=¤t_time,
5881
g_assert_nonnull(added_task);
5882
g_assert_cmpint(added_task->fd, >, 0);
5884
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5887
const int fd = added_task->fd;
5888
g_assert_cmpint(fd, >, 0);
5889
g_assert_true(fd_has_cloexec_and_nonblock(fd));
5892
char write_data[PIPE_BUF];
5894
/* Construct test password buffer */
5895
/* Start with + since that is what the real protocol uses */
5896
write_data[0] = '+';
5897
/* Set a special character at string end just to mark the end */
5898
write_data[sizeof(write_data)-2] = 'y';
5899
/* Set NUL at buffer end, as suggested by the protocol */
5900
write_data[sizeof(write_data)-1] = '\0';
5901
/* Fill rest of password with 'x' */
5902
memset(write_data+1, 'x', sizeof(write_data)-3);
5903
g_assert_cmpint((int)send(fd, write_data, sizeof(write_data),
5904
MSG_NOSIGNAL), ==, sizeof(write_data));
5907
/* read from sock_fd */
5908
char read_data[sizeof(write_data)];
5909
g_assert_cmpint((int)read(sock_fd, read_data, sizeof(read_data)),
5910
==, sizeof(read_data));
5912
g_assert_true(memcmp(write_data, read_data, sizeof(write_data))
5915
/* writing to sock_fd should fail */
5916
g_assert_cmpint(send(sock_fd, write_data, sizeof(write_data),
5917
MSG_NOSIGNAL), <, 0);
5919
/* reading from fd should fail */
5920
g_assert_cmpint((int)recv(fd, read_data, sizeof(read_data),
5921
MSG_NOSIGNAL), <, 0);
5923
g_assert_cmpint(unlink(filename), ==, 0);
5924
g_assert_cmpint(rmdir(tempdir), ==, 0);
5928
test_send_password_to_socket_client_not_exited(__attribute__((unused))
5929
test_fixture *fixture,
5930
__attribute__((unused))
5933
__attribute__((cleanup(cleanup_close)))
5934
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5935
g_assert_cmpint(epoll_fd, >=, 0);
5936
__attribute__((cleanup(cleanup_string)))
5937
char *const question_filename = strdup("/nonexistent/question");
5938
g_assert_nonnull(question_filename);
5939
__attribute__((cleanup(cleanup_string)))
5940
char *const filename = strdup("/nonexistent/socket");
5941
g_assert_nonnull(filename);
5942
__attribute__((cleanup(string_set_clear)))
5943
string_set cancelled_filenames = {};
5944
buffer password = {};
5945
bool password_is_read = true;
5946
__attribute__((cleanup(cleanup_queue)))
5947
task_queue *queue = create_queue();
5948
g_assert_nonnull(queue);
5950
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5951
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5953
__attribute__((cleanup(cleanup_close)))
5954
const int read_socket = socketfds[0];
5955
const int write_socket = socketfds[1];
5956
task_context task = {
5957
.func=send_password_to_socket,
5958
.question_filename=strdup(question_filename),
5959
.filename=strdup(filename),
5962
.password=&password,
5963
.cancelled_filenames=&cancelled_filenames,
5964
.mandos_client_exited=(bool[]){false},
5965
.password_is_read=&password_is_read,
5966
.current_time=(mono_microsecs[]){0},
5968
g_assert_nonnull(task.question_filename);
5970
task.func(task, queue);
5972
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5974
const task_context *const added_task
5975
= find_matching_task(queue, task);
5976
g_assert_nonnull(added_task);
5977
g_assert_cmpuint((unsigned int)password.length, ==, 0);
5978
g_assert_true(password_is_read);
5980
g_assert_cmpint(added_task->fd, >, 0);
5981
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5986
test_send_password_to_socket_password_not_read(__attribute__((unused))
5987
test_fixture *fixture,
5988
__attribute__((unused))
5991
__attribute__((cleanup(cleanup_close)))
5992
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5993
g_assert_cmpint(epoll_fd, >=, 0);
5994
__attribute__((cleanup(cleanup_string)))
5995
char *const question_filename = strdup("/nonexistent/question");
5996
g_assert_nonnull(question_filename);
5997
__attribute__((cleanup(cleanup_string)))
5998
char *const filename = strdup("/nonexistent/socket");
5999
__attribute__((cleanup(string_set_clear)))
6000
string_set cancelled_filenames = {};
6001
buffer password = {};
6002
__attribute__((cleanup(cleanup_queue)))
6003
task_queue *queue = create_queue();
6004
g_assert_nonnull(queue);
6006
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6007
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6009
__attribute__((cleanup(cleanup_close)))
6010
const int read_socket = socketfds[0];
6011
const int write_socket = socketfds[1];
6012
task_context task = {
6013
.func=send_password_to_socket,
6014
.question_filename=strdup(question_filename),
6015
.filename=strdup(filename),
6018
.password=&password,
6019
.cancelled_filenames=&cancelled_filenames,
6020
.mandos_client_exited=(bool[]){false},
6021
.password_is_read=(bool[]){false},
6022
.current_time=(mono_microsecs[]){0},
6024
g_assert_nonnull(task.question_filename);
6026
task.func(task, queue);
6028
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6030
const task_context *const added_task = find_matching_task(queue,
6032
g_assert_nonnull(added_task);
6033
g_assert_cmpuint((unsigned int)password.length, ==, 0);
6034
g_assert_true(queue->next_run == 0);
6036
g_assert_cmpint(added_task->fd, >, 0);
6037
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
6042
void test_send_password_to_socket_EMSGSIZE(__attribute__((unused))
6043
test_fixture *fixture,
6044
__attribute__((unused))
6045
gconstpointer user_data){
6046
__attribute__((cleanup(cleanup_close)))
6047
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6048
g_assert_cmpint(epoll_fd, >=, 0);
6049
const char question_filename[] = "/nonexistent/question";
6050
char *const filename = strdup("/nonexistent/socket");
6051
__attribute__((cleanup(string_set_clear)))
6052
string_set cancelled_filenames = {};
6055
/* Find a message size which triggers EMSGSIZE */
6056
__attribute__((cleanup(cleanup_string)))
6057
char *message_buffer = NULL;
6058
size_t message_size = PIPE_BUF + 1;
6059
for(ssize_t ssret = 0; ssret >= 0; message_size += 1024){
6060
if(message_size >= 1024*1024*1024){ /* 1 GiB */
6061
g_test_skip("Skipping EMSGSIZE test: Will not try 1GiB");
6064
message_buffer = realloc(message_buffer, message_size);
6065
if(message_buffer == NULL){
6066
g_test_skip("Skipping EMSGSIZE test");
6067
g_test_message("Failed to malloc() %" PRIuMAX " bytes",
6068
(uintmax_t)message_size);
6071
/* Fill buffer with 'x' */
6072
memset(message_buffer, 'x', message_size);
6073
/* Create a new socketpair for each message size to avoid having
6074
to empty the pipe by reading the message to a separate buffer
6076
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6077
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6079
ssret = send(socketfds[1], message_buffer, message_size,
6081
error_t saved_errno = errno;
6082
g_assert_cmpint(close(socketfds[0]), ==, 0);
6083
g_assert_cmpint(close(socketfds[1]), ==, 0);
6086
if(saved_errno != EMSGSIZE) {
6087
g_test_skip("Skipping EMSGSIZE test");
6088
g_test_message("Error on send(%" PRIuMAX " bytes): %s",
6089
(uintmax_t)message_size,
6090
strerror(saved_errno));
6094
} else if(ssret != (ssize_t)message_size){
6095
g_test_skip("Skipping EMSGSIZE test");
6096
g_test_message("Partial send(): %" PRIuMAX " of %" PRIdMAX
6097
" bytes", (uintmax_t)ssret,
6098
(intmax_t)message_size);
6102
g_test_message("EMSGSIZE triggered by %" PRIdMAX " bytes",
6103
(intmax_t)message_size);
6106
.data=message_buffer,
6107
.length=message_size - 2, /* Compensate for added '+' and NUL */
6108
.allocated=message_size,
6110
if(mlock(password.data, password.allocated) != 0){
6111
g_assert_true(errno == EPERM or errno == ENOMEM);
6114
__attribute__((cleanup(cleanup_queue)))
6115
task_queue *queue = create_queue();
6116
g_assert_nonnull(queue);
6117
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6118
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6120
__attribute__((cleanup(cleanup_close)))
6121
const int read_socket = socketfds[0];
6122
__attribute__((cleanup(cleanup_close)))
6123
const int write_socket = socketfds[1];
6124
task_context task = {
6125
.func=send_password_to_socket,
6126
.question_filename=strdup(question_filename),
6130
.password=&password,
6131
.cancelled_filenames=&cancelled_filenames,
6132
.mandos_client_exited=(bool[]){true},
6133
.password_is_read=(bool[]){true},
6134
.current_time=(mono_microsecs[]){0},
6136
g_assert_nonnull(task.question_filename);
6138
run_task_with_stderr_to_dev_null(task, queue);
6140
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6141
g_assert_true(string_set_contains(cancelled_filenames,
6142
question_filename));
6145
static void test_send_password_to_socket_retry(__attribute__((unused))
6146
test_fixture *fixture,
6147
__attribute__((unused))
6150
__attribute__((cleanup(cleanup_close)))
6151
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6152
g_assert_cmpint(epoll_fd, >=, 0);
6153
__attribute__((cleanup(cleanup_string)))
6154
char *const question_filename = strdup("/nonexistent/question");
6155
g_assert_nonnull(question_filename);
6156
__attribute__((cleanup(cleanup_string)))
6157
char *const filename = strdup("/nonexistent/socket");
6158
g_assert_nonnull(filename);
6159
__attribute__((cleanup(string_set_clear)))
6160
string_set cancelled_filenames = {};
6161
__attribute__((cleanup(cleanup_buffer)))
6162
buffer password = {};
6164
__attribute__((cleanup(cleanup_queue)))
6165
task_queue *queue = create_queue();
6166
g_assert_nonnull(queue);
6168
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6169
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6171
__attribute__((cleanup(cleanup_close)))
6172
const int read_socket = socketfds[0];
6173
const int write_socket = socketfds[1];
6174
/* Close the server side socket to force ECONNRESET on client */
6175
g_assert_cmpint(close(read_socket), ==, 0);
6176
task_context task = {
6177
.func=send_password_to_socket,
6178
.question_filename=strdup(question_filename),
6179
.filename=strdup(filename),
6182
.password=&password,
6183
.cancelled_filenames=&cancelled_filenames,
6184
.mandos_client_exited=(bool[]){true},
6185
.password_is_read=(bool[]){true},
6186
.current_time=(mono_microsecs[]){0},
6188
g_assert_nonnull(task.question_filename);
6190
task.func(task, queue);
6192
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6194
const task_context *const added_task = find_matching_task(queue,
6196
g_assert_nonnull(added_task);
6197
g_assert_cmpuint((unsigned int)password.length, ==, 0);
6199
g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
6204
void test_send_password_to_socket_bad_epoll(__attribute__((unused))
6205
test_fixture *fixture,
6206
__attribute__((unused))
6207
gconstpointer user_data){
6208
__attribute__((cleanup(cleanup_close)))
6209
const int epoll_fd = open("/dev/null",
6210
O_WRONLY | O_CLOEXEC | O_NOCTTY);
6211
__attribute__((cleanup(cleanup_string)))
6212
char *const question_filename = strdup("/nonexistent/question");
6213
g_assert_nonnull(question_filename);
6214
__attribute__((cleanup(cleanup_string)))
6215
char *const filename = strdup("/nonexistent/socket");
6216
g_assert_nonnull(filename);
6217
__attribute__((cleanup(string_set_clear)))
6218
string_set cancelled_filenames = {};
6219
__attribute__((cleanup(cleanup_buffer)))
6220
buffer password = {};
6222
const mono_microsecs current_time = 11;
6223
__attribute__((cleanup(cleanup_queue)))
6224
task_queue *queue = create_queue();
6225
g_assert_nonnull(queue);
6227
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6228
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6230
__attribute__((cleanup(cleanup_close)))
6231
const int read_socket = socketfds[0];
6232
const int write_socket = socketfds[1];
6233
/* Close the server side socket to force ECONNRESET on client */
6234
g_assert_cmpint(close(read_socket), ==, 0);
6235
task_context task = {
6236
.func=send_password_to_socket,
6237
.question_filename=strdup(question_filename),
6238
.filename=strdup(filename),
6241
.password=&password,
6242
.cancelled_filenames=&cancelled_filenames,
6243
.mandos_client_exited=(bool[]){true},
6244
.password_is_read=(bool[]){true},
6245
.current_time=¤t_time,
6247
g_assert_nonnull(task.question_filename);
6249
run_task_with_stderr_to_dev_null(task, queue);
6251
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6253
const task_context *const added_task = find_matching_task(queue,
6255
g_assert_nonnull(added_task);
6256
g_assert_true(queue->next_run == current_time + 1000000);
6257
g_assert_cmpuint((unsigned int)password.length, ==, 0);
6260
static void assert_send_password_to_socket_password(buffer password){
6261
__attribute__((cleanup(cleanup_close)))
6262
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6263
g_assert_cmpint(epoll_fd, >=, 0);
6264
char *const question_filename = strdup("/nonexistent/question");
6265
g_assert_nonnull(question_filename);
6266
char *const filename = strdup("/nonexistent/socket");
6267
g_assert_nonnull(filename);
6268
__attribute__((cleanup(string_set_clear)))
6269
string_set cancelled_filenames = {};
6271
__attribute__((cleanup(cleanup_queue)))
6272
task_queue *queue = create_queue();
6273
g_assert_nonnull(queue);
6275
g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6276
| SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6278
__attribute__((cleanup(cleanup_close)))
6279
const int read_socket = socketfds[0];
6280
const int write_socket = socketfds[1];
6281
task_context task = {
6282
.func=send_password_to_socket,
6283
.question_filename=question_filename,
6287
.password=&password,
6288
.cancelled_filenames=&cancelled_filenames,
6289
.mandos_client_exited=(bool[]){true},
6290
.password_is_read=(bool[]){true},
6291
.current_time=(mono_microsecs[]){0},
6294
char *expected_written_data = malloc(password.length + 2);
6295
g_assert_nonnull(expected_written_data);
6296
expected_written_data[0] = '+';
6297
expected_written_data[password.length + 1] = '\0';
6298
if(password.length > 0){
6299
g_assert_nonnull(password.data);
6300
memcpy(expected_written_data + 1, password.data, password.length);
6303
task.func(task, queue);
6306
g_assert_cmpint((int)read(read_socket, buf, PIPE_BUF), ==,
6307
(int)(password.length + 2));
6308
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6310
g_assert_true(memcmp(expected_written_data, buf,
6311
password.length + 2) == 0);
6313
g_assert_true(epoll_set_does_not_contain(epoll_fd, write_socket));
6315
free(expected_written_data);
6319
test_send_password_to_socket_null_password(__attribute__((unused))
6320
test_fixture *fixture,
6321
__attribute__((unused))
6322
gconstpointer user_data){
6323
__attribute__((cleanup(cleanup_buffer)))
6324
buffer password = {};
6325
assert_send_password_to_socket_password(password);
6329
test_send_password_to_socket_empty_password(__attribute__((unused))
6330
test_fixture *fixture,
6331
__attribute__((unused))
6332
gconstpointer user_data){
6333
__attribute__((cleanup(cleanup_buffer)))
6335
.data=malloc(1), /* because malloc(0) may return NULL */
6337
.allocated=0, /* deliberate lie */
6339
g_assert_nonnull(password.data);
6340
assert_send_password_to_socket_password(password);
6344
test_send_password_to_socket_empty_str_pass(__attribute__((unused))
6345
test_fixture *fixture,
6346
__attribute__((unused))
6347
gconstpointer user_data){
6348
__attribute__((cleanup(cleanup_buffer)))
6354
if(mlock(password.data, password.allocated) != 0){
6355
g_assert_true(errno == EPERM or errno == ENOMEM);
6357
assert_send_password_to_socket_password(password);
6361
test_send_password_to_socket_text_password(__attribute__((unused))
6362
test_fixture *fixture,
6363
__attribute__((unused))
6364
gconstpointer user_data){
6365
const char dummy_test_password[] = "dummy test password";
6366
__attribute__((cleanup(cleanup_buffer)))
6368
.data = strdup(dummy_test_password),
6369
.length = strlen(dummy_test_password),
6370
.allocated = sizeof(dummy_test_password),
6372
if(mlock(password.data, password.allocated) != 0){
6373
g_assert_true(errno == EPERM or errno == ENOMEM);
6375
assert_send_password_to_socket_password(password);
6379
test_send_password_to_socket_binary_password(__attribute__((unused))
6380
test_fixture *fixture,
6381
__attribute__((unused))
6382
gconstpointer user_data){
6383
__attribute__((cleanup(cleanup_buffer)))
6389
g_assert_nonnull(password.data);
6390
if(mlock(password.data, password.allocated) != 0){
6391
g_assert_true(errno == EPERM or errno == ENOMEM);
6393
char c = 1; /* Start at 1, avoiding NUL */
6394
for(int i=0; i < 255; i++){
6395
password.data[i] = c++;
6397
assert_send_password_to_socket_password(password);
6401
test_send_password_to_socket_nuls_in_password(__attribute__((unused))
6402
test_fixture *fixture,
6403
__attribute__((unused))
6406
char test_password[] = {'\0', 'a', '\0', 'b', '\0', 'c', '\0'};
6407
__attribute__((cleanup(cleanup_buffer)))
6409
.data=malloc(sizeof(test_password)),
6410
.length=sizeof(test_password),
6411
.allocated=sizeof(test_password),
6413
g_assert_nonnull(password.data);
6414
if(mlock(password.data, password.allocated) !=0){
6415
g_assert_true(errno == EPERM or errno == ENOMEM);
6417
memcpy(password.data, test_password, password.allocated);
6418
assert_send_password_to_socket_password(password);
6421
static bool assert_add_existing_questions_to_devnull(task_queue
6434
static void test_add_existing_questions_ENOENT(__attribute__((unused))
6435
test_fixture *fixture,
6436
__attribute__((unused))
6439
__attribute__((cleanup(cleanup_queue)))
6440
task_queue *queue = create_queue();
6441
g_assert_nonnull(queue);
6442
__attribute__((cleanup(cleanup_close)))
6443
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6444
g_assert_cmpint(epoll_fd, >=, 0);
6445
__attribute__((cleanup(string_set_clear)))
6446
string_set cancelled_filenames = {};
6448
g_assert_false(assert_add_existing_questions_to_devnull
6451
(buffer[]){{}}, /* password */
6452
&cancelled_filenames,
6453
(mono_microsecs[]){0}, /* current_time */
6454
(bool[]){false}, /* mandos_client_exited */
6455
(bool[]){false}, /* password_is_read */
6456
"/nonexistent")); /* dirname */
6458
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6462
bool assert_add_existing_questions_to_devnull(task_queue
6469
*cancelled_filenames,
6470
const mono_microsecs
6471
*const current_time,
6473
mandos_client_exited,
6478
__attribute__((cleanup(cleanup_close)))
6479
const int devnull_fd = open("/dev/null",
6480
O_WRONLY | O_CLOEXEC | O_NOCTTY);
6481
g_assert_cmpint(devnull_fd, >=, 0);
6482
__attribute__((cleanup(cleanup_close)))
6483
const int real_stderr_fd = dup(STDERR_FILENO);
6484
g_assert_cmpint(real_stderr_fd, >=, 0);
6485
dup2(devnull_fd, STDERR_FILENO);
6486
const bool ret = add_existing_questions(queue, epoll_fd, password,
6487
cancelled_filenames,
6489
mandos_client_exited,
6490
password_is_read, dirname);
6491
dup2(real_stderr_fd, STDERR_FILENO);
6496
void test_add_existing_questions_no_questions(__attribute__((unused))
6497
test_fixture *fixture,
6498
__attribute__((unused))
6501
__attribute__((cleanup(cleanup_queue)))
6502
task_queue *queue = create_queue();
6503
g_assert_nonnull(queue);
6504
__attribute__((cleanup(cleanup_close)))
6505
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6506
g_assert_cmpint(epoll_fd, >=, 0);
6507
__attribute__((cleanup(string_set_clear)))
6508
string_set cancelled_filenames = {};
6509
__attribute__((cleanup(cleanup_string)))
6510
char *tempdir = make_temporary_directory();
6511
g_assert_nonnull(tempdir);
6513
g_assert_false(assert_add_existing_questions_to_devnull
6516
(buffer[]){{}}, /* password */
6517
&cancelled_filenames,
6518
(mono_microsecs[]){0}, /* current_time */
6519
(bool[]){false}, /* mandos_client_exited */
6520
(bool[]){false}, /* password_is_read */
6523
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6525
g_assert_cmpint(rmdir(tempdir), ==, 0);
6528
static char *make_question_file_in_directory(const char *const);
6531
void test_add_existing_questions_one_question(__attribute__((unused))
6532
test_fixture *fixture,
6533
__attribute__((unused))
6536
__attribute__((cleanup(cleanup_queue)))
6537
task_queue *queue = create_queue();
6538
g_assert_nonnull(queue);
6539
__attribute__((cleanup(cleanup_close)))
6540
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6541
g_assert_cmpint(epoll_fd, >=, 0);
6542
__attribute__((cleanup(cleanup_buffer)))
6543
buffer password = {};
6544
__attribute__((cleanup(string_set_clear)))
6545
string_set cancelled_filenames = {};
6546
const mono_microsecs current_time = 0;
6547
bool mandos_client_exited = false;
6548
bool password_is_read = false;
6549
__attribute__((cleanup(cleanup_string)))
6550
char *tempdir = make_temporary_directory();
6551
g_assert_nonnull(tempdir);
6552
__attribute__((cleanup(cleanup_string)))
6553
char *question_filename
6554
= make_question_file_in_directory(tempdir);
6555
g_assert_nonnull(question_filename);
6557
g_assert_true(assert_add_existing_questions_to_devnull
6561
&cancelled_filenames,
6563
&mandos_client_exited,
6567
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6569
g_assert_nonnull(find_matching_task(queue, (task_context){
6570
.func=open_and_parse_question,
6572
.filename=question_filename,
6573
.question_filename=question_filename,
6574
.password=&password,
6575
.cancelled_filenames=&cancelled_filenames,
6576
.current_time=¤t_time,
6577
.mandos_client_exited=&mandos_client_exited,
6578
.password_is_read=&password_is_read,
6581
g_assert_true(queue->next_run == 1);
6583
g_assert_cmpint(unlink(question_filename), ==, 0);
6584
g_assert_cmpint(rmdir(tempdir), ==, 0);
6587
static char *make_question_file_in_directory(const char
6589
return make_temporary_prefixed_file_in_directory("ask.", dir);
6593
void test_add_existing_questions_two_questions(__attribute__((unused))
6594
test_fixture *fixture,
6595
__attribute__((unused))
6598
__attribute__((cleanup(cleanup_queue)))
6599
task_queue *queue = create_queue();
6600
g_assert_nonnull(queue);
6601
__attribute__((cleanup(cleanup_close)))
6602
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6603
g_assert_cmpint(epoll_fd, >=, 0);
6604
__attribute__((cleanup(cleanup_buffer)))
6605
buffer password = {};
6606
__attribute__((cleanup(string_set_clear)))
6607
string_set cancelled_filenames = {};
6608
const mono_microsecs current_time = 0;
6609
bool mandos_client_exited = false;
6610
bool password_is_read = false;
6611
__attribute__((cleanup(cleanup_string)))
6612
char *tempdir = make_temporary_directory();
6613
g_assert_nonnull(tempdir);
6614
__attribute__((cleanup(cleanup_string)))
6615
char *question_filename1
6616
= make_question_file_in_directory(tempdir);
6617
g_assert_nonnull(question_filename1);
6618
__attribute__((cleanup(cleanup_string)))
6619
char *question_filename2
6620
= make_question_file_in_directory(tempdir);
6621
g_assert_nonnull(question_filename2);
6623
g_assert_true(assert_add_existing_questions_to_devnull
6627
&cancelled_filenames,
6629
&mandos_client_exited,
6633
g_assert_cmpuint((unsigned int)queue->length, ==, 2);
6635
g_assert_true(queue->next_run == 1);
6637
__attribute__((cleanup(string_set_clear)))
6638
string_set seen_questions = {};
6640
bool queue_contains_question_opener(char *const question_filename){
6641
return(find_matching_task(queue, (task_context){
6642
.func=open_and_parse_question,
6644
.question_filename=question_filename,
6645
.password=&password,
6646
.cancelled_filenames=&cancelled_filenames,
6647
.current_time=¤t_time,
6648
.mandos_client_exited=&mandos_client_exited,
6649
.password_is_read=&password_is_read,
6653
g_assert_true(queue_contains_question_opener(question_filename1));
6654
g_assert_true(queue_contains_question_opener(question_filename2));
6656
g_assert_true(queue->next_run == 1);
6658
g_assert_cmpint(unlink(question_filename1), ==, 0);
6659
g_assert_cmpint(unlink(question_filename2), ==, 0);
6660
g_assert_cmpint(rmdir(tempdir), ==, 0);
6664
test_add_existing_questions_non_questions(__attribute__((unused))
6665
test_fixture *fixture,
6666
__attribute__((unused))
6667
gconstpointer user_data){
6668
__attribute__((cleanup(cleanup_queue)))
6669
task_queue *queue = create_queue();
6670
g_assert_nonnull(queue);
6671
__attribute__((cleanup(cleanup_close)))
6672
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6673
g_assert_cmpint(epoll_fd, >=, 0);
6674
__attribute__((cleanup(string_set_clear)))
6675
string_set cancelled_filenames = {};
6676
__attribute__((cleanup(cleanup_string)))
6677
char *tempdir = make_temporary_directory();
6678
g_assert_nonnull(tempdir);
6679
__attribute__((cleanup(cleanup_string)))
6680
char *question_filename1
6681
= make_temporary_file_in_directory(tempdir);
6682
g_assert_nonnull(question_filename1);
6683
__attribute__((cleanup(cleanup_string)))
6684
char *question_filename2
6685
= make_temporary_file_in_directory(tempdir);
6686
g_assert_nonnull(question_filename2);
6688
g_assert_false(assert_add_existing_questions_to_devnull
6691
(buffer[]){{}}, /* password */
6692
&cancelled_filenames,
6693
(mono_microsecs[]){0}, /* current_time */
6694
(bool[]){false}, /* mandos_client_exited */
6695
(bool[]){false}, /* password_is_read */
6698
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6700
g_assert_cmpint(unlink(question_filename1), ==, 0);
6701
g_assert_cmpint(unlink(question_filename2), ==, 0);
6702
g_assert_cmpint(rmdir(tempdir), ==, 0);
6706
test_add_existing_questions_both_types(__attribute__((unused))
6707
test_fixture *fixture,
6708
__attribute__((unused))
6709
gconstpointer user_data){
6710
__attribute__((cleanup(cleanup_queue)))
6711
task_queue *queue = create_queue();
6712
g_assert_nonnull(queue);
6713
__attribute__((cleanup(cleanup_close)))
6714
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6715
g_assert_cmpint(epoll_fd, >=, 0);
6716
__attribute__((cleanup(cleanup_buffer)))
6717
buffer password = {};
6718
__attribute__((cleanup(string_set_clear)))
6719
string_set cancelled_filenames = {};
6720
const mono_microsecs current_time = 0;
6721
bool mandos_client_exited = false;
6722
bool password_is_read = false;
6723
__attribute__((cleanup(cleanup_string)))
6724
char *tempdir = make_temporary_directory();
6725
g_assert_nonnull(tempdir);
6726
__attribute__((cleanup(cleanup_string)))
6727
char *tempfilename1 = make_temporary_file_in_directory(tempdir);
6728
g_assert_nonnull(tempfilename1);
6729
__attribute__((cleanup(cleanup_string)))
6730
char *tempfilename2 = make_temporary_file_in_directory(tempdir);
6731
g_assert_nonnull(tempfilename2);
6732
__attribute__((cleanup(cleanup_string)))
6733
char *question_filename
6734
= make_question_file_in_directory(tempdir);
6735
g_assert_nonnull(question_filename);
6737
g_assert_true(assert_add_existing_questions_to_devnull
6741
&cancelled_filenames,
6743
&mandos_client_exited,
6747
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6749
g_assert_nonnull(find_matching_task(queue, (task_context){
6750
.func=open_and_parse_question,
6752
.filename=question_filename,
6753
.question_filename=question_filename,
6754
.password=&password,
6755
.cancelled_filenames=&cancelled_filenames,
6756
.current_time=¤t_time,
6757
.mandos_client_exited=&mandos_client_exited,
6758
.password_is_read=&password_is_read,
6761
g_assert_true(queue->next_run == 1);
6763
g_assert_cmpint(unlink(tempfilename1), ==, 0);
6764
g_assert_cmpint(unlink(tempfilename2), ==, 0);
6765
g_assert_cmpint(unlink(question_filename), ==, 0);
6766
g_assert_cmpint(rmdir(tempdir), ==, 0);
6769
static void test_wait_for_event_timeout(__attribute__((unused))
6770
test_fixture *fixture,
6771
__attribute__((unused))
6772
gconstpointer user_data){
6773
__attribute__((cleanup(cleanup_close)))
6774
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6775
g_assert_cmpint(epoll_fd, >=, 0);
6777
g_assert_true(wait_for_event(epoll_fd, 1, 0));
6780
static void test_wait_for_event_event(__attribute__((unused))
6781
test_fixture *fixture,
6782
__attribute__((unused))
6783
gconstpointer user_data){
6784
__attribute__((cleanup(cleanup_close)))
6785
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6786
g_assert_cmpint(epoll_fd, >=, 0);
6788
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6789
__attribute__((cleanup(cleanup_close)))
6790
const int read_pipe = pipefds[0];
6791
__attribute__((cleanup(cleanup_close)))
6792
const int write_pipe = pipefds[1];
6793
g_assert_cmpint(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, read_pipe,
6794
&(struct epoll_event)
6795
{ .events=EPOLLIN | EPOLLRDHUP }), ==, 0);
6796
g_assert_cmpint((int)write(write_pipe, "x", 1), ==, 1);
6798
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6801
static void test_wait_for_event_sigchld(test_fixture *fixture,
6802
__attribute__((unused))
6803
gconstpointer user_data){
6804
const pid_t pid = fork();
6805
if(pid == 0){ /* Child */
6806
if(not restore_signal_handler(&fixture->orig_sigaction)){
6807
_exit(EXIT_FAILURE);
6809
if(not restore_sigmask(&fixture->orig_sigmask)){
6810
_exit(EXIT_FAILURE);
6814
g_assert_true(pid != -1);
6815
__attribute__((cleanup(cleanup_close)))
6816
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6817
g_assert_cmpint(epoll_fd, >=, 0);
6819
g_assert_true(wait_for_event(epoll_fd, 0, 0));
6822
g_assert_true(waitpid(pid, &status, 0) == pid);
6823
g_assert_true(WIFEXITED(status));
6824
g_assert_cmpint(WEXITSTATUS(status), ==, EXIT_SUCCESS);
6827
static void test_run_queue_zeroes_next_run(__attribute__((unused))
6828
test_fixture *fixture,
6829
__attribute__((unused))
6830
gconstpointer user_data){
6831
__attribute__((cleanup(cleanup_queue)))
6832
task_queue *queue = create_queue();
6833
g_assert_nonnull(queue);
6834
queue->next_run = 1;
6835
__attribute__((cleanup(cleanup_close)))
6836
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6837
__attribute__((cleanup(string_set_clear)))
6838
string_set cancelled_filenames = {};
6839
bool quit_now = false;
6841
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6842
g_assert_false(quit_now);
6843
g_assert_cmpuint((unsigned int)queue->next_run, ==, 0);
6847
void test_run_queue_clears_cancelled_filenames(__attribute__((unused))
6848
test_fixture *fixture,
6849
__attribute__((unused))
6852
__attribute__((cleanup(cleanup_queue)))
6853
task_queue *queue = create_queue();
6854
g_assert_nonnull(queue);
6855
__attribute__((cleanup(string_set_clear)))
6856
string_set cancelled_filenames = {};
6857
bool quit_now = false;
6858
const char question_filename[] = "/nonexistent/question_filename";
6859
g_assert_true(string_set_add(&cancelled_filenames,
6860
question_filename));
6862
g_assert_true(add_to_queue(queue,
6863
(task_context){ .func=dummy_func }));
6865
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6866
g_assert_false(quit_now);
6867
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6868
g_assert_false(string_set_contains(cancelled_filenames,
6869
question_filename));
6873
void test_run_queue_skips_cancelled_filenames(__attribute__((unused))
6874
test_fixture *fixture,
6875
__attribute__((unused))
6878
__attribute__((cleanup(cleanup_queue)))
6879
task_queue *queue = create_queue();
6880
g_assert_nonnull(queue);
6881
__attribute__((cleanup(string_set_clear)))
6882
string_set cancelled_filenames = {};
6883
bool quit_now = false;
6885
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6886
__attribute__((cleanup(cleanup_close)))
6887
const int read_pipe = pipefds[0];
6888
g_assert_cmpint(close(pipefds[1]), ==, 0);
6889
const char question_filename[] = "/nonexistent/question_filename";
6890
g_assert_true(string_set_add(&cancelled_filenames,
6891
question_filename));
6892
__attribute__((nonnull))
6893
void quit_func(const task_context task,
6894
__attribute__((unused)) task_queue *const q){
6895
g_assert_nonnull(task.quit_now);
6896
*task.quit_now = true;
6898
task_context task = {
6900
.question_filename=strdup(question_filename),
6901
.quit_now=&quit_now,
6904
g_assert_nonnull(task.question_filename);
6906
g_assert_true(add_to_queue(queue, task));
6908
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6909
g_assert_false(quit_now);
6911
/* read_pipe should be closed already */
6913
bool read_pipe_closed = (close(read_pipe) == -1);
6914
read_pipe_closed &= (errno == EBADF);
6915
g_assert_true(read_pipe_closed);
6918
static void test_run_queue_one_task(__attribute__((unused))
6919
test_fixture *fixture,
6920
__attribute__((unused))
6921
gconstpointer user_data){
6922
__attribute__((cleanup(cleanup_queue)))
6923
task_queue *queue = create_queue();
6924
g_assert_nonnull(queue);
6925
__attribute__((cleanup(string_set_clear)))
6926
string_set cancelled_filenames = {};
6927
bool quit_now = false;
6929
__attribute__((nonnull))
6930
void next_run_func(__attribute__((unused))
6931
const task_context task,
6932
task_queue *const q){
6936
task_context task = {
6937
.func=next_run_func,
6939
g_assert_true(add_to_queue(queue, task));
6941
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6942
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6943
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6946
static void test_run_queue_two_tasks(__attribute__((unused))
6947
test_fixture *fixture,
6948
__attribute__((unused))
6949
gconstpointer user_data){
6950
__attribute__((cleanup(cleanup_queue)))
6951
task_queue *queue = create_queue();
6952
g_assert_nonnull(queue);
6953
queue->next_run = 1;
6954
__attribute__((cleanup(string_set_clear)))
6955
string_set cancelled_filenames = {};
6956
bool quit_now = false;
6957
bool mandos_client_exited = false;
6959
__attribute__((nonnull))
6960
void next_run_func(__attribute__((unused))
6961
const task_context task,
6962
task_queue *const q){
6966
__attribute__((nonnull))
6967
void exited_func(const task_context task,
6968
__attribute__((unused)) task_queue *const q){
6969
*task.mandos_client_exited = true;
6972
task_context task1 = {
6973
.func=next_run_func,
6975
g_assert_true(add_to_queue(queue, task1));
6977
task_context task2 = {
6979
.mandos_client_exited=&mandos_client_exited,
6981
g_assert_true(add_to_queue(queue, task2));
6983
g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6984
g_assert_false(quit_now);
6985
g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6986
g_assert_true(mandos_client_exited);
6987
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6990
static void test_run_queue_two_tasks_quit(__attribute__((unused))
6991
test_fixture *fixture,
6992
__attribute__((unused))
6993
gconstpointer user_data){
6994
__attribute__((cleanup(cleanup_queue)))
6995
task_queue *queue = create_queue();
6996
g_assert_nonnull(queue);
6997
__attribute__((cleanup(string_set_clear)))
6998
string_set cancelled_filenames = {};
6999
bool quit_now = false;
7000
bool mandos_client_exited = false;
7001
bool password_is_read = false;
7003
__attribute__((nonnull))
7004
void set_exited_func(const task_context task,
7005
__attribute__((unused)) task_queue *const q){
7006
*task.mandos_client_exited = true;
7007
*task.quit_now = true;
7009
task_context task1 = {
7010
.func=set_exited_func,
7011
.quit_now=&quit_now,
7012
.mandos_client_exited=&mandos_client_exited,
7014
g_assert_true(add_to_queue(queue, task1));
7016
__attribute__((nonnull))
7017
void set_read_func(const task_context task,
7018
__attribute__((unused)) task_queue *const q){
7019
*task.quit_now = true;
7020
*task.password_is_read = true;
7022
task_context task2 = {
7023
.func=set_read_func,
7024
.quit_now=&quit_now,
7025
.password_is_read=&password_is_read,
7027
g_assert_true(add_to_queue(queue, task2));
7029
g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
7030
g_assert_true(quit_now);
7031
g_assert_true(mandos_client_exited xor password_is_read);
7032
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
7035
static void test_run_queue_two_tasks_cleanup(__attribute__((unused))
7036
test_fixture *fixture,
7037
__attribute__((unused))
7038
gconstpointer user_data){
7039
__attribute__((cleanup(cleanup_queue)))
7040
task_queue *queue = create_queue();
7041
g_assert_nonnull(queue);
7042
__attribute__((cleanup(string_set_clear)))
7043
string_set cancelled_filenames = {};
7045
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
7046
__attribute__((cleanup(cleanup_close)))
7047
const int read_pipe = pipefds[0];
7048
__attribute__((cleanup(cleanup_close)))
7049
const int write_pipe = pipefds[1];
7050
bool quit_now = false;
7052
__attribute__((nonnull))
7053
void read_func(const task_context task,
7054
__attribute__((unused)) task_queue *const q){
7055
*task.quit_now = true;
7057
task_context task1 = {
7059
.quit_now=&quit_now,
7062
g_assert_true(add_to_queue(queue, task1));
7064
__attribute__((nonnull))
7065
void write_func(const task_context task,
7066
__attribute__((unused)) task_queue *const q){
7067
*task.quit_now = true;
7069
task_context task2 = {
7071
.quit_now=&quit_now,
7074
g_assert_true(add_to_queue(queue, task2));
7076
g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
7077
g_assert_true(quit_now);
7079
/* Either read_pipe or write_pipe should be closed already */
7081
bool close_read_pipe = (close(read_pipe) == -1);
7082
close_read_pipe &= (errno == EBADF);
7084
bool close_write_pipe = (close(write_pipe) == -1);
7085
close_write_pipe &= (errno == EBADF);
7086
g_assert_true(close_read_pipe xor close_write_pipe);
7087
g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
7090
static void test_setup_signal_handler(__attribute__((unused))
7091
test_fixture *fixture,
7092
__attribute__((unused))
7093
gconstpointer user_data){
7094
/* Save current SIGCHLD action, whatever it is */
7095
struct sigaction expected_sigchld_action;
7096
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
7099
/* Act; i.e. run the setup_signal_handler() function */
7100
struct sigaction actual_old_sigchld_action;
7101
g_assert_true(setup_signal_handler(&actual_old_sigchld_action));
7103
/* Check that the function correctly set "actual_old_sigchld_action"
7104
to the same values as the previously saved
7105
"expected_sigchld_action" */
7106
/* Check member sa_handler */
7107
g_assert_true(actual_old_sigchld_action.sa_handler
7108
== expected_sigchld_action.sa_handler);
7109
/* Check member sa_mask */
7110
for(int signum = 1; signum < NSIG; signum++){
7111
const int expected_old_block_state
7112
= sigismember(&expected_sigchld_action.sa_mask, signum);
7113
g_assert_cmpint(expected_old_block_state, >=, 0);
7114
const int actual_old_block_state
7115
= sigismember(&actual_old_sigchld_action.sa_mask, signum);
7116
g_assert_cmpint(actual_old_block_state, >=, 0);
7117
g_assert_cmpint(actual_old_block_state,
7118
==, expected_old_block_state);
7120
/* Check member sa_flags */
7121
g_assert_true((actual_old_sigchld_action.sa_flags
7122
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
7123
== (expected_sigchld_action.sa_flags
7124
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
7126
/* Retrieve the current signal handler for SIGCHLD as set by
7127
setup_signal_handler() */
7128
struct sigaction actual_new_sigchld_action;
7129
g_assert_cmpint(sigaction(SIGCHLD, NULL,
7130
&actual_new_sigchld_action), ==, 0);
7131
/* Check that the signal handler (member sa_handler) is correctly
7132
set to the "handle_sigchld" function */
7133
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_DFL);
7134
g_assert_true(actual_new_sigchld_action.sa_handler != SIG_IGN);
7135
g_assert_true(actual_new_sigchld_action.sa_handler
7137
/* Check (in member sa_mask) that at least a handful of signals are
7138
actually blocked during the signal handler */
7139
for(int signum = 1; signum < NSIG; signum++){
7140
int actual_new_block_state;
7146
actual_new_block_state
7147
= sigismember(&actual_new_sigchld_action.sa_mask, signum);
7148
g_assert_cmpint(actual_new_block_state, ==, 1);
7150
case SIGKILL: /* non-blockable */
7151
case SIGSTOP: /* non-blockable */
7152
case SIGCHLD: /* always blocked */
7157
/* Check member sa_flags */
7158
g_assert_true((actual_new_sigchld_action.sa_flags
7159
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
7160
== (SA_NOCLDSTOP | SA_RESTART));
7162
/* Restore signal handler */
7163
g_assert_cmpint(sigaction(SIGCHLD, &expected_sigchld_action, NULL),
7167
static void test_restore_signal_handler(__attribute__((unused))
7168
test_fixture *fixture,
7169
__attribute__((unused))
7170
gconstpointer user_data){
7171
/* Save current SIGCHLD action, whatever it is */
7172
struct sigaction expected_sigchld_action;
7173
g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
7175
/* Since we haven't established a signal handler yet, there should
7176
not be one established. But another test may have relied on
7177
restore_signal_handler() to restore the signal handler, and if
7178
restore_signal_handler() is buggy (which we should be prepared
7179
for in this test) the signal handler may not have been restored
7180
properly; check for this: */
7181
g_assert_true(expected_sigchld_action.sa_handler != handle_sigchld);
7183
/* Establish a signal handler */
7184
struct sigaction sigchld_action = {
7185
.sa_handler=handle_sigchld,
7186
.sa_flags=SA_RESTART | SA_NOCLDSTOP,
7188
g_assert_cmpint(sigfillset(&sigchld_action.sa_mask), ==, 0);
7189
g_assert_cmpint(sigaction(SIGCHLD, &sigchld_action, NULL), ==, 0);
7191
/* Act; i.e. run the restore_signal_handler() function */
7192
g_assert_true(restore_signal_handler(&expected_sigchld_action));
7194
/* Retrieve the restored signal handler data */
7195
struct sigaction actual_restored_sigchld_action;
7196
g_assert_cmpint(sigaction(SIGCHLD, NULL,
7197
&actual_restored_sigchld_action), ==, 0);
7199
/* Check that the function correctly restored the signal action, as
7200
saved in "actual_restored_sigchld_action", to the same values as
7201
the previously saved "expected_sigchld_action" */
7202
/* Check member sa_handler */
7203
g_assert_true(actual_restored_sigchld_action.sa_handler
7204
== expected_sigchld_action.sa_handler);
7205
/* Check member sa_mask */
7206
for(int signum = 1; signum < NSIG; signum++){
7207
const int expected_old_block_state
7208
= sigismember(&expected_sigchld_action.sa_mask, signum);
7209
g_assert_cmpint(expected_old_block_state, >=, 0);
7210
const int actual_restored_block_state
7211
= sigismember(&actual_restored_sigchld_action.sa_mask, signum);
7212
g_assert_cmpint(actual_restored_block_state, >=, 0);
7213
g_assert_cmpint(actual_restored_block_state,
7214
==, expected_old_block_state);
7216
/* Check member sa_flags */
7217
g_assert_true((actual_restored_sigchld_action.sa_flags
7218
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
7219
== (expected_sigchld_action.sa_flags
7220
& (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
7223
static void test_block_sigchld(__attribute__((unused))
7224
test_fixture *fixture,
7225
__attribute__((unused))
7226
gconstpointer user_data){
7227
/* Save original signal mask */
7228
sigset_t expected_sigmask;
7229
g_assert_cmpint(pthread_sigmask(-1, NULL, &expected_sigmask),
7232
/* Make sure SIGCHLD is unblocked for this test */
7233
sigset_t sigchld_sigmask;
7234
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
7235
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
7236
g_assert_cmpint(pthread_sigmask(SIG_UNBLOCK, &sigchld_sigmask,
7239
/* Act; i.e. run the block_sigchld() function */
7240
sigset_t actual_old_sigmask;
7241
g_assert_true(block_sigchld(&actual_old_sigmask));
7243
/* Check the actual_old_sigmask; it should be the same as the
7244
previously saved signal mask "expected_sigmask". */
7245
for(int signum = 1; signum < NSIG; signum++){
7246
const int expected_old_block_state
7247
= sigismember(&expected_sigmask, signum);
7248
g_assert_cmpint(expected_old_block_state, >=, 0);
7249
const int actual_old_block_state
7250
= sigismember(&actual_old_sigmask, signum);
7251
g_assert_cmpint(actual_old_block_state, >=, 0);
7252
g_assert_cmpint(actual_old_block_state,
7253
==, expected_old_block_state);
7256
/* Retrieve the newly set signal mask */
7257
sigset_t actual_sigmask;
7258
g_assert_cmpint(pthread_sigmask(-1, NULL, &actual_sigmask), ==, 0);
7260
/* SIGCHLD should be blocked */
7261
g_assert_cmpint(sigismember(&actual_sigmask, SIGCHLD), ==, 1);
7263
/* Restore signal mask */
7264
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &expected_sigmask,
7268
static void test_restore_sigmask(__attribute__((unused))
7269
test_fixture *fixture,
7270
__attribute__((unused))
7271
gconstpointer user_data){
7272
/* Save original signal mask */
7273
sigset_t orig_sigmask;
7274
g_assert_cmpint(pthread_sigmask(-1, NULL, &orig_sigmask), ==, 0);
7276
/* Make sure SIGCHLD is blocked for this test */
7277
sigset_t sigchld_sigmask;
7278
g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
7279
g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
7280
g_assert_cmpint(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask,
7283
/* Act; i.e. run the restore_sigmask() function */
7284
g_assert_true(restore_sigmask(&orig_sigmask));
7286
/* Retrieve the newly restored signal mask */
7287
sigset_t restored_sigmask;
7288
g_assert_cmpint(pthread_sigmask(-1, NULL, &restored_sigmask),
7291
/* Check the restored_sigmask; it should be the same as the
7292
previously saved signal mask "orig_sigmask". */
7293
for(int signum = 1; signum < NSIG; signum++){
7294
const int orig_block_state = sigismember(&orig_sigmask, signum);
7295
g_assert_cmpint(orig_block_state, >=, 0);
7296
const int restored_block_state = sigismember(&restored_sigmask,
7298
g_assert_cmpint(restored_block_state, >=, 0);
7299
g_assert_cmpint(restored_block_state, ==, orig_block_state);
7302
/* Restore signal mask */
7303
g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &orig_sigmask,
7307
static void test_parse_arguments_noargs(__attribute__((unused))
7308
test_fixture *fixture,
7309
__attribute__((unused))
7310
gconstpointer user_data){
7314
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7316
char *agent_directory = NULL;
7317
char *helper_directory = NULL;
7320
char *mandos_argz = NULL;
7321
size_t mandos_argz_length = 0;
7323
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7324
&helper_directory, &user, &group,
7325
&mandos_argz, &mandos_argz_length));
7326
g_assert_null(agent_directory);
7327
g_assert_null(helper_directory);
7328
g_assert_true(user == 0);
7329
g_assert_true(group == 0);
7330
g_assert_null(mandos_argz);
7331
g_assert_true(mandos_argz_length == 0);
7333
for(char **arg = argv; *arg != NULL; arg++){
7338
__attribute__((nonnull))
7339
static bool parse_arguments_devnull(int argc, char *argv[],
7340
const bool exit_failure,
7341
char **agent_directory,
7342
char **helper_directory,
7346
size_t *mandos_argz_length){
7348
FILE *real_stderr = stderr;
7349
FILE *devnull = fopen("/dev/null", "we");
7350
g_assert_nonnull(devnull);
7353
const bool ret = parse_arguments(argc, argv, exit_failure,
7355
helper_directory, user, group,
7356
mandos_argz, mandos_argz_length);
7357
const error_t saved_errno = errno;
7359
stderr = real_stderr;
7360
g_assert_cmpint(fclose(devnull), ==, 0);
7362
errno = saved_errno;
7367
static void test_parse_arguments_invalid(__attribute__((unused))
7368
test_fixture *fixture,
7369
__attribute__((unused))
7370
gconstpointer user_data){
7373
strdup("--invalid"),
7375
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7377
char *agent_directory = NULL;
7378
char *helper_directory = NULL;
7381
char *mandos_argz = NULL;
7382
size_t mandos_argz_length = 0;
7384
g_assert_false(parse_arguments_devnull(argc, argv, false,
7386
&helper_directory, &user,
7387
&group, &mandos_argz,
7388
&mandos_argz_length));
7390
g_assert_true(errno == EINVAL);
7391
g_assert_null(agent_directory);
7392
g_assert_null(helper_directory);
7393
g_assert_null(mandos_argz);
7394
g_assert_true(mandos_argz_length == 0);
7396
for(char **arg = argv; *arg != NULL; arg++){
7401
static void test_parse_arguments_long_dir(__attribute__((unused))
7402
test_fixture *fixture,
7403
__attribute__((unused))
7404
gconstpointer user_data){
7407
strdup("--agent-directory"),
7410
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7412
__attribute__((cleanup(cleanup_string)))
7413
char *agent_directory = NULL;
7414
char *helper_directory = NULL;
7417
__attribute__((cleanup(cleanup_string)))
7418
char *mandos_argz = NULL;
7419
size_t mandos_argz_length = 0;
7421
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7422
&helper_directory, &user, &group,
7423
&mandos_argz, &mandos_argz_length));
7425
g_assert_cmpstr(agent_directory, ==, "/tmp");
7426
g_assert_null(helper_directory);
7427
g_assert_true(user == 0);
7428
g_assert_true(group == 0);
7429
g_assert_null(mandos_argz);
7430
g_assert_true(mandos_argz_length == 0);
7432
for(char **arg = argv; *arg != NULL; arg++){
7437
static void test_parse_arguments_short_dir(__attribute__((unused))
7438
test_fixture *fixture,
7439
__attribute__((unused))
7440
gconstpointer user_data){
7446
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7448
__attribute__((cleanup(cleanup_string)))
7449
char *agent_directory = NULL;
7450
char *helper_directory = NULL;
7453
__attribute__((cleanup(cleanup_string)))
7454
char *mandos_argz = NULL;
7455
size_t mandos_argz_length = 0;
7457
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7458
&helper_directory, &user, &group,
7459
&mandos_argz, &mandos_argz_length));
7461
g_assert_cmpstr(agent_directory, ==, "/tmp");
7462
g_assert_null(helper_directory);
7463
g_assert_true(user == 0);
7464
g_assert_true(group == 0);
7465
g_assert_null(mandos_argz);
7466
g_assert_true(mandos_argz_length == 0);
7468
for(char **arg = argv; *arg != NULL; arg++){
7474
void test_parse_arguments_helper_directory(__attribute__((unused))
7475
test_fixture *fixture,
7476
__attribute__((unused))
7477
gconstpointer user_data){
7480
strdup("--helper-directory"),
7483
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7485
char *agent_directory = NULL;
7486
__attribute__((cleanup(cleanup_string)))
7487
char *helper_directory = NULL;
7490
__attribute__((cleanup(cleanup_string)))
7491
char *mandos_argz = NULL;
7492
size_t mandos_argz_length = 0;
7494
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7495
&helper_directory, &user, &group,
7496
&mandos_argz, &mandos_argz_length));
7498
g_assert_cmpstr(helper_directory, ==, "/tmp");
7499
g_assert_null(agent_directory);
7500
g_assert_true(user == 0);
7501
g_assert_true(group == 0);
7502
g_assert_null(mandos_argz);
7503
g_assert_true(mandos_argz_length == 0);
7505
for(char **arg = argv; *arg != NULL; arg++){
7511
void test_parse_arguments_plugin_helper_dir(__attribute__((unused))
7512
test_fixture *fixture,
7513
__attribute__((unused))
7514
gconstpointer user_data){
7517
strdup("--plugin-helper-dir"),
7520
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7522
char *agent_directory = NULL;
7523
__attribute__((cleanup(cleanup_string)))
7524
char *helper_directory = NULL;
7527
__attribute__((cleanup(cleanup_string)))
7528
char *mandos_argz = NULL;
7529
size_t mandos_argz_length = 0;
7531
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7532
&helper_directory, &user, &group,
7533
&mandos_argz, &mandos_argz_length));
7535
g_assert_cmpstr(helper_directory, ==, "/tmp");
7536
g_assert_null(agent_directory);
7537
g_assert_true(user == 0);
7538
g_assert_true(group == 0);
7539
g_assert_null(mandos_argz);
7540
g_assert_true(mandos_argz_length == 0);
7542
for(char **arg = argv; *arg != NULL; arg++){
7547
static void test_parse_arguments_user(__attribute__((unused))
7548
test_fixture *fixture,
7549
__attribute__((unused))
7550
gconstpointer user_data){
7556
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7558
char *agent_directory = NULL;
7559
__attribute__((cleanup(cleanup_string)))
7560
char *helper_directory = NULL;
7563
__attribute__((cleanup(cleanup_string)))
7564
char *mandos_argz = NULL;
7565
size_t mandos_argz_length = 0;
7567
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7568
&helper_directory, &user, &group,
7569
&mandos_argz, &mandos_argz_length));
7571
g_assert_null(helper_directory);
7572
g_assert_null(agent_directory);
7573
g_assert_cmpuint((unsigned int)user, ==, 1000);
7574
g_assert_true(group == 0);
7575
g_assert_null(mandos_argz);
7576
g_assert_true(mandos_argz_length == 0);
7578
for(char **arg = argv; *arg != NULL; arg++){
7583
static void test_parse_arguments_user_invalid(__attribute__((unused))
7584
test_fixture *fixture,
7585
__attribute__((unused))
7593
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7595
char *agent_directory = NULL;
7596
__attribute__((cleanup(cleanup_string)))
7597
char *helper_directory = NULL;
7600
__attribute__((cleanup(cleanup_string)))
7601
char *mandos_argz = NULL;
7602
size_t mandos_argz_length = 0;
7604
g_assert_false(parse_arguments_devnull(argc, argv, false,
7606
&helper_directory, &user,
7607
&group, &mandos_argz,
7608
&mandos_argz_length));
7610
g_assert_null(helper_directory);
7611
g_assert_null(agent_directory);
7612
g_assert_cmpuint((unsigned int)user, ==, 0);
7613
g_assert_true(group == 0);
7614
g_assert_null(mandos_argz);
7615
g_assert_true(mandos_argz_length == 0);
7617
for(char **arg = argv; *arg != NULL; arg++){
7623
void test_parse_arguments_user_zero_invalid(__attribute__((unused))
7624
test_fixture *fixture,
7625
__attribute__((unused))
7626
gconstpointer user_data){
7632
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7634
char *agent_directory = NULL;
7635
__attribute__((cleanup(cleanup_string)))
7636
char *helper_directory = NULL;
7639
__attribute__((cleanup(cleanup_string)))
7640
char *mandos_argz = NULL;
7641
size_t mandos_argz_length = 0;
7643
g_assert_false(parse_arguments_devnull(argc, argv, false,
7645
&helper_directory, &user,
7646
&group, &mandos_argz,
7647
&mandos_argz_length));
7649
g_assert_null(helper_directory);
7650
g_assert_null(agent_directory);
7651
g_assert_cmpuint((unsigned int)user, ==, 0);
7652
g_assert_true(group == 0);
7653
g_assert_null(mandos_argz);
7654
g_assert_true(mandos_argz_length == 0);
7656
for(char **arg = argv; *arg != NULL; arg++){
7661
static void test_parse_arguments_group(__attribute__((unused))
7662
test_fixture *fixture,
7663
__attribute__((unused))
7664
gconstpointer user_data){
7670
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7672
char *agent_directory = NULL;
7673
__attribute__((cleanup(cleanup_string)))
7674
char *helper_directory = NULL;
7677
__attribute__((cleanup(cleanup_string)))
7678
char *mandos_argz = NULL;
7679
size_t mandos_argz_length = 0;
7681
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7682
&helper_directory, &user, &group,
7683
&mandos_argz, &mandos_argz_length));
7685
g_assert_null(helper_directory);
7686
g_assert_null(agent_directory);
7687
g_assert_true(user == 0);
7688
g_assert_cmpuint((unsigned int)group, ==, 1000);
7689
g_assert_null(mandos_argz);
7690
g_assert_true(mandos_argz_length == 0);
7692
for(char **arg = argv; *arg != NULL; arg++){
7697
static void test_parse_arguments_group_invalid(__attribute__((unused))
7698
test_fixture *fixture,
7699
__attribute__((unused))
7707
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7709
char *agent_directory = NULL;
7710
__attribute__((cleanup(cleanup_string)))
7711
char *helper_directory = NULL;
7714
__attribute__((cleanup(cleanup_string)))
7715
char *mandos_argz = NULL;
7716
size_t mandos_argz_length = 0;
7718
g_assert_false(parse_arguments_devnull(argc, argv, false,
7720
&helper_directory, &user,
7721
&group, &mandos_argz,
7722
&mandos_argz_length));
7724
g_assert_null(helper_directory);
7725
g_assert_null(agent_directory);
7726
g_assert_true(user == 0);
7727
g_assert_true(group == 0);
7728
g_assert_null(mandos_argz);
7729
g_assert_true(mandos_argz_length == 0);
7731
for(char **arg = argv; *arg != NULL; arg++){
7737
void test_parse_arguments_group_zero_invalid(__attribute__((unused))
7738
test_fixture *fixture,
7739
__attribute__((unused))
7740
gconstpointer user_data){
7746
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
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_false(parse_arguments_devnull(argc, argv, false,
7759
&helper_directory, &user,
7760
&group, &mandos_argz,
7761
&mandos_argz_length));
7763
g_assert_null(helper_directory);
7764
g_assert_null(agent_directory);
7765
g_assert_cmpuint((unsigned int)group, ==, 0);
7766
g_assert_true(group == 0);
7767
g_assert_null(mandos_argz);
7768
g_assert_true(mandos_argz_length == 0);
7770
for(char **arg = argv; *arg != NULL; arg++){
7775
static void test_parse_arguments_mandos_noargs(__attribute__((unused))
7776
test_fixture *fixture,
7777
__attribute__((unused))
7782
strdup("mandos-client"),
7784
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7786
__attribute__((cleanup(cleanup_string)))
7787
char *agent_directory = NULL;
7788
__attribute__((cleanup(cleanup_string)))
7789
char *helper_directory = NULL;
7792
__attribute__((cleanup(cleanup_string)))
7793
char *mandos_argz = NULL;
7794
size_t mandos_argz_length = 0;
7796
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7797
&helper_directory, &user, &group,
7798
&mandos_argz, &mandos_argz_length));
7800
g_assert_null(agent_directory);
7801
g_assert_null(helper_directory);
7802
g_assert_true(user == 0);
7803
g_assert_true(group == 0);
7804
g_assert_cmpstr(mandos_argz, ==, "mandos-client");
7805
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7806
mandos_argz_length),
7809
for(char **arg = argv; *arg != NULL; arg++){
7814
static void test_parse_arguments_mandos_args(__attribute__((unused))
7815
test_fixture *fixture,
7816
__attribute__((unused))
7817
gconstpointer user_data){
7820
strdup("mandos-client"),
7825
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7827
__attribute__((cleanup(cleanup_string)))
7828
char *agent_directory = NULL;
7829
__attribute__((cleanup(cleanup_string)))
7830
char *helper_directory = NULL;
7833
__attribute__((cleanup(cleanup_string)))
7834
char *mandos_argz = NULL;
7835
size_t mandos_argz_length = 0;
7837
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7838
&helper_directory, &user, &group,
7839
&mandos_argz, &mandos_argz_length));
7841
g_assert_null(agent_directory);
7842
g_assert_null(helper_directory);
7843
g_assert_true(user == 0);
7844
g_assert_true(group == 0);
7845
char *marg = mandos_argz;
7846
g_assert_cmpstr(marg, ==, "mandos-client");
7847
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7848
g_assert_cmpstr(marg, ==, "one");
7849
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7850
g_assert_cmpstr(marg, ==, "two");
7851
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7852
g_assert_cmpstr(marg, ==, "three");
7853
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7854
mandos_argz_length),
7857
for(char **arg = argv; *arg != NULL; arg++){
7862
static void test_parse_arguments_all_args(__attribute__((unused))
7863
test_fixture *fixture,
7864
__attribute__((unused))
7865
gconstpointer user_data){
7868
strdup("--agent-directory"),
7870
strdup("--helper-directory"),
7876
strdup("mandos-client"),
7881
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7883
__attribute__((cleanup(cleanup_string)))
7884
char *agent_directory = NULL;
7885
__attribute__((cleanup(cleanup_string)))
7886
char *helper_directory = NULL;
7889
__attribute__((cleanup(cleanup_string)))
7890
char *mandos_argz = NULL;
7891
size_t mandos_argz_length = 0;
7893
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7894
&helper_directory, &user, &group,
7895
&mandos_argz, &mandos_argz_length));
7897
g_assert_cmpstr(agent_directory, ==, "/tmp");
7898
g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7899
g_assert_true(user == 1);
7900
g_assert_true(group == 2);
7901
char *marg = mandos_argz;
7902
g_assert_cmpstr(marg, ==, "mandos-client");
7903
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7904
g_assert_cmpstr(marg, ==, "one");
7905
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7906
g_assert_cmpstr(marg, ==, "two");
7907
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7908
g_assert_cmpstr(marg, ==, "three");
7909
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7910
mandos_argz_length),
7913
for(char **arg = argv; *arg != NULL; arg++){
7918
static void test_parse_arguments_mixed(__attribute__((unused))
7919
test_fixture *fixture,
7920
__attribute__((unused))
7921
gconstpointer user_data){
7924
strdup("mandos-client"),
7928
strdup("--agent-directory"),
7932
strdup("--helper-directory=/var/tmp"),
7934
const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7936
__attribute__((cleanup(cleanup_string)))
7937
char *agent_directory = NULL;
7938
__attribute__((cleanup(cleanup_string)))
7939
char *helper_directory = NULL;
7942
__attribute__((cleanup(cleanup_string)))
7943
char *mandos_argz = NULL;
7944
size_t mandos_argz_length = 0;
7946
g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7947
&helper_directory, &user, &group,
7948
&mandos_argz, &mandos_argz_length));
7950
g_assert_cmpstr(agent_directory, ==, "/tmp");
7951
g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7952
g_assert_true(user == 1);
7953
g_assert_true(group == 0);
7954
char *marg = mandos_argz;
7955
g_assert_cmpstr(marg, ==, "mandos-client");
7956
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7957
g_assert_cmpstr(marg, ==, "one");
7958
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7959
g_assert_cmpstr(marg, ==, "two");
7960
marg = argz_next(mandos_argz, mandos_argz_length, marg);
7961
g_assert_cmpstr(marg, ==, "three");
7962
g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7963
mandos_argz_length),
7966
for(char **arg = argv; *arg != NULL; arg++){
7971
/* End of tests section */
7973
/* Test boilerplate section; New tests should be added to the test
7974
suite definition here, in the "run_tests" function.
7976
Finally, this section also contains the should_only_run_tests()
7977
function used by main() for deciding if tests should be run or to
7980
__attribute__((cold))
7981
static bool run_tests(int argc, char *argv[]){
7982
g_test_init(&argc, &argv, NULL);
7984
/* A macro to add a test with no setup or teardown functions */
7985
#define test_add(testpath, testfunc) \
7987
g_test_add((testpath), test_fixture, NULL, NULL, \
7988
(testfunc), NULL); \
7991
/* Test the signal-related functions first, since some other tests
7992
depend on these functions in their setups and teardowns */
7993
test_add("/signal-handling/setup", test_setup_signal_handler);
7994
test_add("/signal-handling/restore", test_restore_signal_handler);
7995
test_add("/signal-handling/block", test_block_sigchld);
7996
test_add("/signal-handling/restore-sigmask", test_restore_sigmask);
7998
/* Regular non-signal-related tests; these use no setups or
8000
test_add("/parse_arguments/noargs", test_parse_arguments_noargs);
8001
test_add("/parse_arguments/invalid", test_parse_arguments_invalid);
8002
test_add("/parse_arguments/long-dir",
8003
test_parse_arguments_long_dir);
8004
test_add("/parse_arguments/short-dir",
8005
test_parse_arguments_short_dir);
8006
test_add("/parse_arguments/helper-directory",
8007
test_parse_arguments_helper_directory);
8008
test_add("/parse_arguments/plugin-helper-dir",
8009
test_parse_arguments_plugin_helper_dir);
8010
test_add("/parse_arguments/user", test_parse_arguments_user);
8011
test_add("/parse_arguments/user-invalid",
8012
test_parse_arguments_user_invalid);
8013
test_add("/parse_arguments/user-zero-invalid",
8014
test_parse_arguments_user_zero_invalid);
8015
test_add("/parse_arguments/group", test_parse_arguments_group);
8016
test_add("/parse_arguments/group-invalid",
8017
test_parse_arguments_group_invalid);
8018
test_add("/parse_arguments/group-zero-invalid",
8019
test_parse_arguments_group_zero_invalid);
8020
test_add("/parse_arguments/mandos-noargs",
8021
test_parse_arguments_mandos_noargs);
8022
test_add("/parse_arguments/mandos-args",
8023
test_parse_arguments_mandos_args);
8024
test_add("/parse_arguments/all-args",
8025
test_parse_arguments_all_args);
8026
test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
8027
test_add("/queue/create", test_create_queue);
8028
test_add("/queue/add", test_add_to_queue);
8029
test_add("/queue/add/overflow", test_add_to_queue_overflow);
8030
test_add("/queue/has_question/empty",
8031
test_queue_has_question_empty);
8032
test_add("/queue/has_question/false",
8033
test_queue_has_question_false);
8034
test_add("/queue/has_question/true", test_queue_has_question_true);
8035
test_add("/queue/has_question/false2",
8036
test_queue_has_question_false2);
8037
test_add("/queue/has_question/true2",
8038
test_queue_has_question_true2);
8039
test_add("/buffer/cleanup", test_cleanup_buffer);
8040
test_add("/string_set/net-set-contains-nothing",
8041
test_string_set_new_set_contains_nothing);
8042
test_add("/string_set/with-added-string-contains-it",
8043
test_string_set_with_added_string_contains_it);
8044
test_add("/string_set/cleared-does-not-contain-string",
8045
test_string_set_cleared_does_not_contain_str);
8046
test_add("/string_set/swap/one-with-empty",
8047
test_string_set_swap_one_with_empty);
8048
test_add("/string_set/swap/empty-with-one",
8049
test_string_set_swap_empty_with_one);
8050
test_add("/string_set/swap/one-with-one",
8051
test_string_set_swap_one_with_one);
8053
/* A macro to add a test using the setup and teardown functions */
8054
#define test_add_st(path, func) \
8056
g_test_add((path), test_fixture, NULL, test_setup, (func), \
8060
/* Signal-related tests; these use setups and teardowns which
8061
establish, during each test run, a signal handler for, and a
8062
signal mask blocking, the SIGCHLD signal, just like main() */
8063
test_add_st("/wait_for_event/timeout", test_wait_for_event_timeout);
8064
test_add_st("/wait_for_event/event", test_wait_for_event_event);
8065
test_add_st("/wait_for_event/sigchld", test_wait_for_event_sigchld);
8066
test_add_st("/run_queue/zeroes-next-run",
8067
test_run_queue_zeroes_next_run);
8068
test_add_st("/run_queue/clears-cancelled_filenames",
8069
test_run_queue_clears_cancelled_filenames);
8070
test_add_st("/run_queue/skips-cancelled-filenames",
8071
test_run_queue_skips_cancelled_filenames);
8072
test_add_st("/run_queue/one-task", test_run_queue_one_task);
8073
test_add_st("/run_queue/two-tasks", test_run_queue_two_tasks);
8074
test_add_st("/run_queue/two-tasks/quit",
8075
test_run_queue_two_tasks_quit);
8076
test_add_st("/run_queue/two-tasks-cleanup",
8077
test_run_queue_two_tasks_cleanup);
8078
test_add_st("/task-creators/start_mandos_client",
8079
test_start_mandos_client);
8080
test_add_st("/task-creators/start_mandos_client/execv",
8081
test_start_mandos_client_execv);
8082
test_add_st("/task-creators/start_mandos_client/suid/euid",
8083
test_start_mandos_client_suid_euid);
8084
test_add_st("/task-creators/start_mandos_client/suid/egid",
8085
test_start_mandos_client_suid_egid);
8086
test_add_st("/task-creators/start_mandos_client/suid/ruid",
8087
test_start_mandos_client_suid_ruid);
8088
test_add_st("/task-creators/start_mandos_client/suid/rgid",
8089
test_start_mandos_client_suid_rgid);
8090
test_add_st("/task-creators/start_mandos_client/read",
8091
test_start_mandos_client_read);
8092
test_add_st("/task-creators/start_mandos_client/helper-directory",
8093
test_start_mandos_client_helper_directory);
8094
test_add_st("/task-creators/start_mandos_client/sigmask",
8095
test_start_mandos_client_sigmask);
8096
test_add_st("/task/wait_for_mandos_client_exit/badpid",
8097
test_wait_for_mandos_client_exit_badpid);
8098
test_add_st("/task/wait_for_mandos_client_exit/noexit",
8099
test_wait_for_mandos_client_exit_noexit);
8100
test_add_st("/task/wait_for_mandos_client_exit/success",
8101
test_wait_for_mandos_client_exit_success);
8102
test_add_st("/task/wait_for_mandos_client_exit/failure",
8103
test_wait_for_mandos_client_exit_failure);
8104
test_add_st("/task/wait_for_mandos_client_exit/killed",
8105
test_wait_for_mandos_client_exit_killed);
8106
test_add_st("/task/read_mandos_client_output/readerror",
8107
test_read_mandos_client_output_readerror);
8108
test_add_st("/task/read_mandos_client_output/nodata",
8109
test_read_mandos_client_output_nodata);
8110
test_add_st("/task/read_mandos_client_output/eof",
8111
test_read_mandos_client_output_eof);
8112
test_add_st("/task/read_mandos_client_output/once",
8113
test_read_mandos_client_output_once);
8114
test_add_st("/task/read_mandos_client_output/malloc",
8115
test_read_mandos_client_output_malloc);
8116
test_add_st("/task/read_mandos_client_output/append",
8117
test_read_mandos_client_output_append);
8118
test_add_st("/task-creators/add_inotify_dir_watch",
8119
test_add_inotify_dir_watch);
8120
test_add_st("/task-creators/add_inotify_dir_watch/fail",
8121
test_add_inotify_dir_watch_fail);
8122
test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory",
8123
test_add_inotify_dir_watch_nondir);
8124
test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
8125
test_add_inotify_dir_watch_EAGAIN);
8126
test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
8127
test_add_inotify_dir_watch_IN_CLOSE_WRITE);
8128
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
8129
test_add_inotify_dir_watch_IN_MOVED_TO);
8130
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
8131
test_add_inotify_dir_watch_IN_MOVED_FROM);
8132
test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
8133
test_add_inotify_dir_watch_IN_EXCL_UNLINK);
8134
test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
8135
test_add_inotify_dir_watch_IN_DELETE);
8136
test_add_st("/task/read_inotify_event/readerror",
8137
test_read_inotify_event_readerror);
8138
test_add_st("/task/read_inotify_event/bad-epoll",
8139
test_read_inotify_event_bad_epoll);
8140
test_add_st("/task/read_inotify_event/nodata",
8141
test_read_inotify_event_nodata);
8142
test_add_st("/task/read_inotify_event/eof",
8143
test_read_inotify_event_eof);
8144
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE",
8145
test_read_inotify_event_IN_CLOSE_WRITE);
8146
test_add_st("/task/read_inotify_event/IN_MOVED_TO",
8147
test_read_inotify_event_IN_MOVED_TO);
8148
test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
8149
test_read_inotify_event_IN_MOVED_FROM);
8150
test_add_st("/task/read_inotify_event/IN_DELETE",
8151
test_read_inotify_event_IN_DELETE);
8152
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
8153
test_read_inotify_event_IN_CLOSE_WRITE_badname);
8154
test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
8155
test_read_inotify_event_IN_MOVED_TO_badname);
8156
test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
8157
test_read_inotify_event_IN_MOVED_FROM_badname);
8158
test_add_st("/task/read_inotify_event/IN_DELETE/badname",
8159
test_read_inotify_event_IN_DELETE_badname);
8160
test_add_st("/task/open_and_parse_question/ENOENT",
8161
test_open_and_parse_question_ENOENT);
8162
test_add_st("/task/open_and_parse_question/EIO",
8163
test_open_and_parse_question_EIO);
8164
test_add_st("/task/open_and_parse_question/parse-error",
8165
test_open_and_parse_question_parse_error);
8166
test_add_st("/task/open_and_parse_question/nosocket",
8167
test_open_and_parse_question_nosocket);
8168
test_add_st("/task/open_and_parse_question/badsocket",
8169
test_open_and_parse_question_badsocket);
8170
test_add_st("/task/open_and_parse_question/nopid",
8171
test_open_and_parse_question_nopid);
8172
test_add_st("/task/open_and_parse_question/badpid",
8173
test_open_and_parse_question_badpid);
8174
test_add_st("/task/open_and_parse_question/noexist_pid",
8175
test_open_and_parse_question_noexist_pid);
8176
test_add_st("/task/open_and_parse_question/no-notafter",
8177
test_open_and_parse_question_no_notafter);
8178
test_add_st("/task/open_and_parse_question/bad-notafter",
8179
test_open_and_parse_question_bad_notafter);
8180
test_add_st("/task/open_and_parse_question/notafter-0",
8181
test_open_and_parse_question_notafter_0);
8182
test_add_st("/task/open_and_parse_question/notafter-1",
8183
test_open_and_parse_question_notafter_1);
8184
test_add_st("/task/open_and_parse_question/notafter-1-1",
8185
test_open_and_parse_question_notafter_1_1);
8186
test_add_st("/task/open_and_parse_question/notafter-1-2",
8187
test_open_and_parse_question_notafter_1_2);
8188
test_add_st("/task/open_and_parse_question/equal-notafter",
8189
test_open_and_parse_question_equal_notafter);
8190
test_add_st("/task/open_and_parse_question/late-notafter",
8191
test_open_and_parse_question_late_notafter);
8192
test_add_st("/task/cancel_old_question/0-1-2",
8193
test_cancel_old_question_0_1_2);
8194
test_add_st("/task/cancel_old_question/0-2-1",
8195
test_cancel_old_question_0_2_1);
8196
test_add_st("/task/cancel_old_question/1-2-3",
8197
test_cancel_old_question_1_2_3);
8198
test_add_st("/task/cancel_old_question/1-3-2",
8199
test_cancel_old_question_1_3_2);
8200
test_add_st("/task/cancel_old_question/2-1-3",
8201
test_cancel_old_question_2_1_3);
8202
test_add_st("/task/cancel_old_question/2-3-1",
8203
test_cancel_old_question_2_3_1);
8204
test_add_st("/task/cancel_old_question/3-1-2",
8205
test_cancel_old_question_3_1_2);
8206
test_add_st("/task/cancel_old_question/3-2-1",
8207
test_cancel_old_question_3_2_1);
8208
test_add_st("/task/connect_question_socket/name-too-long",
8209
test_connect_question_socket_name_too_long);
8210
test_add_st("/task/connect_question_socket/connect-fail",
8211
test_connect_question_socket_connect_fail);
8212
test_add_st("/task/connect_question_socket/bad-epoll",
8213
test_connect_question_socket_bad_epoll);
8214
test_add_st("/task/connect_question_socket/usable",
8215
test_connect_question_socket_usable);
8216
test_add_st("/task/send_password_to_socket/client-not-exited",
8217
test_send_password_to_socket_client_not_exited);
8218
test_add_st("/task/send_password_to_socket/password-not-read",
8219
test_send_password_to_socket_password_not_read);
8220
test_add_st("/task/send_password_to_socket/EMSGSIZE",
8221
test_send_password_to_socket_EMSGSIZE);
8222
test_add_st("/task/send_password_to_socket/retry",
8223
test_send_password_to_socket_retry);
8224
test_add_st("/task/send_password_to_socket/bad-epoll",
8225
test_send_password_to_socket_bad_epoll);
8226
test_add_st("/task/send_password_to_socket/null-password",
8227
test_send_password_to_socket_null_password);
8228
test_add_st("/task/send_password_to_socket/empty-password",
8229
test_send_password_to_socket_empty_password);
8230
test_add_st("/task/send_password_to_socket/empty-str-password",
8231
test_send_password_to_socket_empty_str_pass);
8232
test_add_st("/task/send_password_to_socket/text-password",
8233
test_send_password_to_socket_text_password);
8234
test_add_st("/task/send_password_to_socket/binary-password",
8235
test_send_password_to_socket_binary_password);
8236
test_add_st("/task/send_password_to_socket/nuls-in-password",
8237
test_send_password_to_socket_nuls_in_password);
8238
test_add_st("/task-creators/add_existing_questions/ENOENT",
8239
test_add_existing_questions_ENOENT);
8240
test_add_st("/task-creators/add_existing_questions/no-questions",
8241
test_add_existing_questions_no_questions);
8242
test_add_st("/task-creators/add_existing_questions/one-question",
8243
test_add_existing_questions_one_question);
8244
test_add_st("/task-creators/add_existing_questions/two-questions",
8245
test_add_existing_questions_two_questions);
8246
test_add_st("/task-creators/add_existing_questions/non-questions",
8247
test_add_existing_questions_non_questions);
8248
test_add_st("/task-creators/add_existing_questions/both-types",
8249
test_add_existing_questions_both_types);
8251
return g_test_run() == 0;
8254
static bool should_only_run_tests(int *argc_p, char **argv_p[]){
8255
GOptionContext *context = g_option_context_new("");
8257
g_option_context_set_help_enabled(context, FALSE);
8258
g_option_context_set_ignore_unknown_options(context, TRUE);
8260
gboolean should_run_tests = FALSE;
8261
GOptionEntry entries[] = {
8262
{ "test", 0, 0, G_OPTION_ARG_NONE,
8263
&should_run_tests, "Run tests", NULL },
8266
g_option_context_add_main_entries(context, entries, NULL);
8268
GError *error = NULL;
8270
if(g_option_context_parse(context, argc_p, argv_p, &error) != TRUE){
8271
g_option_context_free(context);
8272
g_error("Failed to parse options: %s", error->message);
8275
g_option_context_free(context);
8276
return should_run_tests != FALSE;
8283
(if (not (funcall run-tests-in-test-buffer default-directory))
8284
(funcall show-test-buffer-in-test-window)
8285
(funcall remove-test-window)))
8286
run-tests-in-test-buffer:
8288
(with-current-buffer (get-buffer-create "*Test*")
8289
(setq buffer-read-only nil
8290
default-directory dir)
8293
(let ((process-result
8294
(let ((inhibit-read-only t))
8295
(process-file-shell-command
8296
(funcall get-command-line) nil "*Test*"))))
8297
(and (numberp process-result)
8298
(= process-result 0))))
8303
(funcall find-build-directory (buffer-file-name)))
8304
(local-build-directory
8305
(if (fboundp 'file-local-name)
8306
(file-local-name build-directory)
8307
(or (file-remote-p build-directory 'localname)
8310
(file-relative-name (file-name-sans-extension
8311
(buffer-file-name)) build-directory))
8312
(qbdir (shell-quote-argument local-build-directory))
8313
(qcmd (shell-quote-argument command)))
8314
(format (concat "cd %s && CFLAGS=-Werror make --silent %s"
8315
" && %s --test --verbose") qbdir qcmd qcmd)))
8316
find-build-directory:
8317
(lambda (try-directory &optional base-directory)
8318
(let ((base-directory (or base-directory try-directory)))
8319
(cond ((equal try-directory "/") base-directory)
8321
(concat (file-name-as-directory try-directory)
8322
"Makefile")) try-directory)
8323
((funcall find-build-directory
8324
(directory-file-name (file-name-directory
8327
show-test-buffer-in-test-window:
8329
(when (not (get-buffer-window-list "*Test*"))
8330
(setq next-error-last-buffer (get-buffer "*Test*"))
8331
(let* ((side (if (>= (window-width) 146) 'right 'bottom))
8332
(display-buffer-overriding-action
8333
`((display-buffer-in-side-window) (side . ,side)
8334
(window-height . fit-window-to-buffer)
8335
(window-width . fit-window-to-buffer))))
8336
(display-buffer "*Test*"))))
8339
(let ((test-window (get-buffer-window "*Test*")))
8340
(if test-window (delete-window test-window))))
8341
eval: (add-hook 'after-save-hook run-tests 90 t)