1
 
/* -*- mode: c; coding: utf-8; after-save-hook: (lambda () (let* ((find-build-directory (lambda (try-directory &optional base-directory) (let ((base-directory (or base-directory try-directory))) (cond ((equal try-directory "/") base-directory) ((file-readable-p (concat (file-name-as-directory try-directory) "Makefile")) try-directory) ((funcall find-build-directory (directory-file-name (file-name-directory try-directory)) base-directory)))))) (build-directory (funcall find-build-directory (buffer-file-name))) (local-build-directory (if (fboundp 'file-local-name) (file-local-name build-directory) (or (file-remote-p build-directory 'localname) build-directory))) (command (file-relative-name (file-name-sans-extension (buffer-file-name)) build-directory))) (pcase (progn (if (get-buffer "*Test*") (kill-buffer "*Test*")) (process-file-shell-command (let ((qbdir (shell-quote-argument local-build-directory)) (qcmd (shell-quote-argument command))) (format "cd %s && CFLAGS=-Werror make --silent %s && %s --test --verbose" qbdir qcmd qcmd)) nil "*Test*")) (0 (let ((w (get-buffer-window "*Test*"))) (if w (delete-window w)))) (_ (with-current-buffer "*Test*" (compilation-mode) (cd-absolute build-directory)) (display-buffer "*Test*" '(display-buffer-in-side-window)))))); -*- */
 
3
 
 * Mandos password agent - Simple password agent to run Mandos client
 
5
 
 * Copyright © 2019-2020 Teddy Hogeborn
 
6
 
 * Copyright © 2019-2020 Björn Påhlsson
 
8
 
 * This file is part of Mandos.
 
10
 
 * Mandos is free software: you can redistribute it and/or modify it
 
11
 
 * under the terms of the GNU General Public License as published by
 
12
 
 * the Free Software Foundation, either version 3 of the License, or
 
13
 
 * (at your option) any later version.
 
15
 
 * Mandos is distributed in the hope that it will be useful, but
 
16
 
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 
17
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
18
 
 * General Public License for more details.
 
20
 
 * You should have received a copy of the GNU General Public License
 
21
 
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
 
23
 
 * Contact the authors at <mandos@recompile.se>.
 
27
 
#include <inttypes.h>           /* uintmax_t, PRIuMAX, PRIdMAX,
 
28
 
                                   intmax_t, uint32_t, SCNx32,
 
30
 
#include <stddef.h>             /* size_t */
 
31
 
#include <sys/types.h>          /* pid_t, uid_t, gid_t, getuid(),
 
33
 
#include <stdbool.h>            /* bool, true, false */
 
34
 
#include <signal.h>             /* struct sigaction, sigset_t,
 
35
 
                                   sigemptyset(), sigaddset(),
 
36
 
                                   SIGCHLD, pthread_sigmask(),
 
37
 
                                   SIG_BLOCK, SIG_SETMASK, SA_RESTART,
 
38
 
                                   SA_NOCLDSTOP, sigfillset(), kill(),
 
39
 
                                   SIGTERM, sigdelset(), SIGKILL,
 
40
 
                                   NSIG, sigismember(), SA_ONSTACK,
 
41
 
                                   SIG_DFL, SIG_IGN, SIGINT, SIGQUIT,
 
42
 
                                   SIGHUP, SIGSTOP, SIG_UNBLOCK */
 
43
 
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE,
 
44
 
                                   malloc(), free(), strtoumax(),
 
45
 
                                   realloc(), setenv(), calloc(),
 
46
 
                                   mkdtemp(), mkostemp() */
 
47
 
#include <iso646.h>             /* not, or, and, xor */
 
48
 
#include <error.h>              /* error() */
 
49
 
#include <sysexits.h>           /* EX_USAGE, EX_OSERR, EX_OSFILE */
 
50
 
#include <errno.h>              /* errno, error_t, EACCES,
 
51
 
                                   ENAMETOOLONG, ENOENT, ENOTDIR,
 
52
 
                                   ENOMEM, EEXIST, ECHILD, EPERM,
 
53
 
                                   EAGAIN, EINTR, ENOBUFS, EADDRINUSE,
 
54
 
                                   ECONNREFUSED, ECONNRESET,
 
55
 
                                   ETOOMANYREFS, EMSGSIZE, EBADF,
 
57
 
#include <string.h>             /* strdup(), memcpy(),
 
58
 
                                   explicit_bzero(), memset(),
 
59
 
                                   strcmp(), strlen(), strncpy(),
 
60
 
                                   memcmp(), basename() */
 
61
 
#include <argz.h>               /* argz_create(), argz_count(),
 
62
 
                                   argz_extract(), argz_next(),
 
64
 
#include <sys/epoll.h>          /* epoll_create1(), EPOLL_CLOEXEC,
 
65
 
                                   epoll_ctl(), EPOLL_CTL_ADD,
 
66
 
                                   struct epoll_event, EPOLLIN,
 
69
 
#include <time.h>               /* struct timespec, clock_gettime(),
 
71
 
#include <argp.h>               /* struct argp_option, OPTION_HIDDEN,
 
72
 
                                   OPTION_ALIAS, struct argp_state,
 
73
 
                                   ARGP_ERR_UNKNOWN, ARGP_KEY_ARGS,
 
74
 
                                   struct argp, argp_parse(),
 
76
 
#include <stdint.h>             /* SIZE_MAX */
 
77
 
#include <unistd.h>             /* uid_t, gid_t, close(), pipe2(),
 
78
 
                                   fork(), _exit(), dup2(),
 
79
 
                                   STDOUT_FILENO, setresgid(),
 
80
 
                                   setresuid(), execv(), ssize_t,
 
81
 
                                   read(), dup3(), getuid(), dup(),
 
82
 
                                   STDERR_FILENO, pause(), write(),
 
83
 
                                   rmdir(), unlink(), getpid() */
 
84
 
#include <sys/mman.h>           /* munlock(), mlock() */
 
85
 
#include <fcntl.h>              /* O_CLOEXEC, O_NONBLOCK, fcntl(),
 
86
 
                                   F_GETFD, F_GETFL, FD_CLOEXEC,
 
87
 
                                   open(), O_WRONLY, O_NOCTTY,
 
88
 
                                   O_RDONLY, O_NOFOLLOW */
 
89
 
#include <sys/wait.h>           /* waitpid(), WNOHANG, WIFEXITED(),
 
91
 
#include <limits.h>             /* PIPE_BUF, NAME_MAX, INT_MAX */
 
92
 
#include <sys/inotify.h>        /* inotify_init1(), IN_NONBLOCK,
 
93
 
                                   IN_CLOEXEC, inotify_add_watch(),
 
94
 
                                   IN_CLOSE_WRITE, IN_MOVED_TO,
 
95
 
                                   IN_MOVED_FROM, IN_DELETE,
 
96
 
                                   IN_EXCL_UNLINK, IN_ONLYDIR,
 
97
 
                                   struct inotify_event */
 
98
 
#include <fnmatch.h>            /* fnmatch(), FNM_FILE_NAME */
 
99
 
#include <stdio.h>              /* asprintf(), FILE, stderr, fopen(),
 
100
 
                                   fclose(), getline(), sscanf(),
 
101
 
                                   feof(), ferror(), rename(),
 
102
 
                                   fdopen(), fprintf(), fscanf() */
 
103
 
#include <glib.h>    /* GKeyFile, g_key_file_free(), g_key_file_new(),
 
104
 
                        GError, g_key_file_load_from_file(),
 
105
 
                        G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
 
106
 
                        g_key_file_get_string(), guint64,
 
107
 
                        g_key_file_get_uint64(),
 
108
 
                        G_KEY_FILE_ERROR_KEY_NOT_FOUND, gconstpointer,
 
109
 
                        g_assert_true(), g_assert_nonnull(),
 
110
 
                        g_assert_null(), g_assert_false(),
 
111
 
                        g_assert_cmpint(), g_assert_cmpuint(),
 
112
 
                        g_test_skip(), g_assert_cmpstr(),
 
113
 
                        g_test_init(), g_test_add(), g_test_run(),
 
114
 
                        GOptionContext, g_option_context_new(),
 
115
 
                        g_option_context_set_help_enabled(), FALSE,
 
116
 
                        g_option_context_set_ignore_unknown_options(),
 
117
 
                        gboolean, GOptionEntry, G_OPTION_ARG_NONE,
 
118
 
                        g_option_context_add_main_entries(),
 
119
 
                        g_option_context_parse(),
 
120
 
                        g_option_context_free(), g_error() */
 
121
 
#include <sys/un.h>             /* struct sockaddr_un, SUN_LEN */
 
122
 
#include <sys/socket.h>         /* AF_LOCAL, socket(), PF_LOCAL,
 
123
 
                                   SOCK_DGRAM, SOCK_NONBLOCK,
 
124
 
                                   SOCK_CLOEXEC, connect(),
 
125
 
                                   struct sockaddr, socklen_t,
 
126
 
                                   shutdown(), SHUT_RD, send(),
 
127
 
                                   MSG_NOSIGNAL, bind(), recv(),
 
129
 
#include <glob.h>               /* globfree(), glob_t, glob(),
 
130
 
                                   GLOB_ERR, GLOB_NOSORT, GLOB_MARK,
 
131
 
                                   GLOB_ABORTED, GLOB_NOMATCH,
 
134
 
/* End of includes */
 
136
 
/* Start of declarations of private types and functions */
 
138
 
/* microseconds of CLOCK_MONOTONIC absolute time; 0 means unset */
 
139
 
typedef uintmax_t mono_microsecs;
 
141
 
/* "task_queue" - A queue of tasks to be run */
 
143
 
  struct task_struct *tasks;    /* Tasks in this queue */
 
144
 
  size_t length;                /* Number of tasks */
 
145
 
  /* Memory allocated for "tasks", in bytes */
 
147
 
  /* Time when this queue should be run, at the latest */
 
148
 
  mono_microsecs next_run;
 
149
 
} __attribute__((designated_init)) task_queue;
 
151
 
/* "task_func" - A function type for task functions
 
153
 
   I.e. functions for the code which runs when a task is run, all have
 
155
 
typedef void (task_func) (const struct task_struct,
 
157
 
  __attribute__((nonnull));
 
159
 
/* "buffer" - A data buffer for a growing array of bytes
 
161
 
   Used for the "password" variable */
 
166
 
} __attribute__((designated_init)) buffer;
 
168
 
/* "string_set" - A set type which can contain strings
 
170
 
   Used by the "cancelled_filenames" variable */
 
172
 
  char *argz;                   /* Do not access these except in */
 
173
 
  size_t argz_len;              /* the string_set_* functions */
 
174
 
} __attribute__((designated_init)) string_set;
 
176
 
/* "task_context" - local variables for tasks
 
178
 
   This data structure distinguishes between different tasks which are
 
179
 
   using the same function.  This data structure is passed to every
 
180
 
   task function when each task is run.
 
182
 
   Note that not every task uses every struct member. */
 
183
 
typedef struct task_struct {
 
184
 
  task_func *const func;         /* The function run by this task */
 
185
 
  char *const question_filename; /* The question file */
 
186
 
  const pid_t pid;               /* Mandos client process ID */
 
187
 
  const int epoll_fd;            /* The epoll set file descriptor */
 
188
 
  bool *const quit_now;          /* Set to true on fatal errors */
 
189
 
  const int fd;                  /* General purpose file descriptor */
 
190
 
  bool *const mandos_client_exited; /* Set true when client exits */
 
191
 
  buffer *const password;           /* As read from client process */
 
192
 
  bool *const password_is_read;     /* "password" is done growing */
 
193
 
  char *filename;                   /* General purpose file name */
 
194
 
  /* A set of strings of all the file names of questions which have
 
195
 
     been cancelled for any reason; tasks pertaining to these question
 
196
 
     files should not be run */
 
197
 
  string_set *const cancelled_filenames;
 
198
 
  const mono_microsecs notafter; /* "NotAfter" from question file */
 
199
 
  /* Updated before each queue run; is compared with queue.next_run */
 
200
 
  const mono_microsecs *const current_time;
 
201
 
} __attribute__((designated_init)) task_context;
 
203
 
/* Declare all our functions here so we can define them in any order
 
204
 
   below.  Note: test functions are *not* declared here, they are
 
205
 
   declared in the test section. */
 
206
 
__attribute__((warn_unused_result))
 
207
 
static bool should_only_run_tests(int *, char **[]);
 
208
 
__attribute__((warn_unused_result, cold))
 
209
 
static bool run_tests(int, char *[]);
 
210
 
static void handle_sigchld(__attribute__((unused)) int sig){}
 
211
 
__attribute__((warn_unused_result, malloc))
 
212
 
task_queue *create_queue(void);
 
213
 
__attribute__((nonnull, warn_unused_result))
 
214
 
bool add_to_queue(task_queue *const, const task_context);
 
215
 
__attribute__((nonnull))
 
216
 
void cleanup_task(const task_context *const);
 
217
 
__attribute__((nonnull))
 
218
 
void cleanup_queue(task_queue *const *const);
 
219
 
__attribute__((pure, nonnull, warn_unused_result))
 
220
 
bool queue_has_question(const task_queue *const);
 
221
 
__attribute__((nonnull))
 
222
 
void cleanup_close(const int *const);
 
223
 
__attribute__((nonnull))
 
224
 
void cleanup_string(char *const *const);
 
225
 
__attribute__((nonnull))
 
226
 
void cleanup_buffer(buffer *const);
 
227
 
__attribute__((pure, nonnull, warn_unused_result))
 
228
 
bool string_set_contains(const string_set, const char *const);
 
229
 
__attribute__((nonnull, warn_unused_result))
 
230
 
bool string_set_add(string_set *const, const char *const);
 
231
 
__attribute__((nonnull))
 
232
 
void string_set_clear(string_set *);
 
233
 
void string_set_swap(string_set *const, string_set *const);
 
234
 
__attribute__((nonnull, warn_unused_result))
 
235
 
bool start_mandos_client(task_queue *const, const int, bool *const,
 
236
 
                         bool *const, buffer *const, bool *const,
 
237
 
                         const struct sigaction *const,
 
238
 
                         const sigset_t, const char *const,
 
239
 
                         const uid_t, const gid_t,
 
240
 
                         const char *const *const);
 
241
 
__attribute__((nonnull))
 
242
 
task_func wait_for_mandos_client_exit;
 
243
 
__attribute__((nonnull))
 
244
 
task_func read_mandos_client_output;
 
245
 
__attribute__((warn_unused_result))
 
246
 
bool add_inotify_dir_watch(task_queue *const, const int, bool *const,
 
247
 
                           buffer *const, const char *const,
 
248
 
                           string_set *, const mono_microsecs *const,
 
249
 
                           bool *const, bool *const);
 
250
 
__attribute__((nonnull))
 
251
 
task_func read_inotify_event;
 
252
 
__attribute__((nonnull))
 
253
 
task_func open_and_parse_question;
 
254
 
__attribute__((nonnull))
 
255
 
task_func cancel_old_question;
 
256
 
__attribute__((nonnull))
 
257
 
task_func connect_question_socket;
 
258
 
__attribute__((nonnull))
 
259
 
task_func send_password_to_socket;
 
260
 
__attribute__((warn_unused_result))
 
261
 
bool add_existing_questions(task_queue *const, const int,
 
262
 
                            buffer *const, string_set *,
 
263
 
                            const mono_microsecs *const,
 
264
 
                            bool *const, bool *const,
 
266
 
__attribute__((nonnull, warn_unused_result))
 
267
 
bool wait_for_event(const int, const mono_microsecs,
 
268
 
                    const mono_microsecs);
 
269
 
bool run_queue(task_queue **const, string_set *const, bool *const);
 
270
 
bool clear_all_fds_from_epoll_set(const int);
 
271
 
mono_microsecs get_current_time(void);
 
272
 
__attribute__((nonnull, warn_unused_result))
 
273
 
bool setup_signal_handler(struct sigaction *const);
 
274
 
__attribute__((nonnull))
 
275
 
bool restore_signal_handler(const struct sigaction *const);
 
276
 
__attribute__((nonnull, warn_unused_result))
 
277
 
bool block_sigchld(sigset_t *const);
 
278
 
__attribute__((nonnull))
 
279
 
bool restore_sigmask(const sigset_t *const);
 
280
 
__attribute__((nonnull))
 
281
 
bool parse_arguments(int, char *[], const bool, char **, char **,
 
282
 
                     uid_t *const , gid_t *const, char **, size_t *);
 
284
 
/* End of declarations of private types and functions */
 
286
 
/* Start of "main" section; this section LACKS TESTS!
 
288
 
   Code here should be as simple as possible. */
 
290
 
/* These are required to be global by Argp */
 
291
 
const char *argp_program_version = "password-agent " VERSION;
 
292
 
const char *argp_program_bug_address = "<mandos@recompile.se>";
 
294
 
int main(int argc, char *argv[]){
 
296
 
  /* If the --test option is passed, skip all normal operations and
 
297
 
     instead only run the run_tests() function, which also does all
 
298
 
     its own option parsing, so we don't have to do anything here. */
 
299
 
  if(should_only_run_tests(&argc, &argv)){
 
300
 
    if(run_tests(argc, argv)){
 
301
 
      return EXIT_SUCCESS;      /* All tests successful */
 
303
 
    return EXIT_FAILURE;        /* Some test(s) failed */
 
306
 
  __attribute__((cleanup(cleanup_string)))
 
307
 
    char *agent_directory = NULL;
 
309
 
  __attribute__((cleanup(cleanup_string)))
 
310
 
    char *helper_directory = NULL;
 
315
 
  __attribute__((cleanup(cleanup_string)))
 
316
 
    char *mandos_argz = NULL;
 
317
 
  size_t mandos_argz_length = 0;
 
319
 
  if(not parse_arguments(argc, argv, true, &agent_directory,
 
320
 
                         &helper_directory, &user, &group,
 
321
 
                         &mandos_argz, &mandos_argz_length)){
 
322
 
    /* This should never happen, since "true" is passed as the third
 
323
 
       argument to parse_arguments() above, which should make
 
324
 
       argp_parse() call exit() if any parsing error occurs. */
 
325
 
    error(EX_USAGE, errno, "Failed to parse arguments");
 
328
 
  const char default_agent_directory[] = "/run/systemd/ask-password";
 
329
 
  const char default_helper_directory[]
 
330
 
    = "/lib/mandos/plugin-helpers";
 
331
 
  const char *const default_argv[]
 
332
 
    = {"/lib/mandos/plugins.d/mandos-client", NULL };
 
334
 
  /* Set variables to default values if unset */
 
335
 
  if(agent_directory == NULL){
 
336
 
    agent_directory = strdup(default_agent_directory);
 
337
 
    if(agent_directory == NULL){
 
338
 
      error(EX_OSERR, errno, "Failed strdup()");
 
341
 
  if(helper_directory == NULL){
 
342
 
    helper_directory = strdup(default_helper_directory);
 
343
 
    if(helper_directory == NULL){
 
344
 
      error(EX_OSERR, errno, "Failed strdup()");
 
348
 
    user = 65534;               /* nobody */
 
351
 
    group = 65534;              /* nogroup */
 
353
 
  /* If parse_opt did not create an argz vector, create one with
 
355
 
  if(mandos_argz == NULL){
 
357
 
#pragma GCC diagnostic push
 
358
 
    /* argz_create() takes a non-const argv for some unknown reason -
 
359
 
       argz_create() isn't modifying the strings, just copying them.
 
360
 
       Therefore, this cast to non-const should be safe. */
 
361
 
#pragma GCC diagnostic ignored "-Wcast-qual"
 
363
 
    errno = argz_create((char *const *)default_argv, &mandos_argz,
 
364
 
                        &mandos_argz_length);
 
366
 
#pragma GCC diagnostic pop
 
369
 
      error(EX_OSERR, errno, "Failed argz_create()");
 
372
 
  /* Use argz vector to create a normal argv, usable by execv() */
 
374
 
  char **mandos_argv = malloc((argz_count(mandos_argz,
 
376
 
                               + 1) * sizeof(char *));
 
377
 
  if(mandos_argv == NULL){
 
378
 
    error_t saved_errno = errno;
 
380
 
    error(EX_OSERR, saved_errno, "Failed malloc()");
 
382
 
  argz_extract(mandos_argz, mandos_argz_length, mandos_argv);
 
384
 
  sigset_t orig_sigmask;
 
385
 
  if(not block_sigchld(&orig_sigmask)){
 
389
 
  struct sigaction old_sigchld_action;
 
390
 
  if(not setup_signal_handler(&old_sigchld_action)){
 
394
 
  mono_microsecs current_time = 0;
 
396
 
  bool mandos_client_exited = false;
 
397
 
  bool quit_now = false;
 
398
 
  __attribute__((cleanup(cleanup_close)))
 
399
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
401
 
    error(EX_OSERR, errno, "Failed to create epoll set fd");
 
403
 
  __attribute__((cleanup(cleanup_queue)))
 
404
 
    task_queue *queue = create_queue();
 
406
 
    error(EX_OSERR, errno, "Failed to create task queue");
 
409
 
  __attribute__((cleanup(cleanup_buffer)))
 
410
 
    buffer password = {};
 
411
 
  bool password_is_read = false;
 
413
 
  __attribute__((cleanup(string_set_clear)))
 
414
 
    string_set cancelled_filenames = {};
 
416
 
  /* Add tasks to queue */
 
417
 
  if(not start_mandos_client(queue, epoll_fd, &mandos_client_exited,
 
418
 
                             &quit_now, &password, &password_is_read,
 
419
 
                             &old_sigchld_action, orig_sigmask,
 
420
 
                             helper_directory, user, group,
 
421
 
                             (const char *const *)mandos_argv)){
 
422
 
    return EX_OSERR;            /* Error has already been printed */
 
424
 
  /* These variables were only for start_mandos_client() and are not
 
429
 
  if(not add_inotify_dir_watch(queue, epoll_fd, &quit_now, &password,
 
430
 
                               agent_directory, &cancelled_filenames,
 
431
 
                               ¤t_time, &mandos_client_exited,
 
433
 
    switch(errno){              /* Error has already been printed */
 
443
 
  if(not add_existing_questions(queue, epoll_fd, &password,
 
444
 
                                &cancelled_filenames, ¤t_time,
 
445
 
                                &mandos_client_exited,
 
446
 
                                &password_is_read, agent_directory)){
 
447
 
    return EXIT_FAILURE;        /* Error has already been printed */
 
452
 
    current_time = get_current_time();
 
453
 
    if(not wait_for_event(epoll_fd, queue->next_run, current_time)){
 
454
 
      const error_t saved_errno = errno;
 
455
 
      error(EXIT_FAILURE, saved_errno, "Failure while waiting for"
 
459
 
    current_time = get_current_time();
 
460
 
    if(not run_queue(&queue, &cancelled_filenames, &quit_now)){
 
461
 
      const error_t saved_errno = errno;
 
462
 
      error(EXIT_FAILURE, saved_errno, "Failure while running queue");
 
465
 
    /*  When no tasks about questions are left in the queue, break out
 
466
 
        of the loop (and implicitly exit the program) */
 
467
 
  } while(queue_has_question(queue));
 
469
 
  restore_signal_handler(&old_sigchld_action);
 
470
 
  restore_sigmask(&orig_sigmask);
 
475
 
__attribute__((warn_unused_result))
 
476
 
mono_microsecs get_current_time(void){
 
477
 
  struct timespec currtime;
 
478
 
  if(clock_gettime(CLOCK_MONOTONIC, &currtime) != 0){
 
479
 
    error(0, errno, "Failed to get current time");
 
482
 
  return ((mono_microsecs)currtime.tv_sec * 1000000) /* seconds */
 
483
 
    + ((mono_microsecs)currtime.tv_nsec / 1000);     /* nanoseconds */
 
486
 
/* End of "main" section */
 
488
 
/* Start of regular code section; ALL this code has tests */
 
490
 
__attribute__((nonnull))
 
491
 
bool parse_arguments(int argc, char *argv[], const bool exit_failure,
 
492
 
                     char **agent_directory, char **helper_directory,
 
493
 
                     uid_t *const user, gid_t *const group,
 
494
 
                     char **mandos_argz, size_t *mandos_argz_length){
 
496
 
  const struct argp_option options[] = {
 
497
 
    { .name="agent-directory",.key='d', .arg="DIRECTORY",
 
498
 
      .doc="Systemd password agent directory" },
 
499
 
    { .name="helper-directory",.key=128, .arg="DIRECTORY",
 
500
 
      .doc="Mandos Client password helper directory" },
 
501
 
    { .name="plugin-helper-dir", .key=129, /* From plugin-runner */
 
502
 
      .flags=OPTION_HIDDEN | OPTION_ALIAS },
 
503
 
    { .name="user", .key='u', .arg="USERID",
 
504
 
      .doc="User ID the Mandos Client will use as its unprivileged"
 
506
 
    { .name="userid", .key=130, /* From plugin--runner */
 
507
 
      .flags=OPTION_HIDDEN | OPTION_ALIAS },
 
508
 
    { .name="group", .key='g', .arg="GROUPID",
 
509
 
      .doc="Group ID the Mandos Client will use as its unprivileged"
 
511
 
    { .name="groupid", .key=131, /* From plugin--runner */
 
512
 
      .flags=OPTION_HIDDEN | OPTION_ALIAS },
 
513
 
    { .name="test", .key=255, /* See should_only_run_tests() */
 
514
 
      .doc="Skip normal operation, and only run self-tests.  See"
 
515
 
      " --test --help.", .group=10, },
 
519
 
  __attribute__((nonnull(3)))
 
520
 
    error_t parse_opt(int key, char *arg, struct argp_state *state){
 
523
 
    case 'd':                   /* --agent-directory */
 
524
 
      *agent_directory = strdup(arg);
 
526
 
    case 128:                   /* --helper-directory */
 
527
 
    case 129:                   /* --plugin-helper-dir */
 
528
 
      *helper_directory = strdup(arg);
 
530
 
    case 'u':                   /* --user */
 
531
 
    case 130:                   /* --userid */
 
534
 
        uintmax_t tmp_id = 0;
 
536
 
        tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
 
537
 
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
538
 
           or tmp_id != (uid_t)tmp_id or (uid_t)tmp_id == 0){
 
539
 
          return ARGP_ERR_UNKNOWN;
 
541
 
        *user = (uid_t)tmp_id;
 
545
 
    case 'g':                   /* --group */
 
546
 
    case 131:                   /* --groupid */
 
549
 
        uintmax_t tmp_id = 0;
 
551
 
        tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
 
552
 
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
553
 
           or tmp_id != (gid_t)tmp_id or (gid_t)tmp_id == 0){
 
554
 
          return ARGP_ERR_UNKNOWN;
 
556
 
        *group = (gid_t)tmp_id;
 
561
 
      /* Copy arguments into argz vector */
 
562
 
      return argz_create(state->argv + state->next, mandos_argz,
 
565
 
      return ARGP_ERR_UNKNOWN;
 
570
 
  const struct argp argp = {
 
573
 
    .args_doc="[MANDOS_CLIENT [OPTION...]]\n--test",
 
574
 
    .doc = "Mandos password agent -- runs Mandos client as a"
 
575
 
    " systemd password agent",
 
578
 
  errno = argp_parse(&argp, argc, argv,
 
579
 
                     exit_failure ? 0 : ARGP_NO_EXIT, NULL, NULL);
 
584
 
__attribute__((nonnull, warn_unused_result))
 
585
 
bool block_sigchld(sigset_t *const orig_sigmask){
 
586
 
  sigset_t sigchld_sigmask;
 
587
 
  if(sigemptyset(&sigchld_sigmask) < 0){
 
588
 
    error(0, errno, "Failed to empty signal set");
 
591
 
  if(sigaddset(&sigchld_sigmask, SIGCHLD) < 0){
 
592
 
    error(0, errno, "Failed to add SIGCHLD to signal set");
 
595
 
  if(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask, orig_sigmask) != 0){
 
596
 
    error(0, errno, "Failed to block SIGCHLD signal");
 
602
 
__attribute__((nonnull, warn_unused_result, const))
 
603
 
bool restore_sigmask(const sigset_t *const orig_sigmask){
 
604
 
  if(pthread_sigmask(SIG_SETMASK, orig_sigmask, NULL) != 0){
 
605
 
    error(0, errno, "Failed to restore blocked signals");
 
611
 
__attribute__((nonnull, warn_unused_result))
 
612
 
bool setup_signal_handler(struct sigaction *const old_sigchld_action){
 
613
 
  struct sigaction sigchld_action = {
 
614
 
    .sa_handler=handle_sigchld,
 
615
 
    .sa_flags=SA_RESTART | SA_NOCLDSTOP,
 
617
 
  /* Set all signals in "sa_mask" struct member; this makes all
 
618
 
     signals automatically blocked during signal handler */
 
619
 
  if(sigfillset(&sigchld_action.sa_mask) != 0){
 
620
 
    error(0, errno, "Failed to do sigfillset()");
 
623
 
  if(sigaction(SIGCHLD, &sigchld_action, old_sigchld_action) != 0){
 
624
 
    error(0, errno, "Failed to set SIGCHLD signal handler");
 
630
 
__attribute__((nonnull, warn_unused_result))
 
631
 
bool restore_signal_handler(const struct sigaction *const
 
633
 
  if(sigaction(SIGCHLD, old_sigchld_action, NULL) != 0){
 
634
 
    error(0, errno, "Failed to restore signal handler");
 
640
 
__attribute__((warn_unused_result, malloc))
 
641
 
task_queue *create_queue(void){
 
642
 
  task_queue *queue = malloc(sizeof(task_queue));
 
646
 
    queue->allocated = 0;
 
652
 
__attribute__((nonnull, warn_unused_result))
 
653
 
bool add_to_queue(task_queue *const queue, const task_context task){
 
654
 
  if((queue->length + 1) > (SIZE_MAX / sizeof(task_context))){
 
656
 
    error(0, ENOMEM, "Failed to allocate %" PRIuMAX
 
657
 
          " tasks for queue->tasks", (uintmax_t)(queue->length + 1));
 
661
 
  const size_t needed_size = sizeof(task_context)*(queue->length + 1);
 
662
 
  if(needed_size > (queue->allocated)){
 
663
 
    task_context *const new_tasks = realloc(queue->tasks,
 
665
 
    if(new_tasks == NULL){
 
666
 
      error(0, errno, "Failed to allocate %" PRIuMAX
 
667
 
            " bytes for queue->tasks", (uintmax_t)needed_size);
 
670
 
    queue->tasks = new_tasks;
 
671
 
    queue->allocated = needed_size;
 
673
 
  /* Using memcpy here is necessary because doing */
 
674
 
  /* queue->tasks[queue->length++] = task; */
 
675
 
  /* would violate const-ness of task members */
 
676
 
  memcpy(&(queue->tasks[queue->length++]), &task,
 
677
 
         sizeof(task_context));
 
681
 
__attribute__((nonnull))
 
682
 
void cleanup_task(const task_context *const task){
 
683
 
  const error_t saved_errno = errno;
 
684
 
  /* free and close all task data */
 
685
 
  free(task->question_filename);
 
686
 
  if(task->filename != task->question_filename){
 
687
 
    free(task->filename);
 
690
 
    kill(task->pid, SIGTERM);
 
698
 
__attribute__((nonnull))
 
699
 
void free_queue(task_queue *const queue){
 
704
 
__attribute__((nonnull))
 
705
 
void cleanup_queue(task_queue *const *const queue){
 
709
 
  for(size_t i = 0; i < (*queue)->length; i++){
 
710
 
    const task_context *const task = ((*queue)->tasks)+i;
 
716
 
__attribute__((pure, nonnull, warn_unused_result))
 
717
 
bool queue_has_question(const task_queue *const queue){
 
718
 
  for(size_t i=0; i < queue->length; i++){
 
719
 
    if(queue->tasks[i].question_filename != NULL){
 
726
 
__attribute__((nonnull))
 
727
 
void cleanup_close(const int *const fd){
 
728
 
  const error_t saved_errno = errno;
 
733
 
__attribute__((nonnull))
 
734
 
void cleanup_string(char *const *const ptr){
 
738
 
__attribute__((nonnull))
 
739
 
void cleanup_buffer(buffer *buf){
 
740
 
  if(buf->allocated > 0){
 
741
 
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
 
742
 
    explicit_bzero(buf->data, buf->allocated);
 
744
 
    memset(buf->data, '\0', buf->allocated);
 
747
 
  if(buf->data != NULL){
 
748
 
    if(munlock(buf->data, buf->allocated) != 0){
 
749
 
      error(0, errno, "Failed to unlock memory of old buffer");
 
758
 
__attribute__((pure, nonnull, warn_unused_result))
 
759
 
bool string_set_contains(const string_set set, const char *const str){
 
760
 
  for(const char *s = set.argz; s != NULL and set.argz_len > 0;
 
761
 
      s = argz_next(set.argz, set.argz_len, s)){
 
762
 
    if(strcmp(s, str) == 0){
 
769
 
__attribute__((nonnull, warn_unused_result))
 
770
 
bool string_set_add(string_set *const set, const char *const str){
 
771
 
  if(string_set_contains(*set, str)){
 
774
 
  error_t error = argz_add(&set->argz, &set->argz_len, str);
 
782
 
__attribute__((nonnull))
 
783
 
void string_set_clear(string_set *set){
 
789
 
__attribute__((nonnull))
 
790
 
void string_set_swap(string_set *const set1, string_set *const set2){
 
791
 
  /* Swap contents of two string sets */
 
793
 
    char *const tmp_argz = set1->argz;
 
794
 
    set1->argz = set2->argz;
 
795
 
    set2->argz = tmp_argz;
 
798
 
    const size_t tmp_argz_len = set1->argz_len;
 
799
 
    set1->argz_len = set2->argz_len;
 
800
 
    set2->argz_len = tmp_argz_len;
 
804
 
__attribute__((nonnull, warn_unused_result))
 
805
 
bool start_mandos_client(task_queue *const queue,
 
807
 
                         bool *const mandos_client_exited,
 
808
 
                         bool *const quit_now, buffer *const password,
 
809
 
                         bool *const password_is_read,
 
810
 
                         const struct sigaction *const
 
811
 
                         old_sigchld_action, const sigset_t sigmask,
 
812
 
                         const char *const helper_directory,
 
813
 
                         const uid_t user, const gid_t group,
 
814
 
                         const char *const *const argv){
 
816
 
  if(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK) != 0){
 
817
 
    error(0, errno, "Failed to pipe2(..., O_CLOEXEC | O_NONBLOCK)");
 
821
 
  const pid_t pid = fork();
 
823
 
    if(not restore_signal_handler(old_sigchld_action)){
 
826
 
    if(not restore_sigmask(&sigmask)){
 
829
 
    if(close(pipefds[0]) != 0){
 
830
 
      error(0, errno, "Failed to close() parent pipe fd");
 
833
 
    if(dup2(pipefds[1], STDOUT_FILENO) == -1){
 
834
 
      error(0, errno, "Failed to dup2() pipe fd to stdout");
 
837
 
    if(close(pipefds[1]) != 0){
 
838
 
      error(0, errno, "Failed to close() old child pipe fd");
 
841
 
    if(setenv("MANDOSPLUGINHELPERDIR", helper_directory, 1) != 0){
 
842
 
      error(0, errno, "Failed to setenv(\"MANDOSPLUGINHELPERDIR\","
 
843
 
            " \"%s\", 1)", helper_directory);
 
846
 
    if(group != 0 and setresgid(group, 0, 0) == -1){
 
847
 
      error(0, errno, "Failed to setresgid(-1, %" PRIuMAX ", %"
 
848
 
            PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
 
851
 
    if(user != 0 and setresuid(user, 0, 0) == -1){
 
852
 
      error(0, errno, "Failed to setresuid(-1, %" PRIuMAX ", %"
 
853
 
            PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
 
857
 
#pragma GCC diagnostic push
 
858
 
    /* For historical reasons, the "argv" argument to execv() is not
 
859
 
       const, but it is safe to override this. */
 
860
 
#pragma GCC diagnostic ignored "-Wcast-qual"
 
862
 
    execv(argv[0], (char **)argv);
 
864
 
#pragma GCC diagnostic pop
 
866
 
    error(0, errno, "execv(\"%s\", ...) failed", argv[0]);
 
872
 
    error(0, errno, "Failed to fork()");
 
877
 
  if(not add_to_queue(queue, (task_context){
 
878
 
        .func=wait_for_mandos_client_exit,
 
880
 
        .mandos_client_exited=mandos_client_exited,
 
883
 
    error(0, errno, "Failed to add wait_for_mandos_client to queue");
 
888
 
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipefds[0],
 
889
 
                            &(struct epoll_event)
 
890
 
                            { .events=EPOLLIN | EPOLLRDHUP });
 
891
 
  if(ret != 0 and errno != EEXIST){
 
892
 
    error(0, errno, "Failed to add file descriptor to epoll set");
 
897
 
  return add_to_queue(queue, (task_context){
 
898
 
      .func=read_mandos_client_output,
 
903
 
      .password_is_read=password_is_read,
 
907
 
__attribute__((nonnull))
 
908
 
void wait_for_mandos_client_exit(const task_context task,
 
909
 
                                 task_queue *const queue){
 
910
 
  const pid_t pid = task.pid;
 
911
 
  bool *const mandos_client_exited = task.mandos_client_exited;
 
912
 
  bool *const quit_now = task.quit_now;
 
915
 
  switch(waitpid(pid, &status, WNOHANG)){
 
916
 
  case 0:                       /* Not exited yet */
 
917
 
    if(not add_to_queue(queue, task)){
 
918
 
      error(0, errno, "Failed to add myself to queue");
 
923
 
    error(0, errno, "waitpid(%" PRIdMAX ") failed", (intmax_t)pid);
 
929
 
  default:                      /* Has exited */
 
930
 
    *mandos_client_exited = true;
 
931
 
    if((not WIFEXITED(status))
 
932
 
       or (WEXITSTATUS(status) != EXIT_SUCCESS)){
 
933
 
      error(0, 0, "Mandos client failed or was killed");
 
939
 
__attribute__((nonnull))
 
940
 
void read_mandos_client_output(const task_context task,
 
941
 
                               task_queue *const queue){
 
942
 
  buffer *const password = task.password;
 
943
 
  bool *const quit_now = task.quit_now;
 
944
 
  bool *const password_is_read = task.password_is_read;
 
945
 
  const int fd = task.fd;
 
946
 
  const int epoll_fd = task.epoll_fd;
 
948
 
  const size_t new_potential_size = (password->length + PIPE_BUF);
 
949
 
  if(password->allocated < new_potential_size){
 
950
 
    char *const new_buffer = calloc(new_potential_size, 1);
 
951
 
    if(new_buffer == NULL){
 
952
 
      error(0, errno, "Failed to allocate %" PRIuMAX
 
953
 
            " bytes for password", (uintmax_t)new_potential_size);
 
958
 
    if(mlock(new_buffer, new_potential_size) != 0){
 
959
 
      /* Warn but do not treat as fatal error */
 
960
 
      if(errno != EPERM and errno != ENOMEM){
 
961
 
        error(0, errno, "Failed to lock memory for password");
 
964
 
    if(password->length > 0){
 
965
 
      memcpy(new_buffer, password->data, password->length);
 
966
 
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
 
967
 
      explicit_bzero(password->data, password->allocated);
 
969
 
      memset(password->data, '\0', password->allocated);
 
972
 
    if(password->data != NULL){
 
973
 
      if(munlock(password->data, password->allocated) != 0){
 
974
 
        error(0, errno, "Failed to unlock memory of old buffer");
 
976
 
      free(password->data);
 
978
 
    password->data = new_buffer;
 
979
 
    password->allocated = new_potential_size;
 
982
 
  const ssize_t read_length = read(fd, password->data
 
983
 
                                   + password->length, PIPE_BUF);
 
985
 
  if(read_length == 0){ /* EOF */
 
986
 
    *password_is_read = true;
 
990
 
  if(read_length < 0 and errno != EAGAIN){ /* Actual error */
 
991
 
    error(0, errno, "Failed to read password from Mandos client");
 
996
 
  if(read_length > 0){          /* Data has been read */
 
997
 
    password->length += (size_t)read_length;
 
1000
 
  /* Either data was read, or EAGAIN was indicated, meaning no data
 
1003
 
  /* Re-add the fd to the epoll set */
 
1004
 
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
1005
 
                            &(struct epoll_event)
 
1006
 
                            { .events=EPOLLIN | EPOLLRDHUP });
 
1007
 
  if(ret != 0 and errno != EEXIST){
 
1008
 
    error(0, errno, "Failed to re-add file descriptor to epoll set");
 
1014
 
  /* Re-add myself to the queue */
 
1015
 
  if(not add_to_queue(queue, task)){
 
1016
 
    error(0, errno, "Failed to add myself to queue");
 
1022
 
__attribute__((nonnull, warn_unused_result))
 
1023
 
bool add_inotify_dir_watch(task_queue *const queue,
 
1024
 
                           const int epoll_fd, bool *const quit_now,
 
1025
 
                           buffer *const password,
 
1026
 
                           const char *const dir,
 
1027
 
                           string_set *cancelled_filenames,
 
1028
 
                           const mono_microsecs *const current_time,
 
1029
 
                           bool *const mandos_client_exited,
 
1030
 
                           bool *const password_is_read){
 
1031
 
  const int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
 
1033
 
    error(0, errno, "Failed to create inotify instance");
 
1037
 
  if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO
 
1038
 
                       | IN_MOVED_FROM| IN_DELETE | IN_EXCL_UNLINK
 
1041
 
    error(0, errno, "Failed to create inotify watch on %s", dir);
 
1045
 
  /* Add the inotify fd to the epoll set */
 
1046
 
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
1047
 
                            &(struct epoll_event)
 
1048
 
                            { .events=EPOLLIN | EPOLLRDHUP });
 
1049
 
  if(ret != 0 and errno != EEXIST){
 
1050
 
    error(0, errno, "Failed to add file descriptor to epoll set");
 
1055
 
  const task_context read_inotify_event_task = {
 
1056
 
    .func=read_inotify_event,
 
1061
 
    .filename=strdup(dir),
 
1062
 
    .cancelled_filenames=cancelled_filenames,
 
1063
 
    .current_time=current_time,
 
1064
 
    .mandos_client_exited=mandos_client_exited,
 
1065
 
    .password_is_read=password_is_read,
 
1067
 
  if(read_inotify_event_task.filename == NULL){
 
1068
 
    error(0, errno, "Failed to strdup(\"%s\")", dir);
 
1073
 
  return add_to_queue(queue, read_inotify_event_task);
 
1076
 
__attribute__((nonnull))
 
1077
 
void read_inotify_event(const task_context task,
 
1078
 
                        task_queue *const queue){
 
1079
 
  const int fd = task.fd;
 
1080
 
  const int epoll_fd = task.epoll_fd;
 
1081
 
  char *const filename = task.filename;
 
1082
 
  bool *quit_now = task.quit_now;
 
1083
 
  buffer *const password = task.password;
 
1084
 
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
1085
 
  const mono_microsecs *const current_time = task.current_time;
 
1086
 
  bool *const mandos_client_exited = task.mandos_client_exited;
 
1087
 
  bool *const password_is_read = task.password_is_read;
 
1089
 
  /* "sufficient to read at least one event." - inotify(7) */
 
1090
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
1093
 
    struct inotify_event event;
 
1094
 
    char name_buffer[NAME_MAX + 1];
 
1096
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
1098
 
  const ssize_t read_length = read(fd, ievent, ievent_size);
 
1099
 
  if(read_length == 0){ /* EOF */
 
1100
 
    error(0, 0, "Got EOF from inotify fd for directory %s", filename);
 
1102
 
    cleanup_task(&task);
 
1105
 
  if(read_length < 0 and errno != EAGAIN){ /* Actual error */
 
1106
 
    error(0, errno, "Failed to read from inotify fd for directory %s",
 
1109
 
    cleanup_task(&task);
 
1112
 
  if(read_length > 0            /* Data has been read */
 
1113
 
     and fnmatch("ask.*", ievent->name, FNM_FILE_NAME) == 0){
 
1114
 
    char *question_filename = NULL;
 
1115
 
    const ssize_t question_filename_length
 
1116
 
      = asprintf(&question_filename, "%s/%s", filename, ievent->name);
 
1117
 
    if(question_filename_length < 0){
 
1118
 
      error(0, errno, "Failed to create file name from directory name"
 
1119
 
            " %s and file name %s", filename, ievent->name);
 
1121
 
      if(ievent->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)){
 
1122
 
        if(not add_to_queue(queue, (task_context){
 
1123
 
              .func=open_and_parse_question,
 
1125
 
              .question_filename=question_filename,
 
1126
 
              .filename=question_filename,
 
1128
 
              .cancelled_filenames=cancelled_filenames,
 
1129
 
              .current_time=current_time,
 
1130
 
              .mandos_client_exited=mandos_client_exited,
 
1131
 
              .password_is_read=password_is_read,
 
1133
 
          error(0, errno, "Failed to add open_and_parse_question task"
 
1134
 
                " for file name %s to queue", filename);
 
1136
 
          /* Force the added task (open_and_parse_question) to run
 
1138
 
          queue->next_run = 1;
 
1140
 
      } else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
 
1141
 
        if(not string_set_add(cancelled_filenames,
 
1142
 
                              question_filename)){
 
1143
 
          error(0, errno, "Could not add question %s to"
 
1144
 
                " cancelled_questions", question_filename);
 
1146
 
          free(question_filename);
 
1147
 
          cleanup_task(&task);
 
1150
 
        free(question_filename);
 
1155
 
  /* Either data was read, or EAGAIN was indicated, meaning no data
 
1158
 
  /* Re-add myself to the queue */
 
1159
 
  if(not add_to_queue(queue, task)){
 
1160
 
    error(0, errno, "Failed to re-add read_inotify_event(%s) to"
 
1161
 
          " queue", filename);
 
1163
 
    cleanup_task(&task);
 
1167
 
  /* Re-add the fd to the epoll set */
 
1168
 
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
1169
 
                            &(struct epoll_event)
 
1170
 
                            { .events=EPOLLIN | EPOLLRDHUP });
 
1171
 
  if(ret != 0 and errno != EEXIST){
 
1172
 
    error(0, errno, "Failed to re-add inotify file descriptor %d for"
 
1173
 
          " directory %s to epoll set", fd, filename);
 
1174
 
    /* Force the added task (read_inotify_event) to run again, at most
 
1175
 
       one second from now */
 
1176
 
    if((queue->next_run == 0)
 
1177
 
       or (queue->next_run > (*current_time + 1000000))){
 
1178
 
      queue->next_run = *current_time + 1000000;
 
1183
 
__attribute__((nonnull))
 
1184
 
void open_and_parse_question(const task_context task,
 
1185
 
                             task_queue *const queue){
 
1186
 
  __attribute__((cleanup(cleanup_string)))
 
1187
 
    char *question_filename = task.question_filename;
 
1188
 
  const int epoll_fd = task.epoll_fd;
 
1189
 
  buffer *const password = task.password;
 
1190
 
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
1191
 
  const mono_microsecs *const current_time = task.current_time;
 
1192
 
  bool *const mandos_client_exited = task.mandos_client_exited;
 
1193
 
  bool *const password_is_read = task.password_is_read;
 
1195
 
  /* We use the GLib "Key-value file parser" functions to parse the
 
1196
 
     question file.  See <https://www.freedesktop.org/wiki/Software
 
1197
 
     /systemd/PasswordAgents/> for specification of contents */
 
1198
 
  __attribute__((nonnull))
 
1199
 
    void cleanup_g_key_file(GKeyFile **key_file){
 
1200
 
    if(*key_file != NULL){
 
1201
 
      g_key_file_free(*key_file);
 
1205
 
  __attribute__((cleanup(cleanup_g_key_file)))
 
1206
 
    GKeyFile *key_file = g_key_file_new();
 
1207
 
  if(key_file == NULL){
 
1208
 
    error(0, errno, "Failed g_key_file_new() for \"%s\"",
 
1212
 
  GError *glib_error = NULL;
 
1213
 
  if(g_key_file_load_from_file(key_file, question_filename,
 
1214
 
                               G_KEY_FILE_NONE, &glib_error) != TRUE){
 
1215
 
    /* If a file was removed, we should ignore it, so */
 
1216
 
    /* only show error message if file actually existed */
 
1217
 
    if(glib_error->code != G_FILE_ERROR_NOENT){
 
1218
 
      error(0, 0, "Failed to load question data from file \"%s\": %s",
 
1219
 
            question_filename, glib_error->message);
 
1224
 
  __attribute__((cleanup(cleanup_string)))
 
1225
 
    char *socket_name = g_key_file_get_string(key_file, "Ask",
 
1228
 
  if(socket_name == NULL){
 
1229
 
    error(0, 0, "Question file \"%s\" did not contain \"Socket\": %s",
 
1230
 
          question_filename, glib_error->message);
 
1234
 
  if(strlen(socket_name) == 0){
 
1235
 
    error(0, 0, "Question file \"%s\" had empty \"Socket\" value",
 
1240
 
  const guint64 pid = g_key_file_get_uint64(key_file, "Ask", "PID",
 
1242
 
  if(glib_error != NULL){
 
1243
 
    error(0, 0, "Question file \"%s\" contained bad \"PID\": %s",
 
1244
 
          question_filename, glib_error->message);
 
1248
 
  if((pid != (guint64)((pid_t)pid))
 
1249
 
     or (kill((pid_t)pid, 0) != 0)){
 
1250
 
    error(0, 0, "PID %" PRIuMAX " in question file \"%s\" is bad or"
 
1251
 
          " does not exist", (uintmax_t)pid, question_filename);
 
1255
 
  guint64 notafter = g_key_file_get_uint64(key_file, "Ask",
 
1256
 
                                           "NotAfter", &glib_error);
 
1257
 
  if(glib_error != NULL){
 
1258
 
    if(glib_error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND){
 
1259
 
      error(0, 0, "Question file \"%s\" contained bad \"NotAfter\":"
 
1260
 
            " %s", question_filename, glib_error->message);
 
1265
 
    if(queue->next_run == 0 or (queue->next_run > notafter)){
 
1266
 
      queue->next_run = notafter;
 
1268
 
    if(*current_time >= notafter){
 
1273
 
  const task_context connect_question_socket_task = {
 
1274
 
    .func=connect_question_socket,
 
1275
 
    .question_filename=strdup(question_filename),
 
1278
 
    .filename=strdup(socket_name),
 
1279
 
    .cancelled_filenames=task.cancelled_filenames,
 
1280
 
    .mandos_client_exited=mandos_client_exited,
 
1281
 
    .password_is_read=password_is_read,
 
1282
 
    .current_time=current_time,
 
1284
 
  if(connect_question_socket_task.question_filename == NULL
 
1285
 
     or connect_question_socket_task.filename == NULL
 
1286
 
     or not add_to_queue(queue, connect_question_socket_task)){
 
1287
 
    error(0, errno, "Failed to add connect_question_socket for socket"
 
1288
 
          " %s (from \"%s\") to queue", socket_name,
 
1290
 
    cleanup_task(&connect_question_socket_task);
 
1293
 
  /* Force the added task (connect_question_socket) to run
 
1295
 
  queue->next_run = 1;
 
1298
 
    char *const dup_filename = strdup(question_filename);
 
1299
 
    const task_context cancel_old_question_task = {
 
1300
 
      .func=cancel_old_question,
 
1301
 
      .question_filename=dup_filename,
 
1303
 
      .filename=dup_filename,
 
1304
 
      .cancelled_filenames=cancelled_filenames,
 
1305
 
      .current_time=current_time,
 
1307
 
    if(cancel_old_question_task.question_filename == NULL
 
1308
 
       or not add_to_queue(queue, cancel_old_question_task)){
 
1309
 
      error(0, errno, "Failed to add cancel_old_question for file "
 
1310
 
            "\"%s\" to queue", question_filename);
 
1311
 
      cleanup_task(&cancel_old_question_task);
 
1317
 
__attribute__((nonnull))
 
1318
 
void cancel_old_question(const task_context task,
 
1319
 
                         task_queue *const queue){
 
1320
 
  char *const question_filename = task.question_filename;
 
1321
 
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
1322
 
  const mono_microsecs notafter = task.notafter;
 
1323
 
  const mono_microsecs *const current_time = task.current_time;
 
1325
 
  if(*current_time >= notafter){
 
1326
 
    if(not string_set_add(cancelled_filenames, question_filename)){
 
1327
 
      error(0, errno, "Failed to cancel question for file %s",
 
1330
 
    cleanup_task(&task);
 
1334
 
  if(not add_to_queue(queue, task)){
 
1335
 
    error(0, errno, "Failed to add cancel_old_question for file "
 
1336
 
          "%s to queue", question_filename);
 
1337
 
    cleanup_task(&task);
 
1341
 
  if((queue->next_run == 0) or (queue->next_run > notafter)){
 
1342
 
    queue->next_run = notafter;
 
1346
 
__attribute__((nonnull))
 
1347
 
void connect_question_socket(const task_context task,
 
1348
 
                             task_queue *const queue){
 
1349
 
  char *const question_filename = task.question_filename;
 
1350
 
  char *const filename = task.filename;
 
1351
 
  const int epoll_fd = task.epoll_fd;
 
1352
 
  buffer *const password = task.password;
 
1353
 
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
1354
 
  bool *const mandos_client_exited = task.mandos_client_exited;
 
1355
 
  bool *const password_is_read = task.password_is_read;
 
1356
 
  const mono_microsecs *const current_time = task.current_time;
 
1358
 
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
 
1360
 
  if(sizeof(sock_name.sun_path) <= strlen(filename)){
 
1361
 
    error(0, 0, "Socket filename is larger than"
 
1362
 
          " sizeof(sockaddr_un.sun_path); %" PRIuMAX ": \"%s\"",
 
1363
 
          (uintmax_t)sizeof(sock_name.sun_path), filename);
 
1364
 
    if(not string_set_add(cancelled_filenames, question_filename)){
 
1365
 
      error(0, errno, "Failed to cancel question for file %s",
 
1368
 
    cleanup_task(&task);
 
1372
 
  const int fd = socket(PF_LOCAL, SOCK_DGRAM
 
1373
 
                        | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 
1376
 
          "Failed to create socket(PF_LOCAL, SOCK_DGRAM, 0)");
 
1377
 
    if(not add_to_queue(queue, task)){
 
1378
 
      error(0, errno, "Failed to add connect_question_socket for file"
 
1379
 
            " \"%s\" and socket \"%s\" to queue", question_filename,
 
1381
 
      cleanup_task(&task);
 
1383
 
      /* Force the added task (connect_question_socket) to run
 
1385
 
      queue->next_run = 1;
 
1390
 
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
 
1391
 
  if(connect(fd, (struct sockaddr *)&sock_name,
 
1392
 
             (socklen_t)SUN_LEN(&sock_name)) != 0){
 
1393
 
    error(0, errno, "Failed to connect socket to \"%s\"", filename);
 
1394
 
    if(not add_to_queue(queue, task)){
 
1395
 
      error(0, errno, "Failed to add connect_question_socket for file"
 
1396
 
            " \"%s\" and socket \"%s\" to queue", question_filename,
 
1398
 
      cleanup_task(&task);
 
1400
 
      /* Force the added task (connect_question_socket) to run again,
 
1401
 
         at most one second from now */
 
1402
 
      if((queue->next_run == 0)
 
1403
 
         or (queue->next_run > (*current_time + 1000000))){
 
1404
 
        queue->next_run = *current_time + 1000000;
 
1410
 
  /* Not necessary, but we can try, and merely warn on failure */
 
1411
 
  if(shutdown(fd, SHUT_RD) != 0){
 
1412
 
    error(0, errno, "Failed to shutdown reading from socket \"%s\"",
 
1416
 
  /* Add the fd to the epoll set */
 
1417
 
  if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
1418
 
               &(struct epoll_event){ .events=EPOLLOUT })
 
1420
 
    error(0, errno, "Failed to add inotify file descriptor %d for"
 
1421
 
          " socket %s to epoll set", fd, filename);
 
1422
 
    if(not add_to_queue(queue, task)){
 
1423
 
      error(0, errno, "Failed to add connect_question_socket for file"
 
1424
 
            " \"%s\" and socket \"%s\" to queue", question_filename,
 
1426
 
      cleanup_task(&task);
 
1428
 
      /* Force the added task (connect_question_socket) to run again,
 
1429
 
         at most one second from now */
 
1430
 
      if((queue->next_run == 0)
 
1431
 
         or (queue->next_run > (*current_time + 1000000))){
 
1432
 
        queue->next_run = *current_time + 1000000;
 
1438
 
  /* add task send_password_to_socket to queue */
 
1439
 
  const task_context send_password_to_socket_task = {
 
1440
 
    .func=send_password_to_socket,
 
1441
 
    .question_filename=question_filename,
 
1446
 
    .cancelled_filenames=cancelled_filenames,
 
1447
 
    .mandos_client_exited=mandos_client_exited,
 
1448
 
    .password_is_read=password_is_read,
 
1449
 
    .current_time=current_time,
 
1452
 
  if(not add_to_queue(queue, send_password_to_socket_task)){
 
1453
 
    error(0, errno, "Failed to add send_password_to_socket for"
 
1454
 
          " file \"%s\" and socket \"%s\" to queue",
 
1455
 
          question_filename, filename);
 
1456
 
    cleanup_task(&send_password_to_socket_task);
 
1460
 
__attribute__((nonnull))
 
1461
 
void send_password_to_socket(const task_context task,
 
1462
 
                             task_queue *const queue){
 
1463
 
  char *const question_filename=task.question_filename;
 
1464
 
  char *const filename=task.filename;
 
1465
 
  const int epoll_fd=task.epoll_fd;
 
1466
 
  const int fd=task.fd;
 
1467
 
  buffer *const password=task.password;
 
1468
 
  string_set *const cancelled_filenames=task.cancelled_filenames;
 
1469
 
  bool *const mandos_client_exited = task.mandos_client_exited;
 
1470
 
  bool *const password_is_read = task.password_is_read;
 
1471
 
  const mono_microsecs *const current_time = task.current_time;
 
1473
 
  if(*mandos_client_exited and *password_is_read){
 
1475
 
    const size_t send_buffer_length = password->length + 2;
 
1476
 
    char *send_buffer = malloc(send_buffer_length);
 
1477
 
    if(send_buffer == NULL){
 
1478
 
      error(0, errno, "Failed to allocate send_buffer");
 
1480
 
      if(mlock(send_buffer, send_buffer_length) != 0){
 
1481
 
        /* Warn but do not treat as fatal error */
 
1482
 
        if(errno != EPERM and errno != ENOMEM){
 
1483
 
          error(0, errno, "Failed to lock memory for password"
 
1487
 
      /* “[…] send a single datagram to the socket consisting of the
 
1488
 
         password string either prefixed with "+" or with "-"
 
1489
 
         depending on whether the password entry was successful or
 
1490
 
         not. You may but don't have to include a final NUL byte in
 
1493
 
         — <https://www.freedesktop.org/wiki/Software/systemd/
 
1494
 
         PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
 
1496
 
      send_buffer[0] = '+';     /* Prefix with "+" */
 
1497
 
      /* Always add an extra NUL */
 
1498
 
      send_buffer[password->length + 1] = '\0';
 
1499
 
      if(password->length > 0){
 
1500
 
        memcpy(send_buffer + 1, password->data, password->length);
 
1503
 
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
 
1505
 
      const error_t saved_errno = errno;
 
1506
 
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
 
1507
 
      explicit_bzero(send_buffer, send_buffer_length);
 
1509
 
      memset(send_buffer, '\0', send_buffer_length);
 
1511
 
      if(munlock(send_buffer, send_buffer_length) != 0){
 
1512
 
        error(0, errno, "Failed to unlock memory of send buffer");
 
1515
 
      if(ssret < 0 or ssret < (ssize_t)send_buffer_length){
 
1516
 
        switch(saved_errno){
 
1529
 
          error(0, 0, "Password of size %" PRIuMAX " is too big",
 
1530
 
                (uintmax_t)password->length);
 
1534
 
          __attribute__((fallthrough));
 
1537
 
          if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
 
1538
 
            error(0, 0, "Password only partially sent to socket");
 
1543
 
          __attribute__((fallthrough));
 
1546
 
          error(0, saved_errno, "Failed to send() to socket %s",
 
1548
 
          if(not string_set_add(cancelled_filenames,
 
1549
 
                                question_filename)){
 
1550
 
            error(0, errno, "Failed to cancel question for file %s",
 
1553
 
          cleanup_task(&task);
 
1558
 
        cleanup_task(&task);
 
1564
 
  /* We failed or are not ready yet; retry later */
 
1566
 
  if(not add_to_queue(queue, task)){
 
1567
 
    error(0, errno, "Failed to add send_password_to_socket for"
 
1568
 
          " file %s and socket %s to queue", question_filename,
 
1570
 
    cleanup_task(&task);
 
1573
 
  /* Add the fd to the epoll set */
 
1574
 
  if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
1575
 
               &(struct epoll_event){ .events=EPOLLOUT })
 
1577
 
    error(0, errno, "Failed to add socket file descriptor %d for"
 
1578
 
          " socket %s to epoll set", fd, filename);
 
1579
 
    /* Force the added task (send_password_to_socket) to run again, at
 
1580
 
       most one second from now */
 
1581
 
    if((queue->next_run == 0)
 
1582
 
       or (queue->next_run > (*current_time + 1000000))){
 
1583
 
      queue->next_run = *current_time + 1000000;
 
1588
 
__attribute__((warn_unused_result))
 
1589
 
bool add_existing_questions(task_queue *const queue,
 
1591
 
                            buffer *const password,
 
1592
 
                            string_set *cancelled_filenames,
 
1593
 
                            const mono_microsecs *const current_time,
 
1594
 
                            bool *const mandos_client_exited,
 
1595
 
                            bool *const password_is_read,
 
1596
 
                            const char *const dirname){
 
1597
 
  __attribute__((cleanup(cleanup_string)))
 
1598
 
    char *dir_pattern = NULL;
 
1599
 
  const int ret = asprintf(&dir_pattern, "%s/ask.*", dirname);
 
1600
 
  if(ret < 0 or dir_pattern == NULL){
 
1601
 
    error(0, errno, "Could not create glob pattern for directory %s",
 
1605
 
  __attribute__((cleanup(globfree)))
 
1606
 
    glob_t question_filenames = {};
 
1607
 
  switch(glob(dir_pattern, GLOB_ERR | GLOB_NOSORT | GLOB_MARK,
 
1608
 
              NULL, &question_filenames)){
 
1611
 
    error(0, errno, "Failed to open directory %s", dirname);
 
1614
 
    error(0, errno, "There are no question files in %s", dirname);
 
1617
 
    error(0, errno, "Could not allocate memory for question file"
 
1618
 
          " names in %s", dirname);
 
1622
 
    __attribute__((fallthrough));
 
1625
 
    for(size_t i = 0; i < question_filenames.gl_pathc; i++){
 
1626
 
      char *const question_filename = strdup(question_filenames
 
1628
 
      const task_context task = {
 
1629
 
        .func=open_and_parse_question,
 
1631
 
        .question_filename=question_filename,
 
1632
 
        .filename=question_filename,
 
1634
 
        .cancelled_filenames=cancelled_filenames,
 
1635
 
        .current_time=current_time,
 
1636
 
        .mandos_client_exited=mandos_client_exited,
 
1637
 
        .password_is_read=password_is_read,
 
1640
 
      if(question_filename == NULL
 
1641
 
         or not add_to_queue(queue, task)){
 
1642
 
        error(0, errno, "Failed to add open_and_parse_question for"
 
1643
 
              " file %s to queue",
 
1644
 
              question_filenames.gl_pathv[i]);
 
1645
 
        free(question_filename);
 
1647
 
        queue->next_run = 1;
 
1654
 
__attribute__((nonnull, warn_unused_result))
 
1655
 
bool wait_for_event(const int epoll_fd,
 
1656
 
                    const mono_microsecs queue_next_run,
 
1657
 
                    const mono_microsecs current_time){
 
1658
 
  __attribute__((const))
 
1659
 
    int milliseconds_to_wait(const mono_microsecs currtime,
 
1660
 
                             const mono_microsecs nextrun){
 
1661
 
    if(currtime >= nextrun){
 
1664
 
    const uintmax_t wait_time_ms = (nextrun - currtime) / 1000;
 
1665
 
    if(wait_time_ms > (uintmax_t)INT_MAX){
 
1668
 
    return (int)wait_time_ms;
 
1671
 
  const int wait_time_ms = milliseconds_to_wait(current_time,
 
1674
 
  /* Prepare unblocking of SIGCHLD during epoll_pwait */
 
1675
 
  sigset_t temporary_unblocked_sigmask;
 
1676
 
  /* Get current signal mask */
 
1677
 
  if(pthread_sigmask(-1, NULL, &temporary_unblocked_sigmask) != 0){
 
1680
 
  /* Remove SIGCHLD from the signal mask */
 
1681
 
  if(sigdelset(&temporary_unblocked_sigmask, SIGCHLD) != 0){
 
1684
 
  struct epoll_event events[8]; /* Ignored */
 
1685
 
  int ret = epoll_pwait(epoll_fd, events,
 
1686
 
                        sizeof(events) / sizeof(struct epoll_event),
 
1687
 
                        queue_next_run == 0 ? -1 : (int)wait_time_ms,
 
1688
 
                        &temporary_unblocked_sigmask);
 
1689
 
  if(ret < 0 and errno != EINTR){
 
1690
 
    error(0, errno, "Failed epoll_pwait(epfd=%d, ..., timeout=%d,"
 
1692
 
          queue_next_run == 0 ? -1 : (int)wait_time_ms);
 
1695
 
  return clear_all_fds_from_epoll_set(epoll_fd);
 
1698
 
bool clear_all_fds_from_epoll_set(const int epoll_fd){
 
1699
 
  /* Create a new empty epoll set */
 
1700
 
  __attribute__((cleanup(cleanup_close)))
 
1701
 
    const int new_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
1702
 
  if(new_epoll_fd < 0){
 
1705
 
  /* dup3() the new epoll set fd over the old one, replacing it */
 
1706
 
  if(dup3(new_epoll_fd, epoll_fd, O_CLOEXEC) < 0){
 
1712
 
__attribute__((nonnull, warn_unused_result))
 
1713
 
bool run_queue(task_queue **const queue,
 
1714
 
               string_set *const cancelled_filenames,
 
1715
 
               bool *const quit_now){
 
1717
 
  task_queue *new_queue = create_queue();
 
1718
 
  if(new_queue == NULL){
 
1722
 
  __attribute__((cleanup(string_set_clear)))
 
1723
 
    string_set old_cancelled_filenames = {};
 
1724
 
  string_set_swap(cancelled_filenames, &old_cancelled_filenames);
 
1726
 
  /* Declare i outside the for loop, since we might need i after the
 
1727
 
     loop in case we aborted in the middle */
 
1729
 
  for(i=0; i < (*queue)->length and not *quit_now; i++){
 
1730
 
    task_context *const task = &((*queue)->tasks[i]);
 
1731
 
    const char *const question_filename = task->question_filename;
 
1732
 
    /* Skip any task referencing a cancelled question filename */
 
1733
 
    if(question_filename != NULL
 
1734
 
       and string_set_contains(old_cancelled_filenames,
 
1735
 
                               question_filename)){
 
1739
 
    task->func(*task, new_queue);
 
1743
 
    /* we might be in the middle of the queue, so clean up any
 
1744
 
       remaining tasks in the current queue */
 
1745
 
    for(; i < (*queue)->length; i++){
 
1746
 
      cleanup_task(&((*queue)->tasks[i]));
 
1760
 
/* End of regular code section */
 
1762
 
/* Start of tests section; here are the tests for the above code */
 
1764
 
/* This "fixture" data structure is used by the test setup and
 
1765
 
   teardown functions */
 
1767
 
  struct sigaction orig_sigaction;
 
1768
 
  sigset_t orig_sigmask;
 
1771
 
static void test_setup(test_fixture *fixture,
 
1772
 
                       __attribute__((unused))
 
1773
 
                       gconstpointer user_data){
 
1774
 
  g_assert_true(setup_signal_handler(&fixture->orig_sigaction));
 
1775
 
  g_assert_true(block_sigchld(&fixture->orig_sigmask));
 
1778
 
static void test_teardown(test_fixture *fixture,
 
1779
 
                          __attribute__((unused))
 
1780
 
                          gconstpointer user_data){
 
1781
 
  g_assert_true(restore_signal_handler(&fixture->orig_sigaction));
 
1782
 
  g_assert_true(restore_sigmask(&fixture->orig_sigmask));
 
1785
 
/* Utility function used by tests to search queue for matching task */
 
1786
 
__attribute__((pure, nonnull, warn_unused_result))
 
1787
 
static task_context *find_matching_task(const task_queue *const queue,
 
1788
 
                                        const task_context task){
 
1789
 
  /* The argument "task" structure is a pattern to match; 0 in any
 
1790
 
     member means any value matches, otherwise the value must match.
 
1791
 
     The filename strings are compared by strcmp(), not by pointer. */
 
1792
 
  for(size_t i = 0; i < queue->length; i++){
 
1793
 
    task_context *const current_task = queue->tasks+i;
 
1794
 
    /* Check all members of task_context, if set to a non-zero value.
 
1795
 
       If a member does not match, continue to next task in queue */
 
1797
 
    /* task_func *const func */
 
1798
 
    if(task.func != NULL and current_task->func != task.func){
 
1801
 
    /* char *const question_filename; */
 
1802
 
    if(task.question_filename != NULL
 
1803
 
       and (current_task->question_filename == NULL
 
1804
 
            or strcmp(current_task->question_filename,
 
1805
 
                      task.question_filename) != 0)){
 
1808
 
    /* const pid_t pid; */
 
1809
 
    if(task.pid != 0 and current_task->pid != task.pid){
 
1812
 
    /* const int epoll_fd; */
 
1813
 
    if(task.epoll_fd != 0
 
1814
 
       and current_task->epoll_fd != task.epoll_fd){
 
1817
 
    /* bool *const quit_now; */
 
1818
 
    if(task.quit_now != NULL
 
1819
 
       and current_task->quit_now != task.quit_now){
 
1823
 
    if(task.fd != 0 and current_task->fd != task.fd){
 
1826
 
    /* bool *const mandos_client_exited; */
 
1827
 
    if(task.mandos_client_exited != NULL
 
1828
 
       and current_task->mandos_client_exited
 
1829
 
       != task.mandos_client_exited){
 
1832
 
    /* buffer *const password; */
 
1833
 
    if(task.password != NULL
 
1834
 
       and current_task->password != task.password){
 
1837
 
    /* bool *const password_is_read; */
 
1838
 
    if(task.password_is_read != NULL
 
1839
 
       and current_task->password_is_read != task.password_is_read){
 
1842
 
    /* char *filename; */
 
1843
 
    if(task.filename != NULL
 
1844
 
       and (current_task->filename == NULL
 
1845
 
            or strcmp(current_task->filename, task.filename) != 0)){
 
1848
 
    /* string_set *const cancelled_filenames; */
 
1849
 
    if(task.cancelled_filenames != NULL
 
1850
 
       and current_task->cancelled_filenames
 
1851
 
       != task.cancelled_filenames){
 
1854
 
    /* const mono_microsecs notafter; */
 
1855
 
    if(task.notafter != 0
 
1856
 
       and current_task->notafter != task.notafter){
 
1859
 
    /* const mono_microsecs *const current_time; */
 
1860
 
    if(task.current_time != NULL
 
1861
 
       and current_task->current_time != task.current_time){
 
1864
 
    /* Current task matches all members; return it */
 
1865
 
    return current_task;
 
1867
 
  /* No task in queue matches passed pattern task */
 
1871
 
static void test_create_queue(__attribute__((unused))
 
1872
 
                              test_fixture *fixture,
 
1873
 
                              __attribute__((unused))
 
1874
 
                              gconstpointer user_data){
 
1875
 
  __attribute__((cleanup(cleanup_queue)))
 
1876
 
    task_queue *const queue = create_queue();
 
1877
 
  g_assert_nonnull(queue);
 
1878
 
  g_assert_null(queue->tasks);
 
1879
 
  g_assert_true(queue->length == 0);
 
1880
 
  g_assert_true(queue->next_run == 0);
 
1883
 
static task_func dummy_func;
 
1885
 
static void test_add_to_queue(__attribute__((unused))
 
1886
 
                              test_fixture *fixture,
 
1887
 
                              __attribute__((unused))
 
1888
 
                              gconstpointer user_data){
 
1889
 
  __attribute__((cleanup(cleanup_queue)))
 
1890
 
    task_queue *queue = create_queue();
 
1891
 
  g_assert_nonnull(queue);
 
1893
 
  g_assert_true(add_to_queue(queue,
 
1894
 
                             (task_context){ .func=dummy_func }));
 
1895
 
  g_assert_true(queue->length == 1);
 
1896
 
  g_assert_nonnull(queue->tasks);
 
1897
 
  g_assert_true(queue->tasks[0].func == dummy_func);
 
1900
 
static void test_add_to_queue_overflow(__attribute__((unused))
 
1901
 
                                       test_fixture *fixture,
 
1902
 
                                       __attribute__((unused))
 
1903
 
                                       gconstpointer user_data){
 
1904
 
  __attribute__((cleanup(cleanup_queue)))
 
1905
 
    task_queue *queue = create_queue();
 
1906
 
  g_assert_nonnull(queue);
 
1907
 
  g_assert_true(queue->length == 0);
 
1908
 
  queue->length = SIZE_MAX / sizeof(task_context); /* fake max size */
 
1910
 
  FILE *real_stderr = stderr;
 
1911
 
  FILE *devnull = fopen("/dev/null", "we");
 
1912
 
  g_assert_nonnull(devnull);
 
1914
 
  const bool ret = add_to_queue(queue,
 
1915
 
                                (task_context){ .func=dummy_func });
 
1916
 
  g_assert_true(errno == ENOMEM);
 
1917
 
  g_assert_false(ret);
 
1918
 
  stderr = real_stderr;
 
1919
 
  g_assert_cmpint(fclose(devnull), ==, 0);
 
1920
 
  queue->length = 0;            /* Restore real size */
 
1923
 
static void dummy_func(__attribute__((unused))
 
1924
 
                       const task_context task,
 
1925
 
                       __attribute__((unused))
 
1926
 
                       task_queue *const queue){
 
1929
 
static void test_queue_has_question_empty(__attribute__((unused))
 
1930
 
                                          test_fixture *fixture,
 
1931
 
                                          __attribute__((unused))
 
1932
 
                                          gconstpointer user_data){
 
1933
 
  __attribute__((cleanup(cleanup_queue)))
 
1934
 
    task_queue *queue = create_queue();
 
1935
 
  g_assert_nonnull(queue);
 
1936
 
  g_assert_false(queue_has_question(queue));
 
1939
 
static void test_queue_has_question_false(__attribute__((unused))
 
1940
 
                                          test_fixture *fixture,
 
1941
 
                                          __attribute__((unused))
 
1942
 
                                          gconstpointer user_data){
 
1943
 
  __attribute__((cleanup(cleanup_queue)))
 
1944
 
    task_queue *queue = create_queue();
 
1945
 
  g_assert_nonnull(queue);
 
1946
 
  g_assert_true(add_to_queue(queue,
 
1947
 
                             (task_context){ .func=dummy_func }));
 
1948
 
  g_assert_false(queue_has_question(queue));
 
1951
 
static void test_queue_has_question_true(__attribute__((unused))
 
1952
 
                                         test_fixture *fixture,
 
1953
 
                                         __attribute__((unused))
 
1954
 
                                         gconstpointer user_data){
 
1955
 
  __attribute__((cleanup(cleanup_queue)))
 
1956
 
    task_queue *queue = create_queue();
 
1957
 
  g_assert_nonnull(queue);
 
1958
 
  char *const question_filename
 
1959
 
    = strdup("/nonexistent/question_filename");
 
1960
 
  g_assert_nonnull(question_filename);
 
1961
 
  task_context task = {
 
1963
 
    .question_filename=question_filename,
 
1965
 
  g_assert_true(add_to_queue(queue, task));
 
1966
 
  g_assert_true(queue_has_question(queue));
 
1969
 
static void test_queue_has_question_false2(__attribute__((unused))
 
1970
 
                                           test_fixture *fixture,
 
1971
 
                                           __attribute__((unused))
 
1972
 
                                           gconstpointer user_data){
 
1973
 
  __attribute__((cleanup(cleanup_queue)))
 
1974
 
    task_queue *queue = create_queue();
 
1975
 
  g_assert_nonnull(queue);
 
1976
 
  task_context task = { .func=dummy_func };
 
1977
 
  g_assert_true(add_to_queue(queue, task));
 
1978
 
  g_assert_true(add_to_queue(queue, task));
 
1979
 
  g_assert_cmpint((int)queue->length, ==, 2);
 
1980
 
  g_assert_false(queue_has_question(queue));
 
1983
 
static void test_queue_has_question_true2(__attribute__((unused))
 
1984
 
                                          test_fixture *fixture,
 
1985
 
                                          __attribute__((unused))
 
1986
 
                                          gconstpointer user_data){
 
1987
 
  __attribute__((cleanup(cleanup_queue)))
 
1988
 
    task_queue *queue = create_queue();
 
1989
 
  g_assert_nonnull(queue);
 
1990
 
  task_context task1 = { .func=dummy_func };
 
1991
 
  g_assert_true(add_to_queue(queue, task1));
 
1992
 
  char *const question_filename
 
1993
 
    = strdup("/nonexistent/question_filename");
 
1994
 
  g_assert_nonnull(question_filename);
 
1995
 
  task_context task2 = {
 
1997
 
    .question_filename=question_filename,
 
1999
 
  g_assert_true(add_to_queue(queue, task2));
 
2000
 
  g_assert_cmpint((int)queue->length, ==, 2);
 
2001
 
  g_assert_true(queue_has_question(queue));
 
2004
 
static void test_cleanup_buffer(__attribute__((unused))
 
2005
 
                                test_fixture *fixture,
 
2006
 
                                __attribute__((unused))
 
2007
 
                                gconstpointer user_data){
 
2010
 
  const size_t buffersize = 10;
 
2012
 
  buf.data = malloc(buffersize);
 
2013
 
  g_assert_nonnull(buf.data);
 
2014
 
  if(mlock(buf.data, buffersize) != 0){
 
2015
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
2018
 
  cleanup_buffer(&buf);
 
2019
 
  g_assert_null(buf.data);
 
2023
 
void test_string_set_new_set_contains_nothing(__attribute__((unused))
 
2024
 
                                              test_fixture *fixture,
 
2025
 
                                              __attribute__((unused))
 
2028
 
  __attribute__((cleanup(string_set_clear)))
 
2029
 
    string_set set = {};
 
2030
 
  g_assert_false(string_set_contains(set, "")); /* Empty string */
 
2031
 
  g_assert_false(string_set_contains(set, "test_string"));
 
2035
 
test_string_set_with_added_string_contains_it(__attribute__((unused))
 
2036
 
                                              test_fixture *fixture,
 
2037
 
                                              __attribute__((unused))
 
2040
 
  __attribute__((cleanup(string_set_clear)))
 
2041
 
    string_set set = {};
 
2042
 
  g_assert_true(string_set_add(&set, "test_string"));
 
2043
 
  g_assert_true(string_set_contains(set, "test_string"));
 
2047
 
test_string_set_cleared_does_not_contain_str(__attribute__((unused))
 
2048
 
                                             test_fixture *fixture,
 
2049
 
                                             __attribute__((unused))
 
2050
 
                                             gconstpointer user_data){
 
2051
 
  __attribute__((cleanup(string_set_clear)))
 
2052
 
    string_set set = {};
 
2053
 
  g_assert_true(string_set_add(&set, "test_string"));
 
2054
 
  string_set_clear(&set);
 
2055
 
  g_assert_false(string_set_contains(set, "test_string"));
 
2059
 
void test_string_set_swap_one_with_empty(__attribute__((unused))
 
2060
 
                                         test_fixture *fixture,
 
2061
 
                                         __attribute__((unused))
 
2062
 
                                         gconstpointer user_data){
 
2063
 
  __attribute__((cleanup(string_set_clear)))
 
2064
 
    string_set set1 = {};
 
2065
 
  __attribute__((cleanup(string_set_clear)))
 
2066
 
    string_set set2 = {};
 
2067
 
  g_assert_true(string_set_add(&set1, "test_string1"));
 
2068
 
  string_set_swap(&set1, &set2);
 
2069
 
  g_assert_false(string_set_contains(set1, "test_string1"));
 
2070
 
  g_assert_true(string_set_contains(set2, "test_string1"));
 
2074
 
void test_string_set_swap_empty_with_one(__attribute__((unused))
 
2075
 
                                         test_fixture *fixture,
 
2076
 
                                         __attribute__((unused))
 
2077
 
                                         gconstpointer user_data){
 
2078
 
  __attribute__((cleanup(string_set_clear)))
 
2079
 
    string_set set1 = {};
 
2080
 
  __attribute__((cleanup(string_set_clear)))
 
2081
 
    string_set set2 = {};
 
2082
 
  g_assert_true(string_set_add(&set2, "test_string2"));
 
2083
 
  string_set_swap(&set1, &set2);
 
2084
 
  g_assert_true(string_set_contains(set1, "test_string2"));
 
2085
 
  g_assert_false(string_set_contains(set2, "test_string2"));
 
2088
 
static void test_string_set_swap_one_with_one(__attribute__((unused))
 
2089
 
                                              test_fixture *fixture,
 
2090
 
                                              __attribute__((unused))
 
2093
 
  __attribute__((cleanup(string_set_clear)))
 
2094
 
    string_set set1 = {};
 
2095
 
  __attribute__((cleanup(string_set_clear)))
 
2096
 
    string_set set2 = {};
 
2097
 
  g_assert_true(string_set_add(&set1, "test_string1"));
 
2098
 
  g_assert_true(string_set_add(&set2, "test_string2"));
 
2099
 
  string_set_swap(&set1, &set2);
 
2100
 
  g_assert_false(string_set_contains(set1, "test_string1"));
 
2101
 
  g_assert_true(string_set_contains(set1, "test_string2"));
 
2102
 
  g_assert_false(string_set_contains(set2, "test_string2"));
 
2103
 
  g_assert_true(string_set_contains(set2, "test_string1"));
 
2106
 
static bool fd_has_cloexec_and_nonblock(const int);
 
2108
 
static bool epoll_set_contains(int, int, uint32_t);
 
2110
 
static void test_start_mandos_client(test_fixture *fixture,
 
2111
 
                                     __attribute__((unused))
 
2112
 
                                     gconstpointer user_data){
 
2114
 
  bool mandos_client_exited = false;
 
2115
 
  bool quit_now = false;
 
2116
 
  __attribute__((cleanup(cleanup_close)))
 
2117
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2118
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2119
 
  __attribute__((cleanup(cleanup_queue)))
 
2120
 
    task_queue *queue = create_queue();
 
2121
 
  g_assert_nonnull(queue);
 
2122
 
  buffer password = {};
 
2123
 
  bool password_is_read = false;
 
2124
 
  const char helper_directory[] = "/nonexistent";
 
2125
 
  const char *const argv[] = { "/bin/true", NULL };
 
2127
 
  g_assert_true(start_mandos_client(queue, epoll_fd,
 
2128
 
                                    &mandos_client_exited, &quit_now,
 
2129
 
                                    &password, &password_is_read,
 
2130
 
                                    &fixture->orig_sigaction,
 
2131
 
                                    fixture->orig_sigmask,
 
2132
 
                                    helper_directory, 0, 0, argv));
 
2134
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
2136
 
  const task_context *const added_wait_task
 
2137
 
    = find_matching_task(queue, (task_context){
 
2138
 
        .func=wait_for_mandos_client_exit,
 
2139
 
        .mandos_client_exited=&mandos_client_exited,
 
2140
 
        .quit_now=&quit_now,
 
2142
 
  g_assert_nonnull(added_wait_task);
 
2143
 
  g_assert_cmpint(added_wait_task->pid, >, 0);
 
2144
 
  g_assert_cmpint(kill(added_wait_task->pid, SIGKILL), ==, 0);
 
2145
 
  waitpid(added_wait_task->pid, NULL, 0);
 
2147
 
  const task_context *const added_read_task
 
2148
 
    = find_matching_task(queue, (task_context){
 
2149
 
        .func=read_mandos_client_output,
 
2151
 
        .password=&password,
 
2152
 
        .password_is_read=&password_is_read,
 
2153
 
        .quit_now=&quit_now,
 
2155
 
  g_assert_nonnull(added_read_task);
 
2156
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
2157
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
2158
 
  g_assert_true(epoll_set_contains(epoll_fd, added_read_task->fd,
 
2159
 
                                   EPOLLIN | EPOLLRDHUP));
 
2162
 
static bool fd_has_cloexec_and_nonblock(const int fd){
 
2163
 
  const int socket_fd_flags = fcntl(fd, F_GETFD, 0);
 
2164
 
  const int socket_file_flags = fcntl(fd, F_GETFL, 0);
 
2165
 
  return ((socket_fd_flags >= 0)
 
2166
 
          and (socket_fd_flags & FD_CLOEXEC)
 
2167
 
          and (socket_file_flags >= 0)
 
2168
 
          and (socket_file_flags & O_NONBLOCK));
 
2171
 
__attribute__((const))
 
2172
 
bool is_privileged(void){
 
2173
 
  uid_t user = getuid() + 1;
 
2174
 
  if(user == 0){                /* Overflow check */
 
2177
 
  gid_t group = getuid() + 1;
 
2178
 
  if(group == 0){               /* Overflow check */
 
2181
 
  const pid_t pid = fork();
 
2182
 
  if(pid == 0){                 /* Child */
 
2183
 
    if(setresgid((uid_t)-1, group, group) == -1){
 
2185
 
        error(EXIT_FAILURE, errno, "Failed to setresgid(-1, %" PRIuMAX
 
2186
 
              ", %" PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
 
2190
 
    if(setresuid((uid_t)-1, user, user) == -1){
 
2192
 
        error(EXIT_FAILURE, errno, "Failed to setresuid(-1, %" PRIuMAX
 
2193
 
              ", %" PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
 
2200
 
    error(EXIT_FAILURE, errno, "Failed to fork()");
 
2204
 
  waitpid(pid, &status, 0);
 
2205
 
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
 
2211
 
static bool epoll_set_contains(int epoll_fd, int fd, uint32_t events){
 
2212
 
  /* Only scan for events in this eventmask */
 
2213
 
  const uint32_t eventmask = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
 
2214
 
  __attribute__((cleanup(cleanup_string)))
 
2215
 
    char *fdinfo_name = NULL;
 
2216
 
  int ret = asprintf(&fdinfo_name, "/proc/self/fdinfo/%d", epoll_fd);
 
2217
 
  g_assert_cmpint(ret, >, 0);
 
2218
 
  g_assert_nonnull(fdinfo_name);
 
2220
 
  FILE *fdinfo = fopen(fdinfo_name, "r");
 
2221
 
  g_assert_nonnull(fdinfo);
 
2222
 
  uint32_t reported_events;
 
2227
 
    if(getline(&line.data, &line.allocated, fdinfo) < 0){
 
2230
 
    /* See proc(5) for format of /proc/PID/fdinfo/FD for epoll fd's */
 
2231
 
    if(sscanf(line.data, "tfd: %d events: %" SCNx32 " ",
 
2232
 
              &found_fd, &reported_events) == 2){
 
2237
 
  } while(not feof(fdinfo) and not ferror(fdinfo));
 
2238
 
  g_assert_cmpint(fclose(fdinfo), ==, 0);
 
2245
 
    /* Don't check events if none are given */
 
2248
 
  return (reported_events & eventmask) == (events & eventmask);
 
2251
 
static void test_start_mandos_client_execv(test_fixture *fixture,
 
2252
 
                                           __attribute__((unused))
 
2253
 
                                           gconstpointer user_data){
 
2254
 
  bool mandos_client_exited = false;
 
2255
 
  bool quit_now = false;
 
2256
 
  __attribute__((cleanup(cleanup_close)))
 
2257
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2258
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2259
 
  __attribute__((cleanup(cleanup_queue)))
 
2260
 
    task_queue *queue = create_queue();
 
2261
 
  g_assert_nonnull(queue);
 
2262
 
  __attribute__((cleanup(cleanup_buffer)))
 
2263
 
    buffer password = {};
 
2264
 
  const char helper_directory[] = "/nonexistent";
 
2265
 
  /* Can't execv("/", ...), so this should fail */
 
2266
 
  const char *const argv[] = { "/", NULL };
 
2269
 
    __attribute__((cleanup(cleanup_close)))
 
2270
 
      const int devnull_fd = open("/dev/null",
 
2271
 
                                  O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2272
 
    g_assert_cmpint(devnull_fd, >=, 0);
 
2273
 
    __attribute__((cleanup(cleanup_close)))
 
2274
 
      const int real_stderr_fd = dup(STDERR_FILENO);
 
2275
 
    g_assert_cmpint(real_stderr_fd, >=, 0);
 
2276
 
    dup2(devnull_fd, STDERR_FILENO);
 
2278
 
    const bool success = start_mandos_client(queue, epoll_fd,
 
2279
 
                                             &mandos_client_exited,
 
2283
 
                                             &fixture->orig_sigaction,
 
2284
 
                                             fixture->orig_sigmask,
 
2285
 
                                             helper_directory, 0, 0,
 
2287
 
    dup2(real_stderr_fd, STDERR_FILENO);
 
2288
 
    g_assert_true(success);
 
2290
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 2);
 
2292
 
  struct timespec starttime, currtime;
 
2293
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2295
 
    queue->next_run = 0;
 
2296
 
    string_set cancelled_filenames = {};
 
2299
 
      __attribute__((cleanup(cleanup_close)))
 
2300
 
        const int devnull_fd = open("/dev/null",
 
2301
 
                                    O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2302
 
      g_assert_cmpint(devnull_fd, >=, 0);
 
2303
 
      __attribute__((cleanup(cleanup_close)))
 
2304
 
        const int real_stderr_fd = dup(STDERR_FILENO);
 
2305
 
      g_assert_cmpint(real_stderr_fd, >=, 0);
 
2306
 
      g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2307
 
      dup2(devnull_fd, STDERR_FILENO);
 
2308
 
      const bool success = run_queue(&queue, &cancelled_filenames,
 
2310
 
      dup2(real_stderr_fd, STDERR_FILENO);
 
2315
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2316
 
  } while(((queue->length) > 0)
 
2318
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2320
 
  g_assert_true(quit_now);
 
2321
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2322
 
  g_assert_true(mandos_client_exited);
 
2325
 
static void test_start_mandos_client_suid_euid(test_fixture *fixture,
 
2326
 
                                               __attribute__((unused))
 
2329
 
  if(not is_privileged()){
 
2330
 
    g_test_skip("Not privileged");
 
2334
 
  bool mandos_client_exited = false;
 
2335
 
  bool quit_now = false;
 
2336
 
  __attribute__((cleanup(cleanup_close)))
 
2337
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2338
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2339
 
  __attribute__((cleanup(cleanup_queue)))
 
2340
 
    task_queue *queue = create_queue();
 
2341
 
  g_assert_nonnull(queue);
 
2342
 
  __attribute__((cleanup(cleanup_buffer)))
 
2343
 
    buffer password = {};
 
2344
 
  bool password_is_read = false;
 
2345
 
  const char helper_directory[] = "/nonexistent";
 
2346
 
  const char *const argv[] = { "/usr/bin/id", "--user", NULL };
 
2350
 
  const bool success = start_mandos_client(queue, epoll_fd,
 
2351
 
                                           &mandos_client_exited,
 
2352
 
                                           &quit_now, &password,
 
2354
 
                                           &fixture->orig_sigaction,
 
2355
 
                                           fixture->orig_sigmask,
 
2356
 
                                           helper_directory, user,
 
2358
 
  g_assert_true(success);
 
2359
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
2361
 
  struct timespec starttime, currtime;
 
2362
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2364
 
    queue->next_run = 0;
 
2365
 
    string_set cancelled_filenames = {};
 
2366
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2367
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2368
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2369
 
  } while(((queue->length) > 0)
 
2371
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2373
 
  g_assert_false(quit_now);
 
2374
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2375
 
  g_assert_true(mandos_client_exited);
 
2377
 
  g_assert_true(password_is_read);
 
2378
 
  g_assert_nonnull(password.data);
 
2381
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
2383
 
  g_assert_true((uid_t)id == id);
 
2385
 
  g_assert_cmpuint((unsigned int)id, ==, 0);
 
2388
 
static void test_start_mandos_client_suid_egid(test_fixture *fixture,
 
2389
 
                                               __attribute__((unused))
 
2392
 
  if(not is_privileged()){
 
2393
 
    g_test_skip("Not privileged");
 
2397
 
  bool mandos_client_exited = false;
 
2398
 
  bool quit_now = false;
 
2399
 
  __attribute__((cleanup(cleanup_close)))
 
2400
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2401
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2402
 
  __attribute__((cleanup(cleanup_queue)))
 
2403
 
    task_queue *queue = create_queue();
 
2404
 
  g_assert_nonnull(queue);
 
2405
 
  __attribute__((cleanup(cleanup_buffer)))
 
2406
 
    buffer password = {};
 
2407
 
  bool password_is_read = false;
 
2408
 
  const char helper_directory[] = "/nonexistent";
 
2409
 
  const char *const argv[] = { "/usr/bin/id", "--group", NULL };
 
2413
 
  const bool success = start_mandos_client(queue, epoll_fd,
 
2414
 
                                           &mandos_client_exited,
 
2415
 
                                           &quit_now, &password,
 
2417
 
                                           &fixture->orig_sigaction,
 
2418
 
                                           fixture->orig_sigmask,
 
2419
 
                                           helper_directory, user,
 
2421
 
  g_assert_true(success);
 
2422
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
2424
 
  struct timespec starttime, currtime;
 
2425
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2427
 
    queue->next_run = 0;
 
2428
 
    string_set cancelled_filenames = {};
 
2429
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2430
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2431
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2432
 
  } while(((queue->length) > 0)
 
2434
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2436
 
  g_assert_false(quit_now);
 
2437
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2438
 
  g_assert_true(mandos_client_exited);
 
2440
 
  g_assert_true(password_is_read);
 
2441
 
  g_assert_nonnull(password.data);
 
2444
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
2446
 
  g_assert_true((gid_t)id == id);
 
2448
 
  g_assert_cmpuint((unsigned int)id, ==, 0);
 
2451
 
static void test_start_mandos_client_suid_ruid(test_fixture *fixture,
 
2452
 
                                               __attribute__((unused))
 
2455
 
  if(not is_privileged()){
 
2456
 
    g_test_skip("Not privileged");
 
2460
 
  bool mandos_client_exited = false;
 
2461
 
  bool quit_now = false;
 
2462
 
  __attribute__((cleanup(cleanup_close)))
 
2463
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2464
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2465
 
  __attribute__((cleanup(cleanup_queue)))
 
2466
 
    task_queue *queue = create_queue();
 
2467
 
  g_assert_nonnull(queue);
 
2468
 
  __attribute__((cleanup(cleanup_buffer)))
 
2469
 
    buffer password = {};
 
2470
 
  bool password_is_read = false;
 
2471
 
  const char helper_directory[] = "/nonexistent";
 
2472
 
  const char *const argv[] = { "/usr/bin/id", "--user", "--real",
 
2477
 
  const bool success = start_mandos_client(queue, epoll_fd,
 
2478
 
                                           &mandos_client_exited,
 
2479
 
                                           &quit_now, &password,
 
2481
 
                                           &fixture->orig_sigaction,
 
2482
 
                                           fixture->orig_sigmask,
 
2483
 
                                           helper_directory, user,
 
2485
 
  g_assert_true(success);
 
2486
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
2488
 
  struct timespec starttime, currtime;
 
2489
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2491
 
    queue->next_run = 0;
 
2492
 
    string_set cancelled_filenames = {};
 
2493
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2494
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2495
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2496
 
  } while(((queue->length) > 0)
 
2498
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2500
 
  g_assert_false(quit_now);
 
2501
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2502
 
  g_assert_true(mandos_client_exited);
 
2504
 
  g_assert_true(password_is_read);
 
2505
 
  g_assert_nonnull(password.data);
 
2508
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
2510
 
  g_assert_true((uid_t)id == id);
 
2512
 
  g_assert_cmpuint((unsigned int)id, ==, user);
 
2515
 
static void test_start_mandos_client_suid_rgid(test_fixture *fixture,
 
2516
 
                                               __attribute__((unused))
 
2519
 
  if(not is_privileged()){
 
2520
 
    g_test_skip("Not privileged");
 
2524
 
  bool mandos_client_exited = false;
 
2525
 
  bool quit_now = false;
 
2526
 
  __attribute__((cleanup(cleanup_close)))
 
2527
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2528
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2529
 
  __attribute__((cleanup(cleanup_queue)))
 
2530
 
    task_queue *queue = create_queue();
 
2531
 
  g_assert_nonnull(queue);
 
2532
 
  __attribute__((cleanup(cleanup_buffer)))
 
2533
 
    buffer password = {};
 
2534
 
  bool password_is_read = false;
 
2535
 
  const char helper_directory[] = "/nonexistent";
 
2536
 
  const char *const argv[] = { "/usr/bin/id", "--group", "--real",
 
2541
 
  const bool success = start_mandos_client(queue, epoll_fd,
 
2542
 
                                           &mandos_client_exited,
 
2543
 
                                           &quit_now, &password,
 
2545
 
                                           &fixture->orig_sigaction,
 
2546
 
                                           fixture->orig_sigmask,
 
2547
 
                                           helper_directory, user,
 
2549
 
  g_assert_true(success);
 
2550
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
2552
 
  struct timespec starttime, currtime;
 
2553
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2555
 
    queue->next_run = 0;
 
2556
 
    string_set cancelled_filenames = {};
 
2557
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2558
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2559
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2560
 
  } while(((queue->length) > 0)
 
2562
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2564
 
  g_assert_false(quit_now);
 
2565
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2566
 
  g_assert_true(mandos_client_exited);
 
2568
 
  g_assert_true(password_is_read);
 
2569
 
  g_assert_nonnull(password.data);
 
2572
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
2574
 
  g_assert_true((gid_t)id == id);
 
2576
 
  g_assert_cmpuint((unsigned int)id, ==, group);
 
2579
 
static void test_start_mandos_client_read(test_fixture *fixture,
 
2580
 
                                          __attribute__((unused))
 
2581
 
                                          gconstpointer user_data){
 
2582
 
  bool mandos_client_exited = false;
 
2583
 
  bool quit_now = false;
 
2584
 
  __attribute__((cleanup(cleanup_close)))
 
2585
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2586
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2587
 
  __attribute__((cleanup(cleanup_queue)))
 
2588
 
    task_queue *queue = create_queue();
 
2589
 
  g_assert_nonnull(queue);
 
2590
 
  __attribute__((cleanup(cleanup_buffer)))
 
2591
 
    buffer password = {};
 
2592
 
  bool password_is_read = false;
 
2593
 
  const char dummy_test_password[] = "dummy test password";
 
2594
 
  const char helper_directory[] = "/nonexistent";
 
2595
 
  const char *const argv[] = { "/bin/echo", "-n", dummy_test_password,
 
2598
 
  const bool success = start_mandos_client(queue, epoll_fd,
 
2599
 
                                           &mandos_client_exited,
 
2600
 
                                           &quit_now, &password,
 
2602
 
                                           &fixture->orig_sigaction,
 
2603
 
                                           fixture->orig_sigmask,
 
2604
 
                                           helper_directory, 0, 0,
 
2606
 
  g_assert_true(success);
 
2607
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
2609
 
  struct timespec starttime, currtime;
 
2610
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2612
 
    queue->next_run = 0;
 
2613
 
    string_set cancelled_filenames = {};
 
2614
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2615
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2616
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2617
 
  } while(((queue->length) > 0)
 
2619
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2621
 
  g_assert_false(quit_now);
 
2622
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2623
 
  g_assert_true(mandos_client_exited);
 
2625
 
  g_assert_true(password_is_read);
 
2626
 
  g_assert_cmpint((int)password.length, ==,
 
2627
 
                  sizeof(dummy_test_password)-1);
 
2628
 
  g_assert_nonnull(password.data);
 
2629
 
  g_assert_cmpint(memcmp(dummy_test_password, password.data,
 
2630
 
                         sizeof(dummy_test_password)-1), ==, 0);
 
2634
 
void test_start_mandos_client_helper_directory(test_fixture *fixture,
 
2635
 
                                               __attribute__((unused))
 
2638
 
  bool mandos_client_exited = false;
 
2639
 
  bool quit_now = false;
 
2640
 
  __attribute__((cleanup(cleanup_close)))
 
2641
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2642
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2643
 
  __attribute__((cleanup(cleanup_queue)))
 
2644
 
    task_queue *queue = create_queue();
 
2645
 
  g_assert_nonnull(queue);
 
2646
 
  __attribute__((cleanup(cleanup_buffer)))
 
2647
 
    buffer password = {};
 
2648
 
  bool password_is_read = false;
 
2649
 
  const char helper_directory[] = "/nonexistent";
 
2650
 
  const char *const argv[] = { "/bin/sh", "-c",
 
2651
 
    "echo -n ${MANDOSPLUGINHELPERDIR}", NULL };
 
2653
 
  const bool success = start_mandos_client(queue, epoll_fd,
 
2654
 
                                           &mandos_client_exited,
 
2655
 
                                           &quit_now, &password,
 
2657
 
                                           &fixture->orig_sigaction,
 
2658
 
                                           fixture->orig_sigmask,
 
2659
 
                                           helper_directory, 0, 0,
 
2661
 
  g_assert_true(success);
 
2662
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
2664
 
  struct timespec starttime, currtime;
 
2665
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2667
 
    queue->next_run = 0;
 
2668
 
    string_set cancelled_filenames = {};
 
2669
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2670
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2671
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2672
 
  } while(((queue->length) > 0)
 
2674
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2676
 
  g_assert_false(quit_now);
 
2677
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2678
 
  g_assert_true(mandos_client_exited);
 
2680
 
  g_assert_true(password_is_read);
 
2681
 
  g_assert_cmpint((int)password.length, ==,
 
2682
 
                  sizeof(helper_directory)-1);
 
2683
 
  g_assert_nonnull(password.data);
 
2684
 
  g_assert_cmpint(memcmp(helper_directory, password.data,
 
2685
 
                         sizeof(helper_directory)-1), ==, 0);
 
2688
 
__attribute__((nonnull, warn_unused_result))
 
2689
 
static bool proc_status_sigblk_to_sigset(const char *const,
 
2692
 
static void test_start_mandos_client_sigmask(test_fixture *fixture,
 
2693
 
                                             __attribute__((unused))
 
2694
 
                                             gconstpointer user_data){
 
2695
 
  bool mandos_client_exited = false;
 
2696
 
  bool quit_now = false;
 
2697
 
  __attribute__((cleanup(cleanup_close)))
 
2698
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2699
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2700
 
  __attribute__((cleanup(cleanup_queue)))
 
2701
 
    task_queue *queue = create_queue();
 
2702
 
  g_assert_nonnull(queue);
 
2703
 
  __attribute__((cleanup(cleanup_buffer)))
 
2704
 
    buffer password = {};
 
2705
 
  bool password_is_read = false;
 
2706
 
  const char helper_directory[] = "/nonexistent";
 
2707
 
  /* see proc(5) for format of /proc/self/status */
 
2708
 
  const char *const argv[] = { "/usr/bin/awk",
 
2709
 
    "$1==\"SigBlk:\"{ print $2 }", "/proc/self/status", NULL };
 
2711
 
  g_assert_true(start_mandos_client(queue, epoll_fd,
 
2712
 
                                    &mandos_client_exited, &quit_now,
 
2713
 
                                    &password, &password_is_read,
 
2714
 
                                    &fixture->orig_sigaction,
 
2715
 
                                    fixture->orig_sigmask,
 
2716
 
                                    helper_directory, 0, 0, argv));
 
2718
 
  struct timespec starttime, currtime;
 
2719
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2721
 
    queue->next_run = 0;
 
2722
 
    string_set cancelled_filenames = {};
 
2723
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2724
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2725
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2726
 
  } while((not (mandos_client_exited and password_is_read))
 
2728
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2729
 
  g_assert_true(mandos_client_exited);
 
2730
 
  g_assert_true(password_is_read);
 
2732
 
  sigset_t parsed_sigmask;
 
2733
 
  g_assert_true(proc_status_sigblk_to_sigset(password.data,
 
2736
 
  for(int signum = 1; signum < NSIG; signum++){
 
2737
 
    const bool has_signal = sigismember(&parsed_sigmask, signum);
 
2738
 
    if(sigismember(&fixture->orig_sigmask, signum)){
 
2739
 
      g_assert_true(has_signal);
 
2741
 
      g_assert_false(has_signal);
 
2746
 
__attribute__((nonnull, warn_unused_result))
 
2747
 
static bool proc_status_sigblk_to_sigset(const char *const sigblk,
 
2748
 
                                         sigset_t *const sigmask){
 
2749
 
  /* parse /proc/PID/status SigBlk value and convert to a sigset_t */
 
2750
 
  uintmax_t scanned_sigmask;
 
2751
 
  if(sscanf(sigblk, "%" SCNxMAX " ", &scanned_sigmask) != 1){
 
2754
 
  if(sigemptyset(sigmask) != 0){
 
2757
 
  for(int signum = 1; signum < NSIG; signum++){
 
2758
 
    if(scanned_sigmask & ((uintmax_t)1 << (signum-1))){
 
2759
 
      if(sigaddset(sigmask, signum) != 0){
 
2767
 
static void run_task_with_stderr_to_dev_null(const task_context task,
 
2768
 
                                             task_queue *const queue);
 
2771
 
void test_wait_for_mandos_client_exit_badpid(__attribute__((unused))
 
2772
 
                                             test_fixture *fixture,
 
2773
 
                                             __attribute__((unused))
 
2774
 
                                             gconstpointer user_data){
 
2776
 
  bool mandos_client_exited = false;
 
2777
 
  bool quit_now = false;
 
2779
 
  __attribute__((cleanup(cleanup_queue)))
 
2780
 
    task_queue *queue = create_queue();
 
2781
 
  g_assert_nonnull(queue);
 
2782
 
  const task_context task = {
 
2783
 
    .func=wait_for_mandos_client_exit,
 
2785
 
    .mandos_client_exited=&mandos_client_exited,
 
2786
 
    .quit_now=&quit_now,
 
2788
 
  run_task_with_stderr_to_dev_null(task, queue);
 
2790
 
  g_assert_false(mandos_client_exited);
 
2791
 
  g_assert_true(quit_now);
 
2792
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2795
 
static void run_task_with_stderr_to_dev_null(const task_context task,
 
2796
 
                                             task_queue *const queue){
 
2797
 
  FILE *real_stderr = stderr;
 
2798
 
  FILE *devnull = fopen("/dev/null", "we");
 
2799
 
  g_assert_nonnull(devnull);
 
2802
 
  task.func(task, queue);
 
2803
 
  stderr = real_stderr;
 
2805
 
  g_assert_cmpint(fclose(devnull), ==, 0);
 
2809
 
void test_wait_for_mandos_client_exit_noexit(test_fixture *fixture,
 
2810
 
                                             __attribute__((unused))
 
2811
 
                                             gconstpointer user_data){
 
2812
 
  bool mandos_client_exited = false;
 
2813
 
  bool quit_now = false;
 
2815
 
  pid_t create_eternal_process(void){
 
2816
 
    const pid_t pid = fork();
 
2817
 
    if(pid == 0){               /* Child */
 
2818
 
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
2819
 
        _exit(EXIT_FAILURE);
 
2821
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
2822
 
        _exit(EXIT_FAILURE);
 
2830
 
  pid_t pid = create_eternal_process();
 
2831
 
  g_assert_true(pid != -1);
 
2833
 
  __attribute__((cleanup(cleanup_queue)))
 
2834
 
    task_queue *queue = create_queue();
 
2835
 
  g_assert_nonnull(queue);
 
2836
 
  const task_context task = {
 
2837
 
    .func=wait_for_mandos_client_exit,
 
2839
 
    .mandos_client_exited=&mandos_client_exited,
 
2840
 
    .quit_now=&quit_now,
 
2842
 
  task.func(task, queue);
 
2844
 
  g_assert_false(mandos_client_exited);
 
2845
 
  g_assert_false(quit_now);
 
2846
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
2848
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
2849
 
        .func=wait_for_mandos_client_exit,
 
2851
 
        .mandos_client_exited=&mandos_client_exited,
 
2852
 
        .quit_now=&quit_now,
 
2857
 
void test_wait_for_mandos_client_exit_success(test_fixture *fixture,
 
2858
 
                                              __attribute__((unused))
 
2861
 
  bool mandos_client_exited = false;
 
2862
 
  bool quit_now = false;
 
2864
 
  pid_t create_successful_process(void){
 
2865
 
    const pid_t pid = fork();
 
2866
 
    if(pid == 0){               /* Child */
 
2867
 
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
2868
 
        _exit(EXIT_FAILURE);
 
2870
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
2871
 
        _exit(EXIT_FAILURE);
 
2877
 
  const pid_t pid = create_successful_process();
 
2878
 
  g_assert_true(pid != -1);
 
2880
 
  __attribute__((cleanup(cleanup_queue)))
 
2881
 
    task_queue *queue = create_queue();
 
2882
 
  g_assert_nonnull(queue);
 
2883
 
  const task_context initial_task = {
 
2884
 
    .func=wait_for_mandos_client_exit,
 
2886
 
    .mandos_client_exited=&mandos_client_exited,
 
2887
 
    .quit_now=&quit_now,
 
2889
 
  g_assert_true(add_to_queue(queue, initial_task));
 
2891
 
  struct timespec starttime, currtime;
 
2892
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2893
 
  __attribute__((cleanup(cleanup_close)))
 
2894
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2896
 
    queue->next_run = 0;
 
2897
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2898
 
    g_assert_true(run_queue(&queue, (string_set[]){{}}, &quit_now));
 
2899
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2900
 
  } while((not mandos_client_exited)
 
2902
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2904
 
  g_assert_true(mandos_client_exited);
 
2905
 
  g_assert_false(quit_now);
 
2906
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2910
 
void test_wait_for_mandos_client_exit_failure(test_fixture *fixture,
 
2911
 
                                              __attribute__((unused))
 
2914
 
  bool mandos_client_exited = false;
 
2915
 
  bool quit_now = false;
 
2917
 
  pid_t create_failing_process(void){
 
2918
 
    const pid_t pid = fork();
 
2919
 
    if(pid == 0){               /* Child */
 
2920
 
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
2921
 
        _exit(EXIT_FAILURE);
 
2923
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
2924
 
        _exit(EXIT_FAILURE);
 
2930
 
  const pid_t pid = create_failing_process();
 
2931
 
  g_assert_true(pid != -1);
 
2933
 
  __attribute__((cleanup(string_set_clear)))
 
2934
 
    string_set cancelled_filenames = {};
 
2935
 
  __attribute__((cleanup(cleanup_close)))
 
2936
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2937
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2938
 
  __attribute__((cleanup(cleanup_queue)))
 
2939
 
    task_queue *queue = create_queue();
 
2940
 
  g_assert_nonnull(queue);
 
2941
 
  g_assert_true(add_to_queue(queue, (task_context){
 
2942
 
        .func=wait_for_mandos_client_exit,
 
2944
 
        .mandos_client_exited=&mandos_client_exited,
 
2945
 
        .quit_now=&quit_now,
 
2948
 
  g_assert_true(sigismember(&fixture->orig_sigmask, SIGCHLD) == 0);
 
2950
 
  __attribute__((cleanup(cleanup_close)))
 
2951
 
    const int devnull_fd = open("/dev/null",
 
2952
 
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2953
 
  g_assert_cmpint(devnull_fd, >=, 0);
 
2954
 
  __attribute__((cleanup(cleanup_close)))
 
2955
 
    const int real_stderr_fd = dup(STDERR_FILENO);
 
2956
 
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
2958
 
  struct timespec starttime, currtime;
 
2959
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2961
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2962
 
    dup2(devnull_fd, STDERR_FILENO);
 
2963
 
    const bool success = run_queue(&queue, &cancelled_filenames,
 
2965
 
    dup2(real_stderr_fd, STDERR_FILENO);
 
2970
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2971
 
  } while((not mandos_client_exited)
 
2973
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2975
 
  g_assert_true(quit_now);
 
2976
 
  g_assert_true(mandos_client_exited);
 
2977
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2981
 
void test_wait_for_mandos_client_exit_killed(test_fixture *fixture,
 
2982
 
                                             __attribute__((unused))
 
2983
 
                                             gconstpointer user_data){
 
2984
 
  bool mandos_client_exited = false;
 
2985
 
  bool quit_now = false;
 
2987
 
  pid_t create_killed_process(void){
 
2988
 
    const pid_t pid = fork();
 
2989
 
    if(pid == 0){               /* Child */
 
2990
 
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
2991
 
        _exit(EXIT_FAILURE);
 
2993
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
2994
 
        _exit(EXIT_FAILURE);
 
3003
 
  const pid_t pid = create_killed_process();
 
3004
 
  g_assert_true(pid != -1);
 
3006
 
  __attribute__((cleanup(string_set_clear)))
 
3007
 
    string_set cancelled_filenames = {};
 
3008
 
  __attribute__((cleanup(cleanup_close)))
 
3009
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3010
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3011
 
  __attribute__((cleanup(cleanup_queue)))
 
3012
 
    task_queue *queue = create_queue();
 
3013
 
  g_assert_nonnull(queue);
 
3014
 
  g_assert_true(add_to_queue(queue, (task_context){
 
3015
 
        .func=wait_for_mandos_client_exit,
 
3017
 
        .mandos_client_exited=&mandos_client_exited,
 
3018
 
        .quit_now=&quit_now,
 
3021
 
  __attribute__((cleanup(cleanup_close)))
 
3022
 
    const int devnull_fd = open("/dev/null",
 
3023
 
                                O_WRONLY | O_CLOEXEC, O_NOCTTY);
 
3024
 
  g_assert_cmpint(devnull_fd, >=, 0);
 
3025
 
  __attribute__((cleanup(cleanup_close)))
 
3026
 
    const int real_stderr_fd = dup(STDERR_FILENO);
 
3027
 
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
3029
 
  struct timespec starttime, currtime;
 
3030
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
3032
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
3033
 
    dup2(devnull_fd, STDERR_FILENO);
 
3034
 
    const bool success = run_queue(&queue, &cancelled_filenames,
 
3036
 
    dup2(real_stderr_fd, STDERR_FILENO);
 
3041
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
3042
 
  } while((not mandos_client_exited)
 
3044
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
3046
 
  g_assert_true(mandos_client_exited);
 
3047
 
  g_assert_true(quit_now);
 
3048
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
3051
 
static bool epoll_set_does_not_contain(int, int);
 
3054
 
void test_read_mandos_client_output_readerror(__attribute__((unused))
 
3055
 
                                              test_fixture *fixture,
 
3056
 
                                              __attribute__((unused))
 
3059
 
  __attribute__((cleanup(cleanup_close)))
 
3060
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3061
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3063
 
  __attribute__((cleanup(cleanup_buffer)))
 
3064
 
    buffer password = {};
 
3066
 
  /* Reading /proc/self/mem from offset 0 will always give EIO */
 
3067
 
  const int fd = open("/proc/self/mem",
 
3068
 
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
 
3070
 
  bool password_is_read = false;
 
3071
 
  bool quit_now = false;
 
3072
 
  __attribute__((cleanup(cleanup_queue)))
 
3073
 
    task_queue *queue = create_queue();
 
3074
 
  g_assert_nonnull(queue);
 
3076
 
  task_context task = {
 
3077
 
    .func=read_mandos_client_output,
 
3080
 
    .password=&password,
 
3081
 
    .password_is_read=&password_is_read,
 
3082
 
    .quit_now=&quit_now,
 
3084
 
  run_task_with_stderr_to_dev_null(task, queue);
 
3085
 
  g_assert_false(password_is_read);
 
3086
 
  g_assert_cmpint((int)password.length, ==, 0);
 
3087
 
  g_assert_true(quit_now);
 
3088
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
3090
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
 
3092
 
  g_assert_cmpint(close(fd), ==, -1);
 
3095
 
static bool epoll_set_does_not_contain(int epoll_fd, int fd){
 
3096
 
  return not epoll_set_contains(epoll_fd, fd, 0);
 
3100
 
void test_read_mandos_client_output_nodata(__attribute__((unused))
 
3101
 
                                           test_fixture *fixture,
 
3102
 
                                           __attribute__((unused))
 
3103
 
                                           gconstpointer user_data){
 
3104
 
  __attribute__((cleanup(cleanup_close)))
 
3105
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3106
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3109
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3111
 
  __attribute__((cleanup(cleanup_buffer)))
 
3112
 
    buffer password = {};
 
3114
 
  bool password_is_read = false;
 
3115
 
  bool quit_now = false;
 
3116
 
  __attribute__((cleanup(cleanup_queue)))
 
3117
 
    task_queue *queue = create_queue();
 
3118
 
  g_assert_nonnull(queue);
 
3120
 
  task_context task = {
 
3121
 
    .func=read_mandos_client_output,
 
3124
 
    .password=&password,
 
3125
 
    .password_is_read=&password_is_read,
 
3126
 
    .quit_now=&quit_now,
 
3128
 
  task.func(task, queue);
 
3129
 
  g_assert_false(password_is_read);
 
3130
 
  g_assert_cmpint((int)password.length, ==, 0);
 
3131
 
  g_assert_false(quit_now);
 
3132
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
3134
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
3135
 
        .func=read_mandos_client_output,
 
3138
 
        .password=&password,
 
3139
 
        .password_is_read=&password_is_read,
 
3140
 
        .quit_now=&quit_now,
 
3143
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
3144
 
                                   EPOLLIN | EPOLLRDHUP));
 
3146
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
3149
 
static void test_read_mandos_client_output_eof(__attribute__((unused))
 
3150
 
                                               test_fixture *fixture,
 
3151
 
                                               __attribute__((unused))
 
3154
 
  __attribute__((cleanup(cleanup_close)))
 
3155
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3156
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3159
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3160
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
3162
 
  __attribute__((cleanup(cleanup_buffer)))
 
3163
 
    buffer password = {};
 
3165
 
  bool password_is_read = false;
 
3166
 
  bool quit_now = false;
 
3167
 
  __attribute__((cleanup(cleanup_queue)))
 
3168
 
    task_queue *queue = create_queue();
 
3169
 
  g_assert_nonnull(queue);
 
3171
 
  task_context task = {
 
3172
 
    .func=read_mandos_client_output,
 
3175
 
    .password=&password,
 
3176
 
    .password_is_read=&password_is_read,
 
3177
 
    .quit_now=&quit_now,
 
3179
 
  task.func(task, queue);
 
3180
 
  g_assert_true(password_is_read);
 
3181
 
  g_assert_cmpint((int)password.length, ==, 0);
 
3182
 
  g_assert_false(quit_now);
 
3183
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
3185
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
 
3187
 
  g_assert_cmpint(close(pipefds[0]), ==, -1);
 
3191
 
void test_read_mandos_client_output_once(__attribute__((unused))
 
3192
 
                                         test_fixture *fixture,
 
3193
 
                                         __attribute__((unused))
 
3194
 
                                         gconstpointer user_data){
 
3195
 
  __attribute__((cleanup(cleanup_close)))
 
3196
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3197
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3200
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3202
 
  const char dummy_test_password[] = "dummy test password";
 
3203
 
  /* Start with a pre-allocated buffer */
 
3204
 
  __attribute__((cleanup(cleanup_buffer)))
 
3206
 
    .data=malloc(sizeof(dummy_test_password)),
 
3208
 
    .allocated=sizeof(dummy_test_password),
 
3210
 
  g_assert_nonnull(password.data);
 
3211
 
  if(mlock(password.data, password.allocated) != 0){
 
3212
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
3215
 
  bool password_is_read = false;
 
3216
 
  bool quit_now = false;
 
3217
 
  __attribute__((cleanup(cleanup_queue)))
 
3218
 
    task_queue *queue = create_queue();
 
3219
 
  g_assert_nonnull(queue);
 
3221
 
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
3222
 
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
3223
 
                             sizeof(dummy_test_password)),
 
3224
 
                  ==, (int)sizeof(dummy_test_password));
 
3226
 
  task_context task = {
 
3227
 
    .func=read_mandos_client_output,
 
3230
 
    .password=&password,
 
3231
 
    .password_is_read=&password_is_read,
 
3232
 
    .quit_now=&quit_now,
 
3234
 
  task.func(task, queue);
 
3236
 
  g_assert_false(password_is_read);
 
3237
 
  g_assert_cmpint((int)password.length, ==,
 
3238
 
                  (int)sizeof(dummy_test_password));
 
3239
 
  g_assert_nonnull(password.data);
 
3240
 
  g_assert_cmpint(memcmp(password.data, dummy_test_password,
 
3241
 
                         sizeof(dummy_test_password)), ==, 0);
 
3243
 
  g_assert_false(quit_now);
 
3244
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
3246
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
3247
 
        .func=read_mandos_client_output,
 
3250
 
        .password=&password,
 
3251
 
        .password_is_read=&password_is_read,
 
3252
 
        .quit_now=&quit_now,
 
3255
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
3256
 
                                   EPOLLIN | EPOLLRDHUP));
 
3258
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
3262
 
void test_read_mandos_client_output_malloc(__attribute__((unused))
 
3263
 
                                           test_fixture *fixture,
 
3264
 
                                           __attribute__((unused))
 
3265
 
                                           gconstpointer user_data){
 
3266
 
  __attribute__((cleanup(cleanup_close)))
 
3267
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3268
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3271
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3273
 
  const char dummy_test_password[] = "dummy test password";
 
3274
 
  /* Start with an empty buffer */
 
3275
 
  __attribute__((cleanup(cleanup_buffer)))
 
3276
 
    buffer password = {};
 
3278
 
  bool password_is_read = false;
 
3279
 
  bool quit_now = false;
 
3280
 
  __attribute__((cleanup(cleanup_queue)))
 
3281
 
    task_queue *queue = create_queue();
 
3282
 
  g_assert_nonnull(queue);
 
3284
 
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
3285
 
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
3286
 
                             sizeof(dummy_test_password)),
 
3287
 
                  ==, (int)sizeof(dummy_test_password));
 
3289
 
  task_context task = {
 
3290
 
    .func=read_mandos_client_output,
 
3293
 
    .password=&password,
 
3294
 
    .password_is_read=&password_is_read,
 
3295
 
    .quit_now=&quit_now,
 
3297
 
  task.func(task, queue);
 
3299
 
  g_assert_false(password_is_read);
 
3300
 
  g_assert_cmpint((int)password.length, ==,
 
3301
 
                  (int)sizeof(dummy_test_password));
 
3302
 
  g_assert_nonnull(password.data);
 
3303
 
  g_assert_cmpint(memcmp(password.data, dummy_test_password,
 
3304
 
                         sizeof(dummy_test_password)), ==, 0);
 
3306
 
  g_assert_false(quit_now);
 
3307
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
3309
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
3310
 
        .func=read_mandos_client_output,
 
3313
 
        .password=&password,
 
3314
 
        .password_is_read=&password_is_read,
 
3315
 
        .quit_now=&quit_now,
 
3318
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
3319
 
                                   EPOLLIN | EPOLLRDHUP));
 
3321
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
3325
 
void test_read_mandos_client_output_append(__attribute__((unused))
 
3326
 
                                           test_fixture *fixture,
 
3327
 
                                           __attribute__((unused))
 
3328
 
                                           gconstpointer user_data){
 
3329
 
  __attribute__((cleanup(cleanup_close)))
 
3330
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3331
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3334
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3336
 
  const char dummy_test_password[] = "dummy test password";
 
3337
 
  __attribute__((cleanup(cleanup_buffer)))
 
3339
 
    .data=malloc(PIPE_BUF),
 
3341
 
    .allocated=PIPE_BUF,
 
3343
 
  g_assert_nonnull(password.data);
 
3344
 
  if(mlock(password.data, password.allocated) != 0){
 
3345
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
3348
 
  memset(password.data, 'x', PIPE_BUF);
 
3349
 
  char password_expected[PIPE_BUF];
 
3350
 
  memcpy(password_expected, password.data, PIPE_BUF);
 
3352
 
  bool password_is_read = false;
 
3353
 
  bool quit_now = false;
 
3354
 
  __attribute__((cleanup(cleanup_queue)))
 
3355
 
    task_queue *queue = create_queue();
 
3356
 
  g_assert_nonnull(queue);
 
3358
 
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
3359
 
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
3360
 
                             sizeof(dummy_test_password)),
 
3361
 
                  ==, (int)sizeof(dummy_test_password));
 
3363
 
  task_context task = {
 
3364
 
    .func=read_mandos_client_output,
 
3367
 
    .password=&password,
 
3368
 
    .password_is_read=&password_is_read,
 
3369
 
    .quit_now=&quit_now,
 
3371
 
  task.func(task, queue);
 
3373
 
  g_assert_false(password_is_read);
 
3374
 
  g_assert_cmpint((int)password.length, ==,
 
3375
 
                  PIPE_BUF + sizeof(dummy_test_password));
 
3376
 
  g_assert_nonnull(password.data);
 
3377
 
  g_assert_cmpint(memcmp(password_expected, password.data, PIPE_BUF),
 
3379
 
  g_assert_cmpint(memcmp(password.data + PIPE_BUF,
 
3380
 
                         dummy_test_password,
 
3381
 
                         sizeof(dummy_test_password)), ==, 0);
 
3382
 
  g_assert_false(quit_now);
 
3383
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
3385
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
3386
 
        .func=read_mandos_client_output,
 
3389
 
        .password=&password,
 
3390
 
        .password_is_read=&password_is_read,
 
3391
 
        .quit_now=&quit_now,
 
3394
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
3395
 
                                   EPOLLIN | EPOLLRDHUP));
 
3398
 
static char *make_temporary_directory(void);
 
3400
 
static void test_add_inotify_dir_watch(__attribute__((unused))
 
3401
 
                                       test_fixture *fixture,
 
3402
 
                                       __attribute__((unused))
 
3403
 
                                       gconstpointer user_data){
 
3404
 
  __attribute__((cleanup(cleanup_close)))
 
3405
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3406
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3407
 
  __attribute__((cleanup(cleanup_queue)))
 
3408
 
    task_queue *queue = create_queue();
 
3409
 
  g_assert_nonnull(queue);
 
3410
 
  __attribute__((cleanup(string_set_clear)))
 
3411
 
    string_set cancelled_filenames = {};
 
3412
 
  const mono_microsecs current_time = 0;
 
3414
 
  bool quit_now = false;
 
3415
 
  buffer password = {};
 
3416
 
  bool mandos_client_exited = false;
 
3417
 
  bool password_is_read = false;
 
3419
 
  __attribute__((cleanup(cleanup_string)))
 
3420
 
    char *tempdir = make_temporary_directory();
 
3421
 
  g_assert_nonnull(tempdir);
 
3423
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3425
 
                                      &cancelled_filenames,
 
3427
 
                                      &mandos_client_exited,
 
3428
 
                                      &password_is_read));
 
3430
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
3432
 
  const task_context *const added_read_task
 
3433
 
    = find_matching_task(queue, (task_context){
 
3434
 
        .func=read_inotify_event,
 
3436
 
        .quit_now=&quit_now,
 
3437
 
        .password=&password,
 
3439
 
        .cancelled_filenames=&cancelled_filenames,
 
3440
 
        .current_time=¤t_time,
 
3441
 
        .mandos_client_exited=&mandos_client_exited,
 
3442
 
        .password_is_read=&password_is_read,
 
3444
 
  g_assert_nonnull(added_read_task);
 
3446
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
3447
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
3448
 
  g_assert_true(epoll_set_contains(added_read_task->epoll_fd,
 
3449
 
                                   added_read_task->fd,
 
3450
 
                                   EPOLLIN | EPOLLRDHUP));
 
3452
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
3455
 
static char *make_temporary_directory(void){
 
3456
 
  char *name = strdup("/tmp/mandosXXXXXX");
 
3457
 
  g_assert_nonnull(name);
 
3458
 
  char *result = mkdtemp(name);
 
3465
 
static void test_add_inotify_dir_watch_fail(__attribute__((unused))
 
3466
 
                                            test_fixture *fixture,
 
3467
 
                                            __attribute__((unused))
 
3468
 
                                            gconstpointer user_data){
 
3469
 
  __attribute__((cleanup(cleanup_close)))
 
3470
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3471
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3472
 
  __attribute__((cleanup(cleanup_queue)))
 
3473
 
    task_queue *queue = create_queue();
 
3474
 
  g_assert_nonnull(queue);
 
3475
 
  __attribute__((cleanup(string_set_clear)))
 
3476
 
    string_set cancelled_filenames = {};
 
3477
 
  const mono_microsecs current_time = 0;
 
3479
 
  bool quit_now = false;
 
3480
 
  buffer password = {};
 
3481
 
  bool mandos_client_exited = false;
 
3482
 
  bool password_is_read = false;
 
3484
 
  const char nonexistent_dir[] = "/nonexistent";
 
3486
 
  FILE *real_stderr = stderr;
 
3487
 
  FILE *devnull = fopen("/dev/null", "we");
 
3488
 
  g_assert_nonnull(devnull);
 
3490
 
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3491
 
                                       &password, nonexistent_dir,
 
3492
 
                                       &cancelled_filenames,
 
3494
 
                                       &mandos_client_exited,
 
3495
 
                                       &password_is_read));
 
3496
 
  stderr = real_stderr;
 
3497
 
  g_assert_cmpint(fclose(devnull), ==, 0);
 
3499
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
3502
 
static void test_add_inotify_dir_watch_nondir(__attribute__((unused))
 
3503
 
                                              test_fixture *fixture,
 
3504
 
                                            __attribute__((unused))
 
3507
 
  __attribute__((cleanup(cleanup_close)))
 
3508
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3509
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3510
 
  __attribute__((cleanup(cleanup_queue)))
 
3511
 
    task_queue *queue = create_queue();
 
3512
 
  g_assert_nonnull(queue);
 
3513
 
  __attribute__((cleanup(string_set_clear)))
 
3514
 
    string_set cancelled_filenames = {};
 
3515
 
  const mono_microsecs current_time = 0;
 
3517
 
  bool quit_now = false;
 
3518
 
  buffer password = {};
 
3519
 
  bool mandos_client_exited = false;
 
3520
 
  bool password_is_read = false;
 
3522
 
  const char not_a_directory[] = "/dev/tty";
 
3524
 
  FILE *real_stderr = stderr;
 
3525
 
  FILE *devnull = fopen("/dev/null", "we");
 
3526
 
  g_assert_nonnull(devnull);
 
3528
 
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3529
 
                                       &password, not_a_directory,
 
3530
 
                                       &cancelled_filenames,
 
3532
 
                                       &mandos_client_exited,
 
3533
 
                                       &password_is_read));
 
3534
 
  stderr = real_stderr;
 
3535
 
  g_assert_cmpint(fclose(devnull), ==, 0);
 
3537
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
3540
 
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
 
3541
 
                                              test_fixture *fixture,
 
3542
 
                                              __attribute__((unused))
 
3545
 
  __attribute__((cleanup(cleanup_close)))
 
3546
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3547
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3548
 
  __attribute__((cleanup(cleanup_queue)))
 
3549
 
    task_queue *queue = create_queue();
 
3550
 
  g_assert_nonnull(queue);
 
3551
 
  __attribute__((cleanup(string_set_clear)))
 
3552
 
    string_set cancelled_filenames = {};
 
3553
 
  const mono_microsecs current_time = 0;
 
3555
 
  bool quit_now = false;
 
3556
 
  buffer password = {};
 
3557
 
  bool mandos_client_exited = false;
 
3558
 
  bool password_is_read = false;
 
3560
 
  __attribute__((cleanup(cleanup_string)))
 
3561
 
    char *tempdir = make_temporary_directory();
 
3562
 
  g_assert_nonnull(tempdir);
 
3564
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3566
 
                                      &cancelled_filenames,
 
3568
 
                                      &mandos_client_exited,
 
3569
 
                                      &password_is_read));
 
3571
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
3573
 
  const task_context *const added_read_task
 
3574
 
    = find_matching_task(queue,
 
3575
 
                         (task_context){ .func=read_inotify_event });
 
3576
 
  g_assert_nonnull(added_read_task);
 
3578
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
3579
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
3581
 
  /* "sufficient to read at least one event." - inotify(7) */
 
3582
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3584
 
  struct inotify_event *ievent = malloc(ievent_size);
 
3585
 
  g_assert_nonnull(ievent);
 
3587
 
  g_assert_cmpint(read(added_read_task->fd, ievent, ievent_size), ==,
 
3589
 
  g_assert_cmpint(errno, ==, EAGAIN);
 
3593
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
3596
 
static char *make_temporary_file_in_directory(const char
 
3600
 
void test_add_inotify_dir_watch_IN_CLOSE_WRITE(__attribute__((unused))
 
3601
 
                                               test_fixture *fixture,
 
3602
 
                                               __attribute__((unused))
 
3605
 
  __attribute__((cleanup(cleanup_close)))
 
3606
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3607
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3608
 
  __attribute__((cleanup(cleanup_queue)))
 
3609
 
    task_queue *queue = create_queue();
 
3610
 
  g_assert_nonnull(queue);
 
3611
 
  __attribute__((cleanup(string_set_clear)))
 
3612
 
    string_set cancelled_filenames = {};
 
3613
 
  const mono_microsecs current_time = 0;
 
3615
 
  bool quit_now = false;
 
3616
 
  buffer password = {};
 
3617
 
  bool mandos_client_exited = false;
 
3618
 
  bool password_is_read = false;
 
3620
 
  __attribute__((cleanup(cleanup_string)))
 
3621
 
    char *tempdir = make_temporary_directory();
 
3622
 
  g_assert_nonnull(tempdir);
 
3624
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3626
 
                                      &cancelled_filenames,
 
3628
 
                                      &mandos_client_exited,
 
3629
 
                                      &password_is_read));
 
3631
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
3633
 
  const task_context *const added_read_task
 
3634
 
    = find_matching_task(queue,
 
3635
 
                         (task_context){ .func=read_inotify_event });
 
3636
 
  g_assert_nonnull(added_read_task);
 
3638
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
3639
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
3641
 
  __attribute__((cleanup(cleanup_string)))
 
3642
 
    char *filename = make_temporary_file_in_directory(tempdir);
 
3643
 
  g_assert_nonnull(filename);
 
3645
 
  /* "sufficient to read at least one event." - inotify(7) */
 
3646
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3648
 
  struct inotify_event *ievent = malloc(ievent_size);
 
3649
 
  g_assert_nonnull(ievent);
 
3651
 
  ssize_t read_size = 0;
 
3652
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
3654
 
  g_assert_cmpint((int)read_size, >, 0);
 
3655
 
  g_assert_true(ievent->mask & IN_CLOSE_WRITE);
 
3656
 
  g_assert_cmpstr(ievent->name, ==, basename(filename));
 
3660
 
  g_assert_cmpint(unlink(filename), ==, 0);
 
3661
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
3664
 
static char *make_temporary_prefixed_file_in_directory(const char
 
3668
 
  char *filename = NULL;
 
3669
 
  g_assert_cmpint(asprintf(&filename, "%s/%sXXXXXX", dir, prefix),
 
3671
 
  g_assert_nonnull(filename);
 
3672
 
  const int fd = mkostemp(filename, O_CLOEXEC);
 
3673
 
  g_assert_cmpint(fd, >=, 0);
 
3674
 
  g_assert_cmpint(close(fd), ==, 0);
 
3678
 
static char *make_temporary_file_in_directory(const char
 
3680
 
  return make_temporary_prefixed_file_in_directory("temp", dir);
 
3684
 
void test_add_inotify_dir_watch_IN_MOVED_TO(__attribute__((unused))
 
3685
 
                                            test_fixture *fixture,
 
3686
 
                                            __attribute__((unused))
 
3687
 
                                            gconstpointer user_data){
 
3688
 
  __attribute__((cleanup(cleanup_close)))
 
3689
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3690
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3691
 
  __attribute__((cleanup(cleanup_queue)))
 
3692
 
    task_queue *queue = create_queue();
 
3693
 
  g_assert_nonnull(queue);
 
3694
 
  __attribute__((cleanup(string_set_clear)))
 
3695
 
    string_set cancelled_filenames = {};
 
3696
 
  const mono_microsecs current_time = 0;
 
3698
 
  bool quit_now = false;
 
3699
 
  buffer password = {};
 
3700
 
  bool mandos_client_exited = false;
 
3701
 
  bool password_is_read = false;
 
3703
 
  __attribute__((cleanup(cleanup_string)))
 
3704
 
    char *watchdir = make_temporary_directory();
 
3705
 
  g_assert_nonnull(watchdir);
 
3707
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3708
 
                                      &password, watchdir,
 
3709
 
                                      &cancelled_filenames,
 
3711
 
                                      &mandos_client_exited,
 
3712
 
                                      &password_is_read));
 
3714
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
3716
 
  const task_context *const added_read_task
 
3717
 
    = find_matching_task(queue,
 
3718
 
                         (task_context){ .func=read_inotify_event });
 
3719
 
  g_assert_nonnull(added_read_task);
 
3721
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
3722
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
3724
 
  char *sourcedir = make_temporary_directory();
 
3725
 
  g_assert_nonnull(sourcedir);
 
3727
 
  __attribute__((cleanup(cleanup_string)))
 
3728
 
    char *filename = make_temporary_file_in_directory(sourcedir);
 
3729
 
  g_assert_nonnull(filename);
 
3731
 
  __attribute__((cleanup(cleanup_string)))
 
3732
 
    char *targetfilename = NULL;
 
3733
 
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", watchdir,
 
3734
 
                           basename(filename)), >, 0);
 
3735
 
  g_assert_nonnull(targetfilename);
 
3737
 
  g_assert_cmpint(rename(filename, targetfilename), ==, 0);
 
3738
 
  g_assert_cmpint(rmdir(sourcedir), ==, 0);
 
3741
 
  /* "sufficient to read at least one event." - inotify(7) */
 
3742
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3744
 
  struct inotify_event *ievent = malloc(ievent_size);
 
3745
 
  g_assert_nonnull(ievent);
 
3747
 
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
 
3749
 
  g_assert_cmpint((int)read_size, >, 0);
 
3750
 
  g_assert_true(ievent->mask & IN_MOVED_TO);
 
3751
 
  g_assert_cmpstr(ievent->name, ==, basename(targetfilename));
 
3755
 
  g_assert_cmpint(unlink(targetfilename), ==, 0);
 
3756
 
  g_assert_cmpint(rmdir(watchdir), ==, 0);
 
3760
 
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
 
3761
 
                                              test_fixture *fixture,
 
3762
 
                                              __attribute__((unused))
 
3765
 
  __attribute__((cleanup(cleanup_close)))
 
3766
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3767
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3768
 
  __attribute__((cleanup(cleanup_queue)))
 
3769
 
    task_queue *queue = create_queue();
 
3770
 
  g_assert_nonnull(queue);
 
3771
 
  __attribute__((cleanup(string_set_clear)))
 
3772
 
    string_set cancelled_filenames = {};
 
3773
 
  const mono_microsecs current_time = 0;
 
3775
 
  bool quit_now = false;
 
3776
 
  buffer password = {};
 
3777
 
  bool mandos_client_exited = false;
 
3778
 
  bool password_is_read = false;
 
3780
 
  __attribute__((cleanup(cleanup_string)))
 
3781
 
    char *tempdir = make_temporary_directory();
 
3782
 
  g_assert_nonnull(tempdir);
 
3784
 
  __attribute__((cleanup(cleanup_string)))
 
3785
 
    char *tempfilename = make_temporary_file_in_directory(tempdir);
 
3786
 
  g_assert_nonnull(tempfilename);
 
3788
 
  __attribute__((cleanup(cleanup_string)))
 
3789
 
    char *targetdir = make_temporary_directory();
 
3790
 
  g_assert_nonnull(targetdir);
 
3792
 
  __attribute__((cleanup(cleanup_string)))
 
3793
 
    char *targetfilename = NULL;
 
3794
 
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
 
3795
 
                           basename(tempfilename)), >, 0);
 
3796
 
  g_assert_nonnull(targetfilename);
 
3798
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3800
 
                                      &cancelled_filenames,
 
3802
 
                                      &mandos_client_exited,
 
3803
 
                                      &password_is_read));
 
3805
 
  g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
 
3807
 
  const task_context *const added_read_task
 
3808
 
    = find_matching_task(queue,
 
3809
 
                         (task_context){ .func=read_inotify_event });
 
3810
 
  g_assert_nonnull(added_read_task);
 
3812
 
  /* "sufficient to read at least one event." - inotify(7) */
 
3813
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3815
 
  struct inotify_event *ievent = malloc(ievent_size);
 
3816
 
  g_assert_nonnull(ievent);
 
3818
 
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
 
3820
 
  g_assert_cmpint((int)read_size, >, 0);
 
3821
 
  g_assert_true(ievent->mask & IN_MOVED_FROM);
 
3822
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
 
3826
 
  g_assert_cmpint(unlink(targetfilename), ==, 0);
 
3827
 
  g_assert_cmpint(rmdir(targetdir), ==, 0);
 
3828
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
3832
 
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
 
3833
 
                                          test_fixture *fixture,
 
3834
 
                                          __attribute__((unused))
 
3835
 
                                          gconstpointer user_data){
 
3836
 
  __attribute__((cleanup(cleanup_close)))
 
3837
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3838
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3839
 
  __attribute__((cleanup(cleanup_queue)))
 
3840
 
    task_queue *queue = create_queue();
 
3841
 
  g_assert_nonnull(queue);
 
3842
 
  __attribute__((cleanup(string_set_clear)))
 
3843
 
    string_set cancelled_filenames = {};
 
3844
 
  const mono_microsecs current_time = 0;
 
3846
 
  bool quit_now = false;
 
3847
 
  buffer password = {};
 
3848
 
  bool mandos_client_exited = false;
 
3849
 
  bool password_is_read = false;
 
3851
 
  __attribute__((cleanup(cleanup_string)))
 
3852
 
    char *tempdir = make_temporary_directory();
 
3853
 
  g_assert_nonnull(tempdir);
 
3855
 
  __attribute__((cleanup(cleanup_string)))
 
3856
 
    char *tempfile = make_temporary_file_in_directory(tempdir);
 
3857
 
  g_assert_nonnull(tempfile);
 
3859
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3861
 
                                      &cancelled_filenames,
 
3863
 
                                      &mandos_client_exited,
 
3864
 
                                      &password_is_read));
 
3865
 
  g_assert_cmpint(unlink(tempfile), ==, 0);
 
3867
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
3869
 
  const task_context *const added_read_task
 
3870
 
    = find_matching_task(queue,
 
3871
 
                         (task_context){ .func=read_inotify_event });
 
3872
 
  g_assert_nonnull(added_read_task);
 
3874
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
3875
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
3877
 
  /* "sufficient to read at least one event." - inotify(7) */
 
3878
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3880
 
  struct inotify_event *ievent = malloc(ievent_size);
 
3881
 
  g_assert_nonnull(ievent);
 
3883
 
  ssize_t read_size = 0;
 
3884
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
3886
 
  g_assert_cmpint((int)read_size, >, 0);
 
3887
 
  g_assert_true(ievent->mask & IN_DELETE);
 
3888
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
 
3892
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
3896
 
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
 
3897
 
                                               test_fixture *fixture,
 
3898
 
                                               __attribute__((unused))
 
3901
 
  __attribute__((cleanup(cleanup_close)))
 
3902
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3903
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3904
 
  __attribute__((cleanup(cleanup_queue)))
 
3905
 
    task_queue *queue = create_queue();
 
3906
 
  g_assert_nonnull(queue);
 
3907
 
  __attribute__((cleanup(string_set_clear)))
 
3908
 
    string_set cancelled_filenames = {};
 
3909
 
  const mono_microsecs current_time = 0;
 
3911
 
  bool quit_now = false;
 
3912
 
  buffer password = {};
 
3913
 
  bool mandos_client_exited = false;
 
3914
 
  bool password_is_read = false;
 
3916
 
  __attribute__((cleanup(cleanup_string)))
 
3917
 
    char *tempdir = make_temporary_directory();
 
3918
 
  g_assert_nonnull(tempdir);
 
3920
 
  __attribute__((cleanup(cleanup_string)))
 
3921
 
    char *tempfile = make_temporary_file_in_directory(tempdir);
 
3922
 
  g_assert_nonnull(tempfile);
 
3923
 
  int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
 
3925
 
  g_assert_cmpint(tempfile_fd, >, 2);
 
3927
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3929
 
                                      &cancelled_filenames,
 
3931
 
                                      &mandos_client_exited,
 
3932
 
                                      &password_is_read));
 
3933
 
  g_assert_cmpint(unlink(tempfile), ==, 0);
 
3935
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
3937
 
  const task_context *const added_read_task
 
3938
 
    = find_matching_task(queue,
 
3939
 
                         (task_context){ .func=read_inotify_event });
 
3940
 
  g_assert_nonnull(added_read_task);
 
3942
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
3943
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
3945
 
  /* "sufficient to read at least one event." - inotify(7) */
 
3946
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3948
 
  struct inotify_event *ievent = malloc(ievent_size);
 
3949
 
  g_assert_nonnull(ievent);
 
3951
 
  ssize_t read_size = 0;
 
3952
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
3954
 
  g_assert_cmpint((int)read_size, >, 0);
 
3955
 
  g_assert_true(ievent->mask & IN_DELETE);
 
3956
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
 
3958
 
  g_assert_cmpint(close(tempfile_fd), ==, 0);
 
3960
 
  /* IN_EXCL_UNLINK should make the closing of the previously unlinked
 
3961
 
     file not appear as an ievent, so we should not see it now. */
 
3962
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
3963
 
  g_assert_cmpint((int)read_size, ==, -1);
 
3964
 
  g_assert_true(errno == EAGAIN);
 
3968
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
3971
 
static void test_read_inotify_event_readerror(__attribute__((unused))
 
3972
 
                                              test_fixture *fixture,
 
3973
 
                                              __attribute__((unused))
 
3976
 
  __attribute__((cleanup(cleanup_close)))
 
3977
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3978
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3979
 
  const mono_microsecs current_time = 0;
 
3981
 
  /* Reading /proc/self/mem from offset 0 will always result in EIO */
 
3982
 
  const int fd = open("/proc/self/mem",
 
3983
 
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
 
3985
 
  bool quit_now = false;
 
3986
 
  __attribute__((cleanup(cleanup_queue)))
 
3987
 
    task_queue *queue = create_queue();
 
3988
 
  g_assert_nonnull(queue);
 
3990
 
  task_context task = {
 
3991
 
    .func=read_inotify_event,
 
3994
 
    .quit_now=&quit_now,
 
3995
 
    .filename=strdup("/nonexistent"),
 
3996
 
    .cancelled_filenames = &(string_set){},
 
3998
 
    .current_time=¤t_time,
 
4000
 
  g_assert_nonnull(task.filename);
 
4001
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4002
 
  g_assert_true(quit_now);
 
4003
 
  g_assert_true(queue->next_run == 0);
 
4004
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4006
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
 
4008
 
  g_assert_cmpint(close(fd), ==, -1);
 
4011
 
static void test_read_inotify_event_bad_epoll(__attribute__((unused))
 
4012
 
                                              test_fixture *fixture,
 
4013
 
                                              __attribute__((unused))
 
4016
 
  const mono_microsecs current_time = 17;
 
4019
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4020
 
  const int epoll_fd = pipefds[0]; /* This will obviously fail */
 
4022
 
  bool quit_now = false;
 
4023
 
  buffer password = {};
 
4024
 
  bool mandos_client_exited = false;
 
4025
 
  bool password_is_read = false;
 
4026
 
  __attribute__((cleanup(cleanup_queue)))
 
4027
 
    task_queue *queue = create_queue();
 
4028
 
  g_assert_nonnull(queue);
 
4030
 
  task_context task = {
 
4031
 
    .func=read_inotify_event,
 
4034
 
    .quit_now=&quit_now,
 
4035
 
    .password=&password,
 
4036
 
    .filename=strdup("/nonexistent"),
 
4037
 
    .cancelled_filenames = &(string_set){},
 
4039
 
    .current_time=¤t_time,
 
4040
 
    .mandos_client_exited=&mandos_client_exited,
 
4041
 
    .password_is_read=&password_is_read,
 
4043
 
  g_assert_nonnull(task.filename);
 
4044
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4046
 
  g_assert_nonnull(find_matching_task(queue, task));
 
4047
 
  g_assert_true(queue->next_run == 1000000 + current_time);
 
4049
 
  g_assert_cmpint(close(pipefds[0]), ==, 0);
 
4050
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4053
 
static void test_read_inotify_event_nodata(__attribute__((unused))
 
4054
 
                                           test_fixture *fixture,
 
4055
 
                                           __attribute__((unused))
 
4056
 
                                           gconstpointer user_data){
 
4057
 
  __attribute__((cleanup(cleanup_close)))
 
4058
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4059
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4060
 
  const mono_microsecs current_time = 0;
 
4063
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4065
 
  bool quit_now = false;
 
4066
 
  buffer password = {};
 
4067
 
  bool mandos_client_exited = false;
 
4068
 
  bool password_is_read = false;
 
4069
 
  __attribute__((cleanup(cleanup_queue)))
 
4070
 
    task_queue *queue = create_queue();
 
4071
 
  g_assert_nonnull(queue);
 
4073
 
  task_context task = {
 
4074
 
    .func=read_inotify_event,
 
4077
 
    .quit_now=&quit_now,
 
4078
 
    .password=&password,
 
4079
 
    .filename=strdup("/nonexistent"),
 
4080
 
    .cancelled_filenames = &(string_set){},
 
4082
 
    .current_time=¤t_time,
 
4083
 
    .mandos_client_exited=&mandos_client_exited,
 
4084
 
    .password_is_read=&password_is_read,
 
4086
 
  g_assert_nonnull(task.filename);
 
4087
 
  task.func(task, queue);
 
4088
 
  g_assert_false(quit_now);
 
4089
 
  g_assert_true(queue->next_run == 0);
 
4090
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4092
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4093
 
        .func=read_inotify_event,
 
4096
 
        .quit_now=&quit_now,
 
4097
 
        .password=&password,
 
4098
 
        .filename=task.filename,
 
4099
 
        .cancelled_filenames=task.cancelled_filenames,
 
4100
 
        .current_time=¤t_time,
 
4101
 
        .mandos_client_exited=&mandos_client_exited,
 
4102
 
        .password_is_read=&password_is_read,
 
4105
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4106
 
                                   EPOLLIN | EPOLLRDHUP));
 
4108
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4111
 
static void test_read_inotify_event_eof(__attribute__((unused))
 
4112
 
                                        test_fixture *fixture,
 
4113
 
                                        __attribute__((unused))
 
4114
 
                                        gconstpointer user_data){
 
4115
 
  __attribute__((cleanup(cleanup_close)))
 
4116
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4117
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4118
 
  const mono_microsecs current_time = 0;
 
4121
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4122
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4124
 
  bool quit_now = false;
 
4125
 
  buffer password = {};
 
4126
 
  __attribute__((cleanup(cleanup_queue)))
 
4127
 
    task_queue *queue = create_queue();
 
4128
 
  g_assert_nonnull(queue);
 
4130
 
  task_context task = {
 
4131
 
    .func=read_inotify_event,
 
4134
 
    .quit_now=&quit_now,
 
4135
 
    .password=&password,
 
4136
 
    .filename=strdup("/nonexistent"),
 
4137
 
    .cancelled_filenames = &(string_set){},
 
4139
 
    .current_time=¤t_time,
 
4141
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4142
 
  g_assert_true(quit_now);
 
4143
 
  g_assert_true(queue->next_run == 0);
 
4144
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4146
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
 
4148
 
  g_assert_cmpint(close(pipefds[0]), ==, -1);
 
4152
 
void test_read_inotify_event_IN_CLOSE_WRITE(__attribute__((unused))
 
4153
 
                                            test_fixture *fixture,
 
4154
 
                                            __attribute__((unused))
 
4155
 
                                            gconstpointer user_data){
 
4156
 
  __attribute__((cleanup(cleanup_close)))
 
4157
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4158
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4159
 
  const mono_microsecs current_time = 0;
 
4162
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4164
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4165
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4167
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4169
 
    struct inotify_event event;
 
4170
 
    char name_buffer[NAME_MAX + 1];
 
4172
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4174
 
  const char dummy_file_name[] = "ask.dummy_file_name";
 
4175
 
  ievent->mask = IN_CLOSE_WRITE;
 
4176
 
  ievent->len = sizeof(dummy_file_name);
 
4177
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4178
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4179
 
                              + sizeof(dummy_file_name));
 
4180
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4182
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4184
 
  bool quit_now = false;
 
4185
 
  buffer password = {};
 
4186
 
  bool mandos_client_exited = false;
 
4187
 
  bool password_is_read = false;
 
4188
 
  __attribute__((cleanup(cleanup_queue)))
 
4189
 
    task_queue *queue = create_queue();
 
4190
 
  g_assert_nonnull(queue);
 
4192
 
  task_context task = {
 
4193
 
    .func=read_inotify_event,
 
4196
 
    .quit_now=&quit_now,
 
4197
 
    .password=&password,
 
4198
 
    .filename=strdup("/nonexistent"),
 
4199
 
    .cancelled_filenames = &(string_set){},
 
4201
 
    .current_time=¤t_time,
 
4202
 
    .mandos_client_exited=&mandos_client_exited,
 
4203
 
    .password_is_read=&password_is_read,
 
4205
 
  task.func(task, queue);
 
4206
 
  g_assert_false(quit_now);
 
4207
 
  g_assert_true(queue->next_run != 0);
 
4208
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 1);
 
4210
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4211
 
        .func=read_inotify_event,
 
4214
 
        .quit_now=&quit_now,
 
4215
 
        .password=&password,
 
4216
 
        .filename=task.filename,
 
4217
 
        .cancelled_filenames=task.cancelled_filenames,
 
4218
 
        .current_time=¤t_time,
 
4219
 
        .mandos_client_exited=&mandos_client_exited,
 
4220
 
        .password_is_read=&password_is_read,
 
4223
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4224
 
                                   EPOLLIN | EPOLLRDHUP));
 
4226
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
4228
 
  __attribute__((cleanup(cleanup_string)))
 
4229
 
    char *filename = NULL;
 
4230
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
4231
 
                           dummy_file_name), >, 0);
 
4232
 
  g_assert_nonnull(filename);
 
4233
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4234
 
        .func=open_and_parse_question,
 
4237
 
        .question_filename=filename,
 
4238
 
        .password=&password,
 
4239
 
        .cancelled_filenames=task.cancelled_filenames,
 
4240
 
        .current_time=¤t_time,
 
4241
 
        .mandos_client_exited=&mandos_client_exited,
 
4242
 
        .password_is_read=&password_is_read,
 
4247
 
void test_read_inotify_event_IN_MOVED_TO(__attribute__((unused))
 
4248
 
                                         test_fixture *fixture,
 
4249
 
                                         __attribute__((unused))
 
4250
 
                                         gconstpointer user_data){
 
4251
 
  __attribute__((cleanup(cleanup_close)))
 
4252
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4253
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4254
 
  const mono_microsecs current_time = 0;
 
4257
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4259
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4260
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4262
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4264
 
    struct inotify_event event;
 
4265
 
    char name_buffer[NAME_MAX + 1];
 
4267
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4269
 
  const char dummy_file_name[] = "ask.dummy_file_name";
 
4270
 
  ievent->mask = IN_MOVED_TO;
 
4271
 
  ievent->len = sizeof(dummy_file_name);
 
4272
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4273
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4274
 
                              + sizeof(dummy_file_name));
 
4275
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4277
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4279
 
  bool quit_now = false;
 
4280
 
  buffer password = {};
 
4281
 
  bool mandos_client_exited = false;
 
4282
 
  bool password_is_read = false;
 
4283
 
  __attribute__((cleanup(cleanup_queue)))
 
4284
 
    task_queue *queue = create_queue();
 
4285
 
  g_assert_nonnull(queue);
 
4287
 
  task_context task = {
 
4288
 
    .func=read_inotify_event,
 
4291
 
    .quit_now=&quit_now,
 
4292
 
    .password=&password,
 
4293
 
    .filename=strdup("/nonexistent"),
 
4294
 
    .cancelled_filenames = &(string_set){},
 
4296
 
    .current_time=¤t_time,
 
4297
 
    .mandos_client_exited=&mandos_client_exited,
 
4298
 
    .password_is_read=&password_is_read,
 
4300
 
  task.func(task, queue);
 
4301
 
  g_assert_false(quit_now);
 
4302
 
  g_assert_true(queue->next_run != 0);
 
4303
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 1);
 
4305
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4306
 
        .func=read_inotify_event,
 
4309
 
        .quit_now=&quit_now,
 
4310
 
        .password=&password,
 
4311
 
        .filename=task.filename,
 
4312
 
        .cancelled_filenames=task.cancelled_filenames,
 
4313
 
        .current_time=¤t_time,
 
4314
 
        .mandos_client_exited=&mandos_client_exited,
 
4315
 
        .password_is_read=&password_is_read,
 
4318
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4319
 
                                   EPOLLIN | EPOLLRDHUP));
 
4321
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
4323
 
  __attribute__((cleanup(cleanup_string)))
 
4324
 
    char *filename = NULL;
 
4325
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
4326
 
                           dummy_file_name), >, 0);
 
4327
 
  g_assert_nonnull(filename);
 
4328
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4329
 
        .func=open_and_parse_question,
 
4332
 
        .question_filename=filename,
 
4333
 
        .password=&password,
 
4334
 
        .cancelled_filenames=task.cancelled_filenames,
 
4335
 
        .current_time=¤t_time,
 
4336
 
        .mandos_client_exited=&mandos_client_exited,
 
4337
 
        .password_is_read=&password_is_read,
 
4342
 
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
 
4343
 
                                           test_fixture *fixture,
 
4344
 
                                           __attribute__((unused))
 
4345
 
                                           gconstpointer user_data){
 
4346
 
  __attribute__((cleanup(cleanup_close)))
 
4347
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4348
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4349
 
  __attribute__((cleanup(string_set_clear)))
 
4350
 
    string_set cancelled_filenames = {};
 
4351
 
  const mono_microsecs current_time = 0;
 
4354
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4356
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4357
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4359
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4361
 
    struct inotify_event event;
 
4362
 
    char name_buffer[NAME_MAX + 1];
 
4364
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4366
 
  const char dummy_file_name[] = "ask.dummy_file_name";
 
4367
 
  ievent->mask = IN_MOVED_FROM;
 
4368
 
  ievent->len = sizeof(dummy_file_name);
 
4369
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4370
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4371
 
                              + sizeof(dummy_file_name));
 
4372
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4374
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4376
 
  bool quit_now = false;
 
4377
 
  buffer password = {};
 
4378
 
  bool mandos_client_exited = false;
 
4379
 
  bool password_is_read = false;
 
4380
 
  __attribute__((cleanup(cleanup_queue)))
 
4381
 
    task_queue *queue = create_queue();
 
4382
 
  g_assert_nonnull(queue);
 
4384
 
  task_context task = {
 
4385
 
    .func=read_inotify_event,
 
4388
 
    .quit_now=&quit_now,
 
4389
 
    .password=&password,
 
4390
 
    .filename=strdup("/nonexistent"),
 
4391
 
    .cancelled_filenames=&cancelled_filenames,
 
4392
 
    .current_time=¤t_time,
 
4393
 
    .mandos_client_exited=&mandos_client_exited,
 
4394
 
    .password_is_read=&password_is_read,
 
4396
 
  task.func(task, queue);
 
4397
 
  g_assert_false(quit_now);
 
4398
 
  g_assert_true(queue->next_run == 0);
 
4399
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4401
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4402
 
        .func=read_inotify_event,
 
4405
 
        .quit_now=&quit_now,
 
4406
 
        .password=&password,
 
4407
 
        .filename=task.filename,
 
4408
 
        .cancelled_filenames=&cancelled_filenames,
 
4409
 
        .current_time=¤t_time,
 
4410
 
        .mandos_client_exited=&mandos_client_exited,
 
4411
 
        .password_is_read=&password_is_read,
 
4414
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4415
 
                                   EPOLLIN | EPOLLRDHUP));
 
4417
 
  __attribute__((cleanup(cleanup_string)))
 
4418
 
    char *filename = NULL;
 
4419
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
4420
 
                           dummy_file_name), >, 0);
 
4421
 
  g_assert_nonnull(filename);
 
4422
 
  g_assert_true(string_set_contains(*task.cancelled_filenames,
 
4426
 
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
 
4427
 
                                              test_fixture *fixture,
 
4428
 
                                              __attribute__((unused))
 
4431
 
  __attribute__((cleanup(cleanup_close)))
 
4432
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4433
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4434
 
  __attribute__((cleanup(string_set_clear)))
 
4435
 
    string_set cancelled_filenames = {};
 
4436
 
  const mono_microsecs current_time = 0;
 
4439
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4441
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4442
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4444
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4446
 
    struct inotify_event event;
 
4447
 
    char name_buffer[NAME_MAX + 1];
 
4449
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4451
 
  const char dummy_file_name[] = "ask.dummy_file_name";
 
4452
 
  ievent->mask = IN_DELETE;
 
4453
 
  ievent->len = sizeof(dummy_file_name);
 
4454
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4455
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4456
 
                              + sizeof(dummy_file_name));
 
4457
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4459
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4461
 
  bool quit_now = false;
 
4462
 
  buffer password = {};
 
4463
 
  bool mandos_client_exited = false;
 
4464
 
  bool password_is_read = false;
 
4465
 
  __attribute__((cleanup(cleanup_queue)))
 
4466
 
    task_queue *queue = create_queue();
 
4467
 
  g_assert_nonnull(queue);
 
4469
 
  task_context task = {
 
4470
 
    .func=read_inotify_event,
 
4473
 
    .quit_now=&quit_now,
 
4474
 
    .password=&password,
 
4475
 
    .filename=strdup("/nonexistent"),
 
4476
 
    .cancelled_filenames=&cancelled_filenames,
 
4477
 
    .current_time=¤t_time,
 
4478
 
    .mandos_client_exited=&mandos_client_exited,
 
4479
 
    .password_is_read=&password_is_read,
 
4481
 
  task.func(task, queue);
 
4482
 
  g_assert_false(quit_now);
 
4483
 
  g_assert_true(queue->next_run == 0);
 
4484
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4486
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4487
 
        .func=read_inotify_event,
 
4490
 
        .quit_now=&quit_now,
 
4491
 
        .password=&password,
 
4492
 
        .filename=task.filename,
 
4493
 
        .cancelled_filenames=&cancelled_filenames,
 
4494
 
        .current_time=¤t_time,
 
4495
 
        .mandos_client_exited=&mandos_client_exited,
 
4496
 
        .password_is_read=&password_is_read,
 
4499
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4500
 
                                   EPOLLIN | EPOLLRDHUP));
 
4502
 
  __attribute__((cleanup(cleanup_string)))
 
4503
 
    char *filename = NULL;
 
4504
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
4505
 
                           dummy_file_name), >, 0);
 
4506
 
  g_assert_nonnull(filename);
 
4507
 
  g_assert_true(string_set_contains(*task.cancelled_filenames,
 
4512
 
test_read_inotify_event_IN_CLOSE_WRITE_badname(__attribute__((unused))
 
4513
 
                                               test_fixture *fixture,
 
4514
 
                                               __attribute__((unused))
 
4517
 
  __attribute__((cleanup(cleanup_close)))
 
4518
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4519
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4520
 
  const mono_microsecs current_time = 0;
 
4523
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4525
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4526
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4528
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4530
 
    struct inotify_event event;
 
4531
 
    char name_buffer[NAME_MAX + 1];
 
4533
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4535
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
4536
 
  ievent->mask = IN_CLOSE_WRITE;
 
4537
 
  ievent->len = sizeof(dummy_file_name);
 
4538
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4539
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4540
 
                              + sizeof(dummy_file_name));
 
4541
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4543
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4545
 
  bool quit_now = false;
 
4546
 
  buffer password = {};
 
4547
 
  bool mandos_client_exited = false;
 
4548
 
  bool password_is_read = false;
 
4549
 
  __attribute__((cleanup(cleanup_queue)))
 
4550
 
    task_queue *queue = create_queue();
 
4551
 
  g_assert_nonnull(queue);
 
4553
 
  task_context task = {
 
4554
 
    .func=read_inotify_event,
 
4557
 
    .quit_now=&quit_now,
 
4558
 
    .password=&password,
 
4559
 
    .filename=strdup("/nonexistent"),
 
4560
 
    .cancelled_filenames = &(string_set){},
 
4562
 
    .current_time=¤t_time,
 
4563
 
    .mandos_client_exited=&mandos_client_exited,
 
4564
 
    .password_is_read=&password_is_read,
 
4566
 
  task.func(task, queue);
 
4567
 
  g_assert_false(quit_now);
 
4568
 
  g_assert_true(queue->next_run == 0);
 
4569
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4571
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4572
 
        .func=read_inotify_event,
 
4575
 
        .quit_now=&quit_now,
 
4576
 
        .password=&password,
 
4577
 
        .filename=task.filename,
 
4578
 
        .cancelled_filenames=task.cancelled_filenames,
 
4579
 
        .current_time=¤t_time,
 
4580
 
        .mandos_client_exited=&mandos_client_exited,
 
4581
 
        .password_is_read=&password_is_read,
 
4584
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4585
 
                                   EPOLLIN | EPOLLRDHUP));
 
4589
 
test_read_inotify_event_IN_MOVED_TO_badname(__attribute__((unused))
 
4590
 
                                            test_fixture *fixture,
 
4591
 
                                            __attribute__((unused))
 
4592
 
                                            gconstpointer user_data){
 
4593
 
  __attribute__((cleanup(cleanup_close)))
 
4594
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4595
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4596
 
  const mono_microsecs current_time = 0;
 
4599
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4601
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4602
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4604
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4606
 
    struct inotify_event event;
 
4607
 
    char name_buffer[NAME_MAX + 1];
 
4609
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4611
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
4612
 
  ievent->mask = IN_MOVED_TO;
 
4613
 
  ievent->len = sizeof(dummy_file_name);
 
4614
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4615
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4616
 
                              + sizeof(dummy_file_name));
 
4617
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4619
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4621
 
  bool quit_now = false;
 
4622
 
  buffer password = {};
 
4623
 
  bool mandos_client_exited = false;
 
4624
 
  bool password_is_read = false;
 
4625
 
  __attribute__((cleanup(cleanup_queue)))
 
4626
 
    task_queue *queue = create_queue();
 
4627
 
  g_assert_nonnull(queue);
 
4629
 
  task_context task = {
 
4630
 
    .func=read_inotify_event,
 
4633
 
    .quit_now=&quit_now,
 
4634
 
    .password=&password,
 
4635
 
    .filename=strdup("/nonexistent"),
 
4636
 
    .cancelled_filenames = &(string_set){},
 
4638
 
    .current_time=¤t_time,
 
4639
 
    .mandos_client_exited=&mandos_client_exited,
 
4640
 
    .password_is_read=&password_is_read,
 
4642
 
  task.func(task, queue);
 
4643
 
  g_assert_false(quit_now);
 
4644
 
  g_assert_true(queue->next_run == 0);
 
4645
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4647
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4648
 
        .func=read_inotify_event,
 
4651
 
        .quit_now=&quit_now,
 
4652
 
        .password=&password,
 
4653
 
        .filename=task.filename,
 
4654
 
        .cancelled_filenames=task.cancelled_filenames,
 
4655
 
        .current_time=¤t_time,
 
4656
 
        .mandos_client_exited=&mandos_client_exited,
 
4657
 
        .password_is_read=&password_is_read,
 
4660
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4661
 
                                   EPOLLIN | EPOLLRDHUP));
 
4665
 
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
 
4666
 
                                              test_fixture *fixture,
 
4667
 
                                              __attribute__((unused))
 
4670
 
  __attribute__((cleanup(cleanup_close)))
 
4671
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4672
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4673
 
  __attribute__((cleanup(string_set_clear)))
 
4674
 
    string_set cancelled_filenames = {};
 
4675
 
  const mono_microsecs current_time = 0;
 
4678
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4680
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4681
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4683
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4685
 
    struct inotify_event event;
 
4686
 
    char name_buffer[NAME_MAX + 1];
 
4688
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4690
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
4691
 
  ievent->mask = IN_MOVED_FROM;
 
4692
 
  ievent->len = sizeof(dummy_file_name);
 
4693
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4694
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4695
 
                              + sizeof(dummy_file_name));
 
4696
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4698
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4700
 
  bool quit_now = false;
 
4701
 
  buffer password = {};
 
4702
 
  bool mandos_client_exited = false;
 
4703
 
  bool password_is_read = false;
 
4704
 
  __attribute__((cleanup(cleanup_queue)))
 
4705
 
    task_queue *queue = create_queue();
 
4706
 
  g_assert_nonnull(queue);
 
4708
 
  task_context task = {
 
4709
 
    .func=read_inotify_event,
 
4712
 
    .quit_now=&quit_now,
 
4713
 
    .password=&password,
 
4714
 
    .filename=strdup("/nonexistent"),
 
4715
 
    .cancelled_filenames=&cancelled_filenames,
 
4716
 
    .current_time=¤t_time,
 
4717
 
    .mandos_client_exited=&mandos_client_exited,
 
4718
 
    .password_is_read=&password_is_read,
 
4720
 
  task.func(task, queue);
 
4721
 
  g_assert_false(quit_now);
 
4722
 
  g_assert_true(queue->next_run == 0);
 
4723
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4725
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4726
 
        .func=read_inotify_event,
 
4729
 
        .quit_now=&quit_now,
 
4730
 
        .password=&password,
 
4731
 
        .filename=task.filename,
 
4732
 
        .cancelled_filenames=&cancelled_filenames,
 
4733
 
        .current_time=¤t_time,
 
4734
 
        .mandos_client_exited=&mandos_client_exited,
 
4735
 
        .password_is_read=&password_is_read,
 
4738
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4739
 
                                   EPOLLIN | EPOLLRDHUP));
 
4741
 
  __attribute__((cleanup(cleanup_string)))
 
4742
 
    char *filename = NULL;
 
4743
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
4744
 
                           dummy_file_name), >, 0);
 
4745
 
  g_assert_nonnull(filename);
 
4746
 
  g_assert_false(string_set_contains(cancelled_filenames, filename));
 
4750
 
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
 
4751
 
                                               test_fixture *fixture,
 
4752
 
                                               __attribute__((unused))
 
4755
 
  __attribute__((cleanup(cleanup_close)))
 
4756
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4757
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4758
 
  __attribute__((cleanup(string_set_clear)))
 
4759
 
    string_set cancelled_filenames = {};
 
4760
 
  const mono_microsecs current_time = 0;
 
4763
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4765
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4766
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4768
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4770
 
    struct inotify_event event;
 
4771
 
    char name_buffer[NAME_MAX + 1];
 
4773
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4775
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
4776
 
  ievent->mask = IN_DELETE;
 
4777
 
  ievent->len = sizeof(dummy_file_name);
 
4778
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4779
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4780
 
                              + sizeof(dummy_file_name));
 
4781
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4783
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4785
 
  bool quit_now = false;
 
4786
 
  buffer password = {};
 
4787
 
  bool mandos_client_exited = false;
 
4788
 
  bool password_is_read = false;
 
4789
 
  __attribute__((cleanup(cleanup_queue)))
 
4790
 
    task_queue *queue = create_queue();
 
4791
 
  g_assert_nonnull(queue);
 
4793
 
  task_context task = {
 
4794
 
    .func=read_inotify_event,
 
4797
 
    .quit_now=&quit_now,
 
4798
 
    .password=&password,
 
4799
 
    .filename=strdup("/nonexistent"),
 
4800
 
    .cancelled_filenames=&cancelled_filenames,
 
4801
 
    .current_time=¤t_time,
 
4802
 
    .mandos_client_exited=&mandos_client_exited,
 
4803
 
    .password_is_read=&password_is_read,
 
4805
 
  task.func(task, queue);
 
4806
 
  g_assert_false(quit_now);
 
4807
 
  g_assert_true(queue->next_run == 0);
 
4808
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4810
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4811
 
        .func=read_inotify_event,
 
4814
 
        .quit_now=&quit_now,
 
4815
 
        .password=&password,
 
4816
 
        .filename=task.filename,
 
4817
 
        .cancelled_filenames=&cancelled_filenames,
 
4818
 
        .current_time=¤t_time,
 
4819
 
        .mandos_client_exited=&mandos_client_exited,
 
4820
 
        .password_is_read=&password_is_read,
 
4823
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4824
 
                                   EPOLLIN | EPOLLRDHUP));
 
4826
 
  __attribute__((cleanup(cleanup_string)))
 
4827
 
    char *filename = NULL;
 
4828
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
4829
 
                           dummy_file_name), >, 0);
 
4830
 
  g_assert_nonnull(filename);
 
4831
 
  g_assert_false(string_set_contains(cancelled_filenames, filename));
 
4835
 
void test_open_and_parse_question_ENOENT(__attribute__((unused))
 
4836
 
                                         test_fixture *fixture,
 
4837
 
                                         __attribute__((unused))
 
4838
 
                                         gconstpointer user_data){
 
4839
 
  __attribute__((cleanup(cleanup_close)))
 
4840
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4841
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4842
 
  __attribute__((cleanup(string_set_clear)))
 
4843
 
    string_set cancelled_filenames = {};
 
4844
 
  bool mandos_client_exited = false;
 
4845
 
  bool password_is_read = false;
 
4846
 
  __attribute__((cleanup(cleanup_queue)))
 
4847
 
    task_queue *queue = create_queue();
 
4848
 
  g_assert_nonnull(queue);
 
4850
 
  char *const filename = strdup("/nonexistent");
 
4851
 
  g_assert_nonnull(filename);
 
4852
 
  task_context task = {
 
4853
 
    .func=open_and_parse_question,
 
4854
 
    .question_filename=filename,
 
4856
 
    .password=(buffer[]){{}},
 
4858
 
    .cancelled_filenames=&cancelled_filenames,
 
4859
 
    .current_time=(mono_microsecs[]){0},
 
4860
 
    .mandos_client_exited=&mandos_client_exited,
 
4861
 
    .password_is_read=&password_is_read,
 
4863
 
  task.func(task, queue);
 
4864
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4867
 
static void test_open_and_parse_question_EIO(__attribute__((unused))
 
4868
 
                                             test_fixture *fixture,
 
4869
 
                                             __attribute__((unused))
 
4870
 
                                             gconstpointer user_data){
 
4871
 
  __attribute__((cleanup(cleanup_close)))
 
4872
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4873
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4874
 
  __attribute__((cleanup(string_set_clear)))
 
4875
 
    string_set cancelled_filenames = {};
 
4876
 
  buffer password = {};
 
4877
 
  bool mandos_client_exited = false;
 
4878
 
  bool password_is_read = false;
 
4879
 
  __attribute__((cleanup(cleanup_queue)))
 
4880
 
    task_queue *queue = create_queue();
 
4881
 
  g_assert_nonnull(queue);
 
4882
 
  const mono_microsecs current_time = 0;
 
4884
 
  char *filename = strdup("/proc/self/mem");
 
4885
 
  task_context task = {
 
4886
 
    .func=open_and_parse_question,
 
4887
 
    .question_filename=filename,
 
4889
 
    .password=&password,
 
4891
 
    .cancelled_filenames=&cancelled_filenames,
 
4892
 
    .current_time=¤t_time,
 
4893
 
    .mandos_client_exited=&mandos_client_exited,
 
4894
 
    .password_is_read=&password_is_read,
 
4896
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4897
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4901
 
test_open_and_parse_question_parse_error(__attribute__((unused))
 
4902
 
                                         test_fixture *fixture,
 
4903
 
                                         __attribute__((unused))
 
4904
 
                                         gconstpointer user_data){
 
4905
 
  __attribute__((cleanup(cleanup_close)))
 
4906
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4907
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4908
 
  __attribute__((cleanup(string_set_clear)))
 
4909
 
    string_set cancelled_filenames = {};
 
4910
 
  __attribute__((cleanup(cleanup_queue)))
 
4911
 
    task_queue *queue = create_queue();
 
4912
 
  g_assert_nonnull(queue);
 
4914
 
  __attribute__((cleanup(cleanup_string)))
 
4915
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
4916
 
  g_assert_nonnull(tempfilename);
 
4917
 
  int tempfile = mkostemp(tempfilename, O_CLOEXEC);
 
4918
 
  g_assert_cmpint(tempfile, >, 0);
 
4919
 
  const char bad_data[] = "this is bad syntax\n";
 
4920
 
  g_assert_cmpint(write(tempfile, bad_data, sizeof(bad_data)),
 
4921
 
                  ==, sizeof(bad_data));
 
4922
 
  g_assert_cmpint(close(tempfile), ==, 0);
 
4924
 
  char *const filename = strdup(tempfilename);
 
4925
 
  g_assert_nonnull(filename);
 
4926
 
  task_context task = {
 
4927
 
    .func=open_and_parse_question,
 
4928
 
    .question_filename=filename,
 
4930
 
    .password=(buffer[]){{}},
 
4932
 
    .cancelled_filenames=&cancelled_filenames,
 
4933
 
    .current_time=(mono_microsecs[]){0},
 
4934
 
    .mandos_client_exited=(bool[]){false},
 
4935
 
    .password_is_read=(bool[]){false},
 
4937
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4939
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4941
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
4945
 
void test_open_and_parse_question_nosocket(__attribute__((unused))
 
4946
 
                                           test_fixture *fixture,
 
4947
 
                                           __attribute__((unused))
 
4948
 
                                           gconstpointer user_data){
 
4949
 
  __attribute__((cleanup(cleanup_close)))
 
4950
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4951
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4952
 
  __attribute__((cleanup(string_set_clear)))
 
4953
 
    string_set cancelled_filenames = {};
 
4954
 
  __attribute__((cleanup(cleanup_queue)))
 
4955
 
    task_queue *queue = create_queue();
 
4956
 
  g_assert_nonnull(queue);
 
4958
 
  __attribute__((cleanup(cleanup_string)))
 
4959
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
4960
 
  g_assert_nonnull(tempfilename);
 
4961
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
4962
 
  g_assert_cmpint(questionfile, >, 0);
 
4963
 
  FILE *qf = fdopen(questionfile, "w");
 
4964
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nPID=1\n"), >, 0);
 
4965
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
4967
 
  char *const filename = strdup(tempfilename);
 
4968
 
  g_assert_nonnull(filename);
 
4969
 
  task_context task = {
 
4970
 
    .func=open_and_parse_question,
 
4971
 
    .question_filename=filename,
 
4973
 
    .password=(buffer[]){{}},
 
4975
 
    .cancelled_filenames=&cancelled_filenames,
 
4976
 
    .current_time=(mono_microsecs[]){0},
 
4977
 
    .mandos_client_exited=(bool[]){false},
 
4978
 
    .password_is_read=(bool[]){false},
 
4980
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4981
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4983
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
4987
 
void test_open_and_parse_question_badsocket(__attribute__((unused))
 
4988
 
                                            test_fixture *fixture,
 
4989
 
                                            __attribute__((unused))
 
4990
 
                                            gconstpointer user_data){
 
4991
 
  __attribute__((cleanup(cleanup_close)))
 
4992
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4993
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4994
 
  __attribute__((cleanup(string_set_clear)))
 
4995
 
    string_set cancelled_filenames = {};
 
4996
 
  __attribute__((cleanup(cleanup_queue)))
 
4997
 
    task_queue *queue = create_queue();
 
4998
 
  g_assert_nonnull(queue);
 
5000
 
  __attribute__((cleanup(cleanup_string)))
 
5001
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
5002
 
  g_assert_nonnull(tempfilename);
 
5003
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
5004
 
  g_assert_cmpint(questionfile, >, 0);
 
5005
 
  FILE *qf = fdopen(questionfile, "w");
 
5006
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=\nPID=1\n"), >, 0);
 
5007
 
  g_assert_cmpint(fclose(qf), ==, 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);
 
5023
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5025
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
5029
 
void test_open_and_parse_question_nopid(__attribute__((unused))
 
5030
 
                                        test_fixture *fixture,
 
5031
 
                                        __attribute__((unused))
 
5032
 
                                        gconstpointer user_data){
 
5033
 
  __attribute__((cleanup(cleanup_close)))
 
5034
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5035
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5036
 
  __attribute__((cleanup(string_set_clear)))
 
5037
 
    string_set cancelled_filenames = {};
 
5038
 
  __attribute__((cleanup(cleanup_queue)))
 
5039
 
    task_queue *queue = create_queue();
 
5040
 
  g_assert_nonnull(queue);
 
5042
 
  __attribute__((cleanup(cleanup_string)))
 
5043
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
5044
 
  g_assert_nonnull(tempfilename);
 
5045
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
5046
 
  g_assert_cmpint(questionfile, >, 0);
 
5047
 
  FILE *qf = fdopen(questionfile, "w");
 
5048
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\n"), >, 0);
 
5049
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
5051
 
  char *const filename = strdup(tempfilename);
 
5052
 
  g_assert_nonnull(filename);
 
5053
 
  task_context task = {
 
5054
 
    .func=open_and_parse_question,
 
5055
 
    .question_filename=filename,
 
5057
 
    .password=(buffer[]){{}},
 
5059
 
    .cancelled_filenames=&cancelled_filenames,
 
5060
 
    .current_time=(mono_microsecs[]){0},
 
5061
 
    .mandos_client_exited=(bool[]){false},
 
5062
 
    .password_is_read=(bool[]){false},
 
5064
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5065
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5067
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
5071
 
void test_open_and_parse_question_badpid(__attribute__((unused))
 
5072
 
                                         test_fixture *fixture,
 
5073
 
                                         __attribute__((unused))
 
5074
 
                                         gconstpointer user_data){
 
5075
 
  __attribute__((cleanup(cleanup_close)))
 
5076
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5077
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5078
 
  __attribute__((cleanup(string_set_clear)))
 
5079
 
    string_set cancelled_filenames = {};
 
5080
 
  __attribute__((cleanup(cleanup_queue)))
 
5081
 
    task_queue *queue = create_queue();
 
5082
 
  g_assert_nonnull(queue);
 
5084
 
  __attribute__((cleanup(cleanup_string)))
 
5085
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
5086
 
  g_assert_nonnull(tempfilename);
 
5087
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
5088
 
  g_assert_cmpint(questionfile, >, 0);
 
5089
 
  FILE *qf = fdopen(questionfile, "w");
 
5090
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=\n"),
 
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
 
test_open_and_parse_question_noexist_pid(__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
 
  buffer password = {};
 
5124
 
  bool mandos_client_exited = false;
 
5125
 
  bool password_is_read = false;
 
5126
 
  __attribute__((cleanup(cleanup_queue)))
 
5127
 
    task_queue *queue = create_queue();
 
5128
 
  g_assert_nonnull(queue);
 
5129
 
  const mono_microsecs current_time = 0;
 
5131
 
  /* Find value of sysctl kernel.pid_max */
 
5132
 
  uintmax_t pid_max = 0;
 
5133
 
  FILE *sysctl_pid_max = fopen("/proc/sys/kernel/pid_max", "r");
 
5134
 
  g_assert_nonnull(sysctl_pid_max);
 
5135
 
  g_assert_cmpint(fscanf(sysctl_pid_max, "%" PRIuMAX, &pid_max),
 
5137
 
  g_assert_cmpint(fclose(sysctl_pid_max), ==, 0);
 
5139
 
  pid_t nonexisting_pid = ((pid_t)pid_max)+1;
 
5140
 
  g_assert_true(nonexisting_pid > 0); /* Overflow check */
 
5142
 
  __attribute__((cleanup(cleanup_string)))
 
5143
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
5144
 
  g_assert_nonnull(tempfilename);
 
5145
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
5146
 
  g_assert_cmpint(questionfile, >, 0);
 
5147
 
  FILE *qf = fdopen(questionfile, "w");
 
5148
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
5149
 
                          PRIuMAX"\n", (uintmax_t)nonexisting_pid),
 
5151
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
5153
 
  char *const question_filename = strdup(tempfilename);
 
5154
 
  g_assert_nonnull(question_filename);
 
5155
 
  task_context task = {
 
5156
 
    .func=open_and_parse_question,
 
5157
 
    .question_filename=question_filename,
 
5159
 
    .password=&password,
 
5160
 
    .filename=question_filename,
 
5161
 
    .cancelled_filenames=&cancelled_filenames,
 
5162
 
    .current_time=¤t_time,
 
5163
 
    .mandos_client_exited=&mandos_client_exited,
 
5164
 
    .password_is_read=&password_is_read,
 
5166
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5167
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5169
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
5173
 
test_open_and_parse_question_no_notafter(__attribute__((unused))
 
5174
 
                                         test_fixture *fixture,
 
5175
 
                                         __attribute__((unused))
 
5176
 
                                         gconstpointer user_data){
 
5177
 
  __attribute__((cleanup(cleanup_close)))
 
5178
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5179
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5180
 
  __attribute__((cleanup(string_set_clear)))
 
5181
 
    string_set cancelled_filenames = {};
 
5182
 
  buffer password = {};
 
5183
 
  bool mandos_client_exited = false;
 
5184
 
  bool password_is_read = false;
 
5185
 
  __attribute__((cleanup(cleanup_queue)))
 
5186
 
    task_queue *queue = create_queue();
 
5187
 
  g_assert_nonnull(queue);
 
5188
 
  const mono_microsecs current_time = 0;
 
5190
 
  __attribute__((cleanup(cleanup_string)))
 
5191
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
5192
 
  g_assert_nonnull(tempfilename);
 
5193
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
5194
 
  g_assert_cmpint(questionfile, >, 0);
 
5195
 
  FILE *qf = fdopen(questionfile, "w");
 
5196
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
5197
 
                          PRIuMAX "\n", (uintmax_t)getpid()), >, 0);
 
5198
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
5200
 
  char *const filename = strdup(tempfilename);
 
5201
 
  g_assert_nonnull(filename);
 
5202
 
  task_context task = {
 
5203
 
    .func=open_and_parse_question,
 
5204
 
    .question_filename=filename,
 
5206
 
    .password=&password,
 
5208
 
    .cancelled_filenames=&cancelled_filenames,
 
5209
 
    .current_time=¤t_time,
 
5210
 
    .mandos_client_exited=&mandos_client_exited,
 
5211
 
    .password_is_read=&password_is_read,
 
5213
 
  task.func(task, queue);
 
5214
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5216
 
  __attribute__((cleanup(cleanup_string)))
 
5217
 
    char *socket_filename = strdup("/nonexistent");
 
5218
 
  g_assert_nonnull(socket_filename);
 
5219
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
5220
 
        .func=connect_question_socket,
 
5221
 
        .question_filename=tempfilename,
 
5222
 
        .filename=socket_filename,
 
5224
 
        .password=&password,
 
5225
 
        .current_time=¤t_time,
 
5226
 
        .mandos_client_exited=&mandos_client_exited,
 
5227
 
        .password_is_read=&password_is_read,
 
5230
 
  g_assert_true(queue->next_run != 0);
 
5232
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
5236
 
test_open_and_parse_question_bad_notafter(__attribute__((unused))
 
5237
 
                                          test_fixture *fixture,
 
5238
 
                                          __attribute__((unused))
 
5239
 
                                          gconstpointer user_data){
 
5240
 
  __attribute__((cleanup(cleanup_close)))
 
5241
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5242
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5243
 
  __attribute__((cleanup(string_set_clear)))
 
5244
 
    string_set cancelled_filenames = {};
 
5245
 
  buffer password = {};
 
5246
 
  bool mandos_client_exited = false;
 
5247
 
  bool password_is_read = false;
 
5248
 
  __attribute__((cleanup(cleanup_queue)))
 
5249
 
    task_queue *queue = create_queue();
 
5250
 
  g_assert_nonnull(queue);
 
5251
 
  const mono_microsecs current_time = 0;
 
5253
 
  __attribute__((cleanup(cleanup_string)))
 
5254
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
5255
 
  g_assert_nonnull(tempfilename);
 
5256
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
5257
 
  g_assert_cmpint(questionfile, >, 0);
 
5258
 
  FILE *qf = fdopen(questionfile, "w");
 
5259
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
5260
 
                          PRIuMAX "\nNotAfter=\n",
 
5261
 
                          (uintmax_t)getpid()), >, 0);
 
5262
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
5264
 
  char *const filename = strdup(tempfilename);
 
5265
 
  g_assert_nonnull(filename);
 
5266
 
  task_context task = {
 
5267
 
    .func=open_and_parse_question,
 
5268
 
    .question_filename=filename,
 
5270
 
    .password=&password,
 
5272
 
    .cancelled_filenames=&cancelled_filenames,
 
5273
 
    .current_time=¤t_time,
 
5274
 
    .mandos_client_exited=&mandos_client_exited,
 
5275
 
    .password_is_read=&password_is_read,
 
5277
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5278
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5280
 
  __attribute__((cleanup(cleanup_string)))
 
5281
 
    char *socket_filename = strdup("/nonexistent");
 
5282
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
5283
 
        .func=connect_question_socket,
 
5284
 
        .question_filename=tempfilename,
 
5285
 
        .filename=socket_filename,
 
5287
 
        .password=&password,
 
5288
 
        .current_time=¤t_time,
 
5289
 
        .mandos_client_exited=&mandos_client_exited,
 
5290
 
        .password_is_read=&password_is_read,
 
5292
 
  g_assert_true(queue->next_run != 0);
 
5294
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
5298
 
void assert_open_and_parse_question_with_notafter(const mono_microsecs
 
5300
 
                                                  const mono_microsecs
 
5302
 
                                                  const mono_microsecs
 
5304
 
  __attribute__((cleanup(cleanup_close)))
 
5305
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5306
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5307
 
  __attribute__((cleanup(string_set_clear)))
 
5308
 
    string_set cancelled_filenames = {};
 
5309
 
  buffer password = {};
 
5310
 
  bool mandos_client_exited = false;
 
5311
 
  bool password_is_read = false;
 
5312
 
  __attribute__((cleanup(cleanup_queue)))
 
5313
 
    task_queue *queue = create_queue();
 
5314
 
  g_assert_nonnull(queue);
 
5315
 
  queue->next_run = next_queue_run;
 
5317
 
  __attribute__((cleanup(cleanup_string)))
 
5318
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
5319
 
  g_assert_nonnull(tempfilename);
 
5320
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
5321
 
  g_assert_cmpint(questionfile, >, 0);
 
5322
 
  FILE *qf = fdopen(questionfile, "w");
 
5323
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
5324
 
                          PRIuMAX "\nNotAfter=%" PRIuMAX "\n",
 
5325
 
                          (uintmax_t)getpid(), notafter), >, 0);
 
5326
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
5328
 
  char *const filename = strdup(tempfilename);
 
5329
 
  g_assert_nonnull(filename);
 
5330
 
  task_context task = {
 
5331
 
    .func=open_and_parse_question,
 
5332
 
    .question_filename=filename,
 
5334
 
    .password=&password,
 
5336
 
    .cancelled_filenames=&cancelled_filenames,
 
5337
 
    .current_time=¤t_time,
 
5338
 
    .mandos_client_exited=&mandos_client_exited,
 
5339
 
    .password_is_read=&password_is_read,
 
5341
 
  task.func(task, queue);
 
5343
 
  if(queue->length >= 1){
 
5344
 
    __attribute__((cleanup(cleanup_string)))
 
5345
 
      char *socket_filename = strdup("/nonexistent");
 
5346
 
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
5347
 
          .func=connect_question_socket,
 
5348
 
          .filename=socket_filename,
 
5350
 
          .password=&password,
 
5351
 
          .current_time=¤t_time,
 
5352
 
          .cancelled_filenames=&cancelled_filenames,
 
5353
 
          .mandos_client_exited=&mandos_client_exited,
 
5354
 
          .password_is_read=&password_is_read,
 
5356
 
    g_assert_true(queue->next_run != 0);
 
5360
 
    g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5361
 
  } else if(current_time >= notafter) {
 
5362
 
    g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5364
 
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
5365
 
          .func=cancel_old_question,
 
5366
 
          .question_filename=tempfilename,
 
5367
 
          .filename=tempfilename,
 
5369
 
          .cancelled_filenames=&cancelled_filenames,
 
5370
 
          .current_time=¤t_time,
 
5373
 
  g_assert_true(queue->next_run == 1);
 
5375
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
5379
 
test_open_and_parse_question_notafter_0(__attribute__((unused))
 
5380
 
                                        test_fixture *fixture,
 
5381
 
                                        __attribute__((unused))
 
5382
 
                                        gconstpointer user_data){
 
5383
 
  /* current_time, notafter, next_queue_run */
 
5384
 
  assert_open_and_parse_question_with_notafter(0, 0, 0);
 
5388
 
test_open_and_parse_question_notafter_1(__attribute__((unused))
 
5389
 
                                        test_fixture *fixture,
 
5390
 
                                        __attribute__((unused))
 
5391
 
                                        gconstpointer user_data){
 
5392
 
  /* current_time, notafter, next_queue_run */
 
5393
 
  assert_open_and_parse_question_with_notafter(0, 1, 0);
 
5397
 
test_open_and_parse_question_notafter_1_1(__attribute__((unused))
 
5398
 
                                          test_fixture *fixture,
 
5399
 
                                          __attribute__((unused))
 
5400
 
                                          gconstpointer user_data){
 
5401
 
  /* current_time, notafter, next_queue_run */
 
5402
 
  assert_open_and_parse_question_with_notafter(0, 1, 1);
 
5406
 
test_open_and_parse_question_notafter_1_2(__attribute__((unused))
 
5407
 
                                          test_fixture *fixture,
 
5408
 
                                          __attribute__((unused))
 
5409
 
                                          gconstpointer user_data){
 
5410
 
  /* current_time, notafter, next_queue_run */
 
5411
 
  assert_open_and_parse_question_with_notafter(0, 1, 2);
 
5415
 
test_open_and_parse_question_equal_notafter(__attribute__((unused))
 
5416
 
                                            test_fixture *fixture,
 
5417
 
                                            __attribute__((unused))
 
5418
 
                                            gconstpointer user_data){
 
5419
 
  /* current_time, notafter, next_queue_run */
 
5420
 
  assert_open_and_parse_question_with_notafter(1, 1, 0);
 
5424
 
test_open_and_parse_question_late_notafter(__attribute__((unused))
 
5425
 
                                           test_fixture *fixture,
 
5426
 
                                           __attribute__((unused))
 
5427
 
                                           gconstpointer user_data){
 
5428
 
  /* current_time, notafter, next_queue_run */
 
5429
 
  assert_open_and_parse_question_with_notafter(2, 1, 0);
 
5432
 
static void assert_cancel_old_question_param(const mono_microsecs
 
5434
 
                                             const mono_microsecs
 
5436
 
                                             const mono_microsecs
 
5438
 
                                             const mono_microsecs
 
5440
 
  __attribute__((cleanup(string_set_clear)))
 
5441
 
    string_set cancelled_filenames = {};
 
5442
 
  __attribute__((cleanup(cleanup_queue)))
 
5443
 
    task_queue *queue = create_queue();
 
5444
 
  g_assert_nonnull(queue);
 
5445
 
  queue->next_run = next_queue_run;
 
5447
 
  char *const question_filename = strdup("/nonexistent");
 
5448
 
  g_assert_nonnull(question_filename);
 
5449
 
  task_context task = {
 
5450
 
    .func=cancel_old_question,
 
5451
 
    .question_filename=question_filename,
 
5452
 
    .filename=question_filename,
 
5454
 
    .cancelled_filenames=&cancelled_filenames,
 
5455
 
    .current_time=¤t_time,
 
5457
 
  task.func(task, queue);
 
5459
 
  if(current_time >= notafter){
 
5460
 
    g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5461
 
    g_assert_true(string_set_contains(cancelled_filenames,
 
5464
 
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
5465
 
          .func=cancel_old_question,
 
5466
 
          .question_filename=question_filename,
 
5467
 
          .filename=question_filename,
 
5469
 
          .cancelled_filenames=&cancelled_filenames,
 
5470
 
          .current_time=¤t_time,
 
5473
 
    g_assert_false(string_set_contains(cancelled_filenames,
 
5474
 
                                       question_filename));
 
5476
 
  g_assert_cmpuint((unsigned int)queue->next_run, ==,
 
5477
 
                   (unsigned int)next_set_to);
 
5480
 
static void test_cancel_old_question_0_1_2(__attribute__((unused))
 
5481
 
                                           test_fixture *fixture,
 
5482
 
                                           __attribute__((unused))
 
5483
 
                                           gconstpointer user_data){
 
5484
 
  /* next_queue_run unset,
 
5485
 
     cancellation should happen because time has come,
 
5486
 
     next_queue_run should be unchanged */
 
5487
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5488
 
  assert_cancel_old_question_param(0, 1, 2, 0);
 
5491
 
static void test_cancel_old_question_0_2_1(__attribute__((unused))
 
5492
 
                                           test_fixture *fixture,
 
5493
 
                                           __attribute__((unused))
 
5494
 
                                           gconstpointer user_data){
 
5495
 
  /* If next_queue_run is 0, meaning unset, and notafter is 2,
 
5496
 
     and current_time is not yet notafter or greater,
 
5497
 
     update value of next_queue_run to value of notafter */
 
5498
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5499
 
  assert_cancel_old_question_param(0, 2, 1, 2);
 
5502
 
static void test_cancel_old_question_1_2_3(__attribute__((unused))
 
5503
 
                                           test_fixture *fixture,
 
5504
 
                                           __attribute__((unused))
 
5505
 
                                           gconstpointer user_data){
 
5506
 
  /* next_queue_run 1,
 
5507
 
     cancellation should happen because time has come,
 
5508
 
     next_queue_run should be unchanged */
 
5509
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5510
 
  assert_cancel_old_question_param(1, 2, 3, 1);
 
5513
 
static void test_cancel_old_question_1_3_2(__attribute__((unused))
 
5514
 
                                           test_fixture *fixture,
 
5515
 
                                           __attribute__((unused))
 
5516
 
                                           gconstpointer user_data){
 
5517
 
  /* If next_queue_run is set,
 
5518
 
     and current_time is not yet notafter or greater,
 
5519
 
     and notafter is larger than next_queue_run
 
5520
 
     next_queue_run should be unchanged */
 
5521
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5522
 
  assert_cancel_old_question_param(1, 3, 2, 1);
 
5525
 
static void test_cancel_old_question_2_1_3(__attribute__((unused))
 
5526
 
                                           test_fixture *fixture,
 
5527
 
                                           __attribute__((unused))
 
5528
 
                                           gconstpointer user_data){
 
5529
 
  /* next_queue_run 2,
 
5530
 
     cancellation should happen because time has come,
 
5531
 
     next_queue_run should be unchanged */
 
5532
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5533
 
  assert_cancel_old_question_param(2, 1, 3, 2);
 
5536
 
static void test_cancel_old_question_2_3_1(__attribute__((unused))
 
5537
 
                                           test_fixture *fixture,
 
5538
 
                                           __attribute__((unused))
 
5539
 
                                           gconstpointer user_data){
 
5540
 
  /* If next_queue_run is set,
 
5541
 
     and current_time is not yet notafter or greater,
 
5542
 
     and notafter is larger than next_queue_run
 
5543
 
     next_queue_run should be unchanged */
 
5544
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5545
 
  assert_cancel_old_question_param(2, 3, 1, 2);
 
5548
 
static void test_cancel_old_question_3_1_2(__attribute__((unused))
 
5549
 
                                           test_fixture *fixture,
 
5550
 
                                           __attribute__((unused))
 
5551
 
                                           gconstpointer user_data){
 
5552
 
  /* next_queue_run 3,
 
5553
 
     cancellation should happen because time has come,
 
5554
 
     next_queue_run should be unchanged */
 
5555
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5556
 
  assert_cancel_old_question_param(3, 1, 2, 3);
 
5559
 
static void test_cancel_old_question_3_2_1(__attribute__((unused))
 
5560
 
                                           test_fixture *fixture,
 
5561
 
                                           __attribute__((unused))
 
5562
 
                                           gconstpointer user_data){
 
5563
 
  /* If next_queue_run is set,
 
5564
 
     and current_time is not yet notafter or greater,
 
5565
 
     and notafter is smaller than next_queue_run
 
5566
 
     update value of next_queue_run to value of notafter */
 
5567
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5568
 
  assert_cancel_old_question_param(3, 2, 1, 2);
 
5572
 
test_connect_question_socket_name_too_long(__attribute__((unused))
 
5573
 
                                           test_fixture *fixture,
 
5574
 
                                           __attribute__((unused))
 
5575
 
                                           gconstpointer user_data){
 
5576
 
  __attribute__((cleanup(cleanup_close)))
 
5577
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5578
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5579
 
  const char question_filename[] = "/nonexistent/question";
 
5580
 
  __attribute__((cleanup(string_set_clear)))
 
5581
 
    string_set cancelled_filenames = {};
 
5582
 
  __attribute__((cleanup(cleanup_queue)))
 
5583
 
    task_queue *queue = create_queue();
 
5584
 
  g_assert_nonnull(queue);
 
5585
 
  __attribute__((cleanup(cleanup_string)))
 
5586
 
    char *tempdir = make_temporary_directory();
 
5587
 
  g_assert_nonnull(tempdir);
 
5588
 
  struct sockaddr_un unix_socket = { .sun_family=AF_LOCAL };
 
5589
 
  char socket_name[sizeof(unix_socket.sun_path)];
 
5590
 
  memset(socket_name, 'x', sizeof(socket_name));
 
5591
 
  socket_name[sizeof(socket_name)-1] = '\0';
 
5592
 
  char *filename = NULL;
 
5593
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
5595
 
  g_assert_nonnull(filename);
 
5597
 
  task_context task = {
 
5598
 
    .func=connect_question_socket,
 
5599
 
    .question_filename=strdup(question_filename),
 
5601
 
    .password=(buffer[]){{}},
 
5603
 
    .cancelled_filenames=&cancelled_filenames,
 
5604
 
    .mandos_client_exited=(bool[]){false},
 
5605
 
    .password_is_read=(bool[]){false},
 
5606
 
    .current_time=(mono_microsecs[]){0},
 
5608
 
  g_assert_nonnull(task.question_filename);
 
5609
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5611
 
  g_assert_true(string_set_contains(cancelled_filenames,
 
5612
 
                                    question_filename));
 
5613
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5614
 
  g_assert_true(queue->next_run == 0);
 
5616
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
5620
 
void test_connect_question_socket_connect_fail(__attribute__((unused))
 
5621
 
                                               test_fixture *fixture,
 
5622
 
                                               __attribute__((unused))
 
5625
 
  __attribute__((cleanup(cleanup_close)))
 
5626
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5627
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5628
 
  const char question_filename[] = "/nonexistent/question";
 
5629
 
  __attribute__((cleanup(string_set_clear)))
 
5630
 
    string_set cancelled_filenames = {};
 
5631
 
  const mono_microsecs current_time = 3;
 
5632
 
  __attribute__((cleanup(cleanup_queue)))
 
5633
 
    task_queue *queue = create_queue();
 
5634
 
  g_assert_nonnull(queue);
 
5635
 
  __attribute__((cleanup(cleanup_string)))
 
5636
 
    char *tempdir = make_temporary_directory();
 
5637
 
  g_assert_nonnull(tempdir);
 
5638
 
  char socket_name[] = "nonexistent";
 
5639
 
  char *filename = NULL;
 
5640
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
5642
 
  g_assert_nonnull(filename);
 
5644
 
  task_context task = {
 
5645
 
    .func=connect_question_socket,
 
5646
 
    .question_filename=strdup(question_filename),
 
5648
 
    .password=(buffer[]){{}},
 
5650
 
    .cancelled_filenames=&cancelled_filenames,
 
5651
 
    .mandos_client_exited=(bool[]){false},
 
5652
 
    .password_is_read=(bool[]){false},
 
5653
 
    .current_time=¤t_time,
 
5655
 
  g_assert_nonnull(task.question_filename);
 
5656
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5658
 
  g_assert_nonnull(find_matching_task(queue, task));
 
5660
 
  g_assert_false(string_set_contains(cancelled_filenames,
 
5661
 
                                     question_filename));
 
5662
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5663
 
  g_assert_true(queue->next_run == 1000000 + current_time);
 
5665
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
5669
 
void test_connect_question_socket_bad_epoll(__attribute__((unused))
 
5670
 
                                            test_fixture *fixture,
 
5671
 
                                            __attribute__((unused))
 
5672
 
                                            gconstpointer user_data){
 
5673
 
  __attribute__((cleanup(cleanup_close)))
 
5674
 
    const int epoll_fd = open("/dev/null",
 
5675
 
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
5676
 
  __attribute__((cleanup(cleanup_string)))
 
5677
 
    char *const question_filename = strdup("/nonexistent/question");
 
5678
 
  g_assert_nonnull(question_filename);
 
5679
 
  __attribute__((cleanup(string_set_clear)))
 
5680
 
    string_set cancelled_filenames = {};
 
5681
 
  const mono_microsecs current_time = 5;
 
5682
 
  __attribute__((cleanup(cleanup_queue)))
 
5683
 
    task_queue *queue = create_queue();
 
5684
 
  g_assert_nonnull(queue);
 
5685
 
  __attribute__((cleanup(cleanup_string)))
 
5686
 
    char *tempdir = make_temporary_directory();
 
5687
 
  g_assert_nonnull(tempdir);
 
5688
 
  __attribute__((cleanup(cleanup_close)))
 
5689
 
    const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
 
5690
 
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 
5691
 
  g_assert_cmpint(sock_fd, >=, 0);
 
5692
 
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
 
5693
 
  const char socket_name[] = "socket_name";
 
5694
 
  __attribute__((cleanup(cleanup_string)))
 
5695
 
    char *filename = NULL;
 
5696
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
5698
 
  g_assert_nonnull(filename);
 
5699
 
  g_assert_cmpint((int)strlen(filename), <,
 
5700
 
                  (int)sizeof(sock_name.sun_path));
 
5701
 
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
 
5702
 
  sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
 
5703
 
  g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
 
5704
 
                            (socklen_t)SUN_LEN(&sock_name)), >=, 0);
 
5705
 
  task_context task = {
 
5706
 
    .func=connect_question_socket,
 
5707
 
    .question_filename=strdup(question_filename),
 
5709
 
    .password=(buffer[]){{}},
 
5710
 
    .filename=strdup(filename),
 
5711
 
    .cancelled_filenames=&cancelled_filenames,
 
5712
 
    .mandos_client_exited=(bool[]){false},
 
5713
 
    .password_is_read=(bool[]){false},
 
5714
 
    .current_time=¤t_time,
 
5716
 
  g_assert_nonnull(task.question_filename);
 
5717
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5719
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5720
 
  const task_context *const added_task
 
5721
 
    = find_matching_task(queue, task);
 
5722
 
  g_assert_nonnull(added_task);
 
5723
 
  g_assert_true(queue->next_run == 1000000 + current_time);
 
5725
 
  g_assert_cmpint(unlink(filename), ==, 0);
 
5726
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
5730
 
void test_connect_question_socket_usable(__attribute__((unused))
 
5731
 
                                         test_fixture *fixture,
 
5732
 
                                         __attribute__((unused))
 
5733
 
                                         gconstpointer user_data){
 
5734
 
  __attribute__((cleanup(cleanup_close)))
 
5735
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5736
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5737
 
  __attribute__((cleanup(cleanup_string)))
 
5738
 
    char *const question_filename = strdup("/nonexistent/question");
 
5739
 
  g_assert_nonnull(question_filename);
 
5740
 
  __attribute__((cleanup(string_set_clear)))
 
5741
 
    string_set cancelled_filenames = {};
 
5742
 
  buffer password = {};
 
5743
 
  bool mandos_client_exited = false;
 
5744
 
  bool password_is_read = false;
 
5745
 
  const mono_microsecs current_time = 0;
 
5746
 
  __attribute__((cleanup(cleanup_queue)))
 
5747
 
    task_queue *queue = create_queue();
 
5748
 
  g_assert_nonnull(queue);
 
5749
 
  __attribute__((cleanup(cleanup_string)))
 
5750
 
    char *tempdir = make_temporary_directory();
 
5751
 
  g_assert_nonnull(tempdir);
 
5752
 
  __attribute__((cleanup(cleanup_close)))
 
5753
 
    const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
 
5754
 
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 
5755
 
  g_assert_cmpint(sock_fd, >=, 0);
 
5756
 
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
 
5757
 
  const char socket_name[] = "socket_name";
 
5758
 
  __attribute__((cleanup(cleanup_string)))
 
5759
 
    char *filename = NULL;
 
5760
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
5762
 
  g_assert_nonnull(filename);
 
5763
 
  g_assert_cmpint((int)strlen(filename), <,
 
5764
 
                  (int)sizeof(sock_name.sun_path));
 
5765
 
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
 
5766
 
  sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
 
5767
 
  g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
 
5768
 
                            (socklen_t)SUN_LEN(&sock_name)), >=, 0);
 
5769
 
  task_context task = {
 
5770
 
    .func=connect_question_socket,
 
5771
 
    .question_filename=strdup(question_filename),
 
5773
 
    .password=&password,
 
5774
 
    .filename=strdup(filename),
 
5775
 
    .cancelled_filenames=&cancelled_filenames,
 
5776
 
    .mandos_client_exited=&mandos_client_exited,
 
5777
 
    .password_is_read=&password_is_read,
 
5778
 
    .current_time=¤t_time,
 
5780
 
  g_assert_nonnull(task.question_filename);
 
5781
 
  task.func(task, queue);
 
5783
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5784
 
  const task_context *const added_task
 
5785
 
    = find_matching_task(queue, (task_context){
 
5786
 
        .func=send_password_to_socket,
 
5787
 
        .question_filename=question_filename,
 
5790
 
        .password=&password,
 
5791
 
        .cancelled_filenames=&cancelled_filenames,
 
5792
 
        .mandos_client_exited=&mandos_client_exited,
 
5793
 
        .password_is_read=&password_is_read,
 
5794
 
        .current_time=¤t_time,
 
5796
 
  g_assert_nonnull(added_task);
 
5797
 
  g_assert_cmpint(added_task->fd, >, 0);
 
5799
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
5802
 
  const int fd = added_task->fd;
 
5803
 
  g_assert_cmpint(fd, >, 0);
 
5804
 
  g_assert_true(fd_has_cloexec_and_nonblock(fd));
 
5807
 
  char write_data[PIPE_BUF];
 
5809
 
    /* Construct test password buffer */
 
5810
 
    /* Start with + since that is what the real procotol uses */
 
5811
 
    write_data[0] = '+';
 
5812
 
    /* Set a special character at string end just to mark the end */
 
5813
 
    write_data[sizeof(write_data)-2] = 'y';
 
5814
 
    /* Set NUL at buffer end, as suggested by the protocol */
 
5815
 
    write_data[sizeof(write_data)-1] = '\0';
 
5816
 
    /* Fill rest of password with 'x' */
 
5817
 
    memset(write_data+1, 'x', sizeof(write_data)-3);
 
5818
 
    g_assert_cmpint((int)send(fd, write_data, sizeof(write_data),
 
5819
 
                              MSG_NOSIGNAL), ==, sizeof(write_data));
 
5822
 
  /* read from sock_fd */
 
5823
 
  char read_data[sizeof(write_data)];
 
5824
 
  g_assert_cmpint((int)read(sock_fd, read_data, sizeof(read_data)),
 
5825
 
                  ==, sizeof(read_data));
 
5827
 
  g_assert_true(memcmp(write_data, read_data, sizeof(write_data))
 
5830
 
  /* writing to sock_fd should fail */
 
5831
 
  g_assert_cmpint(send(sock_fd, write_data, sizeof(write_data),
 
5832
 
                       MSG_NOSIGNAL), <, 0);
 
5834
 
  /* reading from fd should fail */
 
5835
 
  g_assert_cmpint((int)recv(fd, read_data, sizeof(read_data),
 
5836
 
                            MSG_NOSIGNAL), <, 0);
 
5838
 
  g_assert_cmpint(unlink(filename), ==, 0);
 
5839
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
5843
 
test_send_password_to_socket_client_not_exited(__attribute__((unused))
 
5844
 
                                               test_fixture *fixture,
 
5845
 
                                               __attribute__((unused))
 
5848
 
  __attribute__((cleanup(cleanup_close)))
 
5849
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5850
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5851
 
  __attribute__((cleanup(cleanup_string)))
 
5852
 
    char *const question_filename = strdup("/nonexistent/question");
 
5853
 
  g_assert_nonnull(question_filename);
 
5854
 
  __attribute__((cleanup(cleanup_string)))
 
5855
 
    char *const filename = strdup("/nonexistent/socket");
 
5856
 
  g_assert_nonnull(filename);
 
5857
 
  __attribute__((cleanup(string_set_clear)))
 
5858
 
    string_set cancelled_filenames = {};
 
5859
 
  buffer password = {};
 
5860
 
  bool password_is_read = true;
 
5861
 
  __attribute__((cleanup(cleanup_queue)))
 
5862
 
    task_queue *queue = create_queue();
 
5863
 
  g_assert_nonnull(queue);
 
5865
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
5866
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
5868
 
  __attribute__((cleanup(cleanup_close)))
 
5869
 
    const int read_socket = socketfds[0];
 
5870
 
  const int write_socket = socketfds[1];
 
5871
 
  task_context task = {
 
5872
 
    .func=send_password_to_socket,
 
5873
 
    .question_filename=strdup(question_filename),
 
5874
 
    .filename=strdup(filename),
 
5877
 
    .password=&password,
 
5878
 
    .cancelled_filenames=&cancelled_filenames,
 
5879
 
    .mandos_client_exited=(bool[]){false},
 
5880
 
    .password_is_read=&password_is_read,
 
5881
 
    .current_time=(mono_microsecs[]){0},
 
5883
 
  g_assert_nonnull(task.question_filename);
 
5885
 
  task.func(task, queue);
 
5887
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5889
 
  const task_context *const added_task
 
5890
 
    = find_matching_task(queue, task);
 
5891
 
  g_assert_nonnull(added_task);
 
5892
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
5893
 
  g_assert_true(password_is_read);
 
5895
 
  g_assert_cmpint(added_task->fd, >, 0);
 
5896
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
5901
 
test_send_password_to_socket_password_not_read(__attribute__((unused))
 
5902
 
                                               test_fixture *fixture,
 
5903
 
                                               __attribute__((unused))
 
5906
 
  __attribute__((cleanup(cleanup_close)))
 
5907
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5908
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5909
 
  __attribute__((cleanup(cleanup_string)))
 
5910
 
    char *const question_filename = strdup("/nonexistent/question");
 
5911
 
  g_assert_nonnull(question_filename);
 
5912
 
  __attribute__((cleanup(cleanup_string)))
 
5913
 
    char *const filename = strdup("/nonexistent/socket");
 
5914
 
  __attribute__((cleanup(string_set_clear)))
 
5915
 
    string_set cancelled_filenames = {};
 
5916
 
  buffer password = {};
 
5917
 
  __attribute__((cleanup(cleanup_queue)))
 
5918
 
    task_queue *queue = create_queue();
 
5919
 
  g_assert_nonnull(queue);
 
5921
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
5922
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
5924
 
  __attribute__((cleanup(cleanup_close)))
 
5925
 
    const int read_socket = socketfds[0];
 
5926
 
  const int write_socket = socketfds[1];
 
5927
 
  task_context task = {
 
5928
 
    .func=send_password_to_socket,
 
5929
 
    .question_filename=strdup(question_filename),
 
5930
 
    .filename=strdup(filename),
 
5933
 
    .password=&password,
 
5934
 
    .cancelled_filenames=&cancelled_filenames,
 
5935
 
    .mandos_client_exited=(bool[]){false},
 
5936
 
    .password_is_read=(bool[]){false},
 
5937
 
    .current_time=(mono_microsecs[]){0},
 
5939
 
  g_assert_nonnull(task.question_filename);
 
5941
 
  task.func(task, queue);
 
5943
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5945
 
  const task_context *const added_task = find_matching_task(queue,
 
5947
 
  g_assert_nonnull(added_task);
 
5948
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
5949
 
  g_assert_true(queue->next_run == 0);
 
5951
 
  g_assert_cmpint(added_task->fd, >, 0);
 
5952
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
5957
 
void test_send_password_to_socket_EMSGSIZE(__attribute__((unused))
 
5958
 
                                           test_fixture *fixture,
 
5959
 
                                           __attribute__((unused))
 
5960
 
                                           gconstpointer user_data){
 
5962
 
  g_test_skip("Skipping EMSGSIZE test on non-AMD64 platform");
 
5964
 
  __attribute__((cleanup(cleanup_close)))
 
5965
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5966
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5967
 
  const char question_filename[] = "/nonexistent/question";
 
5968
 
  char *const filename = strdup("/nonexistent/socket");
 
5969
 
  __attribute__((cleanup(string_set_clear)))
 
5970
 
    string_set cancelled_filenames = {};
 
5971
 
  const size_t oversized = 1024*1024; /* Limit seems to be 212960 */
 
5972
 
  __attribute__((cleanup(cleanup_buffer)))
 
5974
 
    .data=malloc(oversized),
 
5976
 
    .allocated=oversized,
 
5978
 
  g_assert_nonnull(password.data);
 
5979
 
  if(mlock(password.data, password.allocated) != 0){
 
5980
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
5982
 
  /* Construct test password buffer */
 
5983
 
  /* Start with + since that is what the real procotol uses */
 
5984
 
  password.data[0] = '+';
 
5985
 
  /* Set a special character at string end just to mark the end */
 
5986
 
  password.data[oversized-3] = 'y';
 
5987
 
  /* Set NUL at buffer end, as suggested by the protocol */
 
5988
 
  password.data[oversized-2] = '\0';
 
5989
 
  /* Fill rest of password with 'x' */
 
5990
 
  memset(password.data+1, 'x', oversized-3);
 
5992
 
  __attribute__((cleanup(cleanup_queue)))
 
5993
 
    task_queue *queue = create_queue();
 
5994
 
  g_assert_nonnull(queue);
 
5996
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
5997
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
5999
 
  __attribute__((cleanup(cleanup_close)))
 
6000
 
    const int read_socket = socketfds[0];
 
6001
 
  __attribute__((cleanup(cleanup_close)))
 
6002
 
    const int write_socket = socketfds[1];
 
6003
 
  task_context task = {
 
6004
 
    .func=send_password_to_socket,
 
6005
 
    .question_filename=strdup(question_filename),
 
6009
 
    .password=&password,
 
6010
 
    .cancelled_filenames=&cancelled_filenames,
 
6011
 
    .mandos_client_exited=(bool[]){true},
 
6012
 
    .password_is_read=(bool[]){true},
 
6013
 
    .current_time=(mono_microsecs[]){0},
 
6015
 
  g_assert_nonnull(task.question_filename);
 
6017
 
  run_task_with_stderr_to_dev_null(task, queue);
 
6019
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
6020
 
  g_assert_true(string_set_contains(cancelled_filenames,
 
6021
 
                                    question_filename));
 
6025
 
static void test_send_password_to_socket_retry(__attribute__((unused))
 
6026
 
                                               test_fixture *fixture,
 
6027
 
                                               __attribute__((unused))
 
6030
 
  __attribute__((cleanup(cleanup_close)))
 
6031
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6032
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6033
 
  __attribute__((cleanup(cleanup_string)))
 
6034
 
    char *const question_filename = strdup("/nonexistent/question");
 
6035
 
  g_assert_nonnull(question_filename);
 
6036
 
  __attribute__((cleanup(cleanup_string)))
 
6037
 
    char *const filename = strdup("/nonexistent/socket");
 
6038
 
  g_assert_nonnull(filename);
 
6039
 
  __attribute__((cleanup(string_set_clear)))
 
6040
 
    string_set cancelled_filenames = {};
 
6041
 
  __attribute__((cleanup(cleanup_buffer)))
 
6042
 
    buffer password = {};
 
6044
 
  __attribute__((cleanup(cleanup_queue)))
 
6045
 
    task_queue *queue = create_queue();
 
6046
 
  g_assert_nonnull(queue);
 
6048
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
6049
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
6051
 
  __attribute__((cleanup(cleanup_close)))
 
6052
 
    const int read_socket = socketfds[0];
 
6053
 
  const int write_socket = socketfds[1];
 
6054
 
  /* Close the server side socket to force ECONNRESET on client */
 
6055
 
  g_assert_cmpint(close(read_socket), ==, 0);
 
6056
 
  task_context task = {
 
6057
 
    .func=send_password_to_socket,
 
6058
 
    .question_filename=strdup(question_filename),
 
6059
 
    .filename=strdup(filename),
 
6062
 
    .password=&password,
 
6063
 
    .cancelled_filenames=&cancelled_filenames,
 
6064
 
    .mandos_client_exited=(bool[]){true},
 
6065
 
    .password_is_read=(bool[]){true},
 
6066
 
    .current_time=(mono_microsecs[]){0},
 
6068
 
  g_assert_nonnull(task.question_filename);
 
6070
 
  task.func(task, queue);
 
6072
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
6074
 
  const task_context *const added_task = find_matching_task(queue,
 
6076
 
  g_assert_nonnull(added_task);
 
6077
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
6079
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
6084
 
void test_send_password_to_socket_bad_epoll(__attribute__((unused))
 
6085
 
                                            test_fixture *fixture,
 
6086
 
                                            __attribute__((unused))
 
6087
 
                                            gconstpointer user_data){
 
6088
 
  __attribute__((cleanup(cleanup_close)))
 
6089
 
    const int epoll_fd = open("/dev/null",
 
6090
 
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
6091
 
  __attribute__((cleanup(cleanup_string)))
 
6092
 
    char *const question_filename = strdup("/nonexistent/question");
 
6093
 
  g_assert_nonnull(question_filename);
 
6094
 
  __attribute__((cleanup(cleanup_string)))
 
6095
 
    char *const filename = strdup("/nonexistent/socket");
 
6096
 
  g_assert_nonnull(filename);
 
6097
 
  __attribute__((cleanup(string_set_clear)))
 
6098
 
    string_set cancelled_filenames = {};
 
6099
 
  __attribute__((cleanup(cleanup_buffer)))
 
6100
 
    buffer password = {};
 
6102
 
  const mono_microsecs current_time = 11;
 
6103
 
  __attribute__((cleanup(cleanup_queue)))
 
6104
 
    task_queue *queue = create_queue();
 
6105
 
  g_assert_nonnull(queue);
 
6107
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
6108
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
6110
 
  __attribute__((cleanup(cleanup_close)))
 
6111
 
    const int read_socket = socketfds[0];
 
6112
 
  const int write_socket = socketfds[1];
 
6113
 
  /* Close the server side socket to force ECONNRESET on client */
 
6114
 
  g_assert_cmpint(close(read_socket), ==, 0);
 
6115
 
  task_context task = {
 
6116
 
    .func=send_password_to_socket,
 
6117
 
    .question_filename=strdup(question_filename),
 
6118
 
    .filename=strdup(filename),
 
6121
 
    .password=&password,
 
6122
 
    .cancelled_filenames=&cancelled_filenames,
 
6123
 
    .mandos_client_exited=(bool[]){true},
 
6124
 
    .password_is_read=(bool[]){true},
 
6125
 
    .current_time=¤t_time,
 
6127
 
  g_assert_nonnull(task.question_filename);
 
6129
 
  run_task_with_stderr_to_dev_null(task, queue);
 
6131
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
6133
 
  const task_context *const added_task = find_matching_task(queue,
 
6135
 
  g_assert_nonnull(added_task);
 
6136
 
  g_assert_true(queue->next_run == current_time + 1000000);
 
6137
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
6140
 
static void assert_send_password_to_socket_password(buffer password){
 
6141
 
  __attribute__((cleanup(cleanup_close)))
 
6142
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6143
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6144
 
  char *const question_filename = strdup("/nonexistent/question");
 
6145
 
  g_assert_nonnull(question_filename);
 
6146
 
  char *const filename = strdup("/nonexistent/socket");
 
6147
 
  g_assert_nonnull(filename);
 
6148
 
  __attribute__((cleanup(string_set_clear)))
 
6149
 
    string_set cancelled_filenames = {};
 
6151
 
  __attribute__((cleanup(cleanup_queue)))
 
6152
 
    task_queue *queue = create_queue();
 
6153
 
  g_assert_nonnull(queue);
 
6155
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
6156
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
6158
 
  __attribute__((cleanup(cleanup_close)))
 
6159
 
    const int read_socket = socketfds[0];
 
6160
 
  const int write_socket = socketfds[1];
 
6161
 
  task_context task = {
 
6162
 
    .func=send_password_to_socket,
 
6163
 
    .question_filename=question_filename,
 
6167
 
    .password=&password,
 
6168
 
    .cancelled_filenames=&cancelled_filenames,
 
6169
 
    .mandos_client_exited=(bool[]){true},
 
6170
 
    .password_is_read=(bool[]){true},
 
6171
 
    .current_time=(mono_microsecs[]){0},
 
6174
 
  char *expected_written_data = malloc(password.length + 2);
 
6175
 
  g_assert_nonnull(expected_written_data);
 
6176
 
  expected_written_data[0] = '+';
 
6177
 
  expected_written_data[password.length + 1] = '\0';
 
6178
 
  if(password.length > 0){
 
6179
 
    g_assert_nonnull(password.data);
 
6180
 
    memcpy(expected_written_data + 1, password.data, password.length);
 
6183
 
  task.func(task, queue);
 
6186
 
  g_assert_cmpint((int)read(read_socket, buf, PIPE_BUF), ==,
 
6187
 
                  (int)(password.length + 2));
 
6188
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
6190
 
  g_assert_true(memcmp(expected_written_data, buf,
 
6191
 
                       password.length + 2) == 0);
 
6193
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, write_socket));
 
6195
 
  free(expected_written_data);
 
6199
 
test_send_password_to_socket_null_password(__attribute__((unused))
 
6200
 
                                           test_fixture *fixture,
 
6201
 
                                           __attribute__((unused))
 
6202
 
                                           gconstpointer user_data){
 
6203
 
  __attribute__((cleanup(cleanup_buffer)))
 
6204
 
    buffer password = {};
 
6205
 
  assert_send_password_to_socket_password(password);
 
6209
 
test_send_password_to_socket_empty_password(__attribute__((unused))
 
6210
 
                                            test_fixture *fixture,
 
6211
 
                                            __attribute__((unused))
 
6212
 
                                            gconstpointer user_data){
 
6213
 
  __attribute__((cleanup(cleanup_buffer)))
 
6215
 
    .data=malloc(1),           /* because malloc(0) may return NULL */
 
6217
 
    .allocated=0,               /* deliberate lie */
 
6219
 
  g_assert_nonnull(password.data);
 
6220
 
  assert_send_password_to_socket_password(password);
 
6224
 
test_send_password_to_socket_empty_str_pass(__attribute__((unused))
 
6225
 
                                            test_fixture *fixture,
 
6226
 
                                            __attribute__((unused))
 
6227
 
                                            gconstpointer user_data){
 
6228
 
  __attribute__((cleanup(cleanup_buffer)))
 
6234
 
  if(mlock(password.data, password.allocated) != 0){
 
6235
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
6237
 
  assert_send_password_to_socket_password(password);
 
6241
 
test_send_password_to_socket_text_password(__attribute__((unused))
 
6242
 
                                           test_fixture *fixture,
 
6243
 
                                           __attribute__((unused))
 
6244
 
                                           gconstpointer user_data){
 
6245
 
  const char dummy_test_password[] = "dummy test password";
 
6246
 
  __attribute__((cleanup(cleanup_buffer)))
 
6248
 
    .data = strdup(dummy_test_password),
 
6249
 
    .length = strlen(dummy_test_password),
 
6250
 
    .allocated = sizeof(dummy_test_password),
 
6252
 
  if(mlock(password.data, password.allocated) != 0){
 
6253
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
6255
 
  assert_send_password_to_socket_password(password);
 
6259
 
test_send_password_to_socket_binary_password(__attribute__((unused))
 
6260
 
                                             test_fixture *fixture,
 
6261
 
                                             __attribute__((unused))
 
6262
 
                                             gconstpointer user_data){
 
6263
 
  __attribute__((cleanup(cleanup_buffer)))
 
6269
 
  g_assert_nonnull(password.data);
 
6270
 
  if(mlock(password.data, password.allocated) != 0){
 
6271
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
6273
 
  char c = 1;                   /* Start at 1, avoiding NUL */
 
6274
 
  for(int i=0; i < 255; i++){
 
6275
 
    password.data[i] = c++;
 
6277
 
  assert_send_password_to_socket_password(password);
 
6281
 
test_send_password_to_socket_nuls_in_password(__attribute__((unused))
 
6282
 
                                              test_fixture *fixture,
 
6283
 
                                              __attribute__((unused))
 
6286
 
  char test_password[] = {'\0', 'a', '\0', 'b', '\0', 'c', '\0'};
 
6287
 
  __attribute__((cleanup(cleanup_buffer)))
 
6289
 
    .data=malloc(sizeof(test_password)),
 
6290
 
    .length=sizeof(test_password),
 
6291
 
    .allocated=sizeof(test_password),
 
6293
 
  g_assert_nonnull(password.data);
 
6294
 
  if(mlock(password.data, password.allocated) !=0){
 
6295
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
6297
 
  memcpy(password.data, test_password, password.allocated);
 
6298
 
  assert_send_password_to_socket_password(password);
 
6301
 
static bool assert_add_existing_questions_to_devnull(task_queue
 
6314
 
static void test_add_existing_questions_ENOENT(__attribute__((unused))
 
6315
 
                                               test_fixture *fixture,
 
6316
 
                                               __attribute__((unused))
 
6319
 
  __attribute__((cleanup(cleanup_queue)))
 
6320
 
    task_queue *queue = create_queue();
 
6321
 
  g_assert_nonnull(queue);
 
6322
 
  __attribute__((cleanup(cleanup_close)))
 
6323
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6324
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6325
 
  __attribute__((cleanup(string_set_clear)))
 
6326
 
    string_set cancelled_filenames = {};
 
6328
 
  g_assert_false(assert_add_existing_questions_to_devnull
 
6331
 
                  (buffer[]){{}}, /* password */
 
6332
 
                  &cancelled_filenames,
 
6333
 
                  (mono_microsecs[]){0}, /* current_time */
 
6334
 
                  (bool[]){false},       /* mandos_client_exited */
 
6335
 
                  (bool[]){false},       /* password_is_read */
 
6336
 
                  "/nonexistent"));      /* dirname */
 
6338
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
6342
 
bool assert_add_existing_questions_to_devnull(task_queue
 
6349
 
                                              *cancelled_filenames,
 
6350
 
                                              const mono_microsecs
 
6351
 
                                              *const current_time,
 
6353
 
                                              mandos_client_exited,
 
6358
 
  __attribute__((cleanup(cleanup_close)))
 
6359
 
    const int devnull_fd = open("/dev/null",
 
6360
 
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
6361
 
  g_assert_cmpint(devnull_fd, >=, 0);
 
6362
 
  __attribute__((cleanup(cleanup_close)))
 
6363
 
    const int real_stderr_fd = dup(STDERR_FILENO);
 
6364
 
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
6365
 
  dup2(devnull_fd, STDERR_FILENO);
 
6366
 
  const bool ret = add_existing_questions(queue, epoll_fd, password,
 
6367
 
                                          cancelled_filenames,
 
6369
 
                                          mandos_client_exited,
 
6370
 
                                          password_is_read, dirname);
 
6371
 
  dup2(real_stderr_fd, STDERR_FILENO);
 
6376
 
void test_add_existing_questions_no_questions(__attribute__((unused))
 
6377
 
                                              test_fixture *fixture,
 
6378
 
                                              __attribute__((unused))
 
6381
 
  __attribute__((cleanup(cleanup_queue)))
 
6382
 
    task_queue *queue = create_queue();
 
6383
 
  g_assert_nonnull(queue);
 
6384
 
  __attribute__((cleanup(cleanup_close)))
 
6385
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6386
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6387
 
  __attribute__((cleanup(string_set_clear)))
 
6388
 
    string_set cancelled_filenames = {};
 
6389
 
  __attribute__((cleanup(cleanup_string)))
 
6390
 
    char *tempdir = make_temporary_directory();
 
6391
 
  g_assert_nonnull(tempdir);
 
6393
 
  g_assert_false(assert_add_existing_questions_to_devnull
 
6396
 
                  (buffer[]){{}}, /* password */
 
6397
 
                  &cancelled_filenames,
 
6398
 
                  (mono_microsecs[]){0}, /* current_time */
 
6399
 
                  (bool[]){false},       /* mandos_client_exited */
 
6400
 
                  (bool[]){false},       /* password_is_read */
 
6403
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
6405
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
6408
 
static char *make_question_file_in_directory(const char *const);
 
6411
 
void test_add_existing_questions_one_question(__attribute__((unused))
 
6412
 
                                              test_fixture *fixture,
 
6413
 
                                              __attribute__((unused))
 
6416
 
  __attribute__((cleanup(cleanup_queue)))
 
6417
 
    task_queue *queue = create_queue();
 
6418
 
  g_assert_nonnull(queue);
 
6419
 
  __attribute__((cleanup(cleanup_close)))
 
6420
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6421
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6422
 
  __attribute__((cleanup(cleanup_buffer)))
 
6423
 
    buffer password = {};
 
6424
 
  __attribute__((cleanup(string_set_clear)))
 
6425
 
    string_set cancelled_filenames = {};
 
6426
 
  const mono_microsecs current_time = 0;
 
6427
 
  bool mandos_client_exited = false;
 
6428
 
  bool password_is_read = false;
 
6429
 
  __attribute__((cleanup(cleanup_string)))
 
6430
 
    char *tempdir = make_temporary_directory();
 
6431
 
  g_assert_nonnull(tempdir);
 
6432
 
  __attribute__((cleanup(cleanup_string)))
 
6433
 
    char *question_filename
 
6434
 
    = make_question_file_in_directory(tempdir);
 
6435
 
  g_assert_nonnull(question_filename);
 
6437
 
  g_assert_true(assert_add_existing_questions_to_devnull
 
6441
 
                 &cancelled_filenames,
 
6443
 
                 &mandos_client_exited,
 
6447
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
6449
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
6450
 
        .func=open_and_parse_question,
 
6452
 
        .filename=question_filename,
 
6453
 
        .question_filename=question_filename,
 
6454
 
        .password=&password,
 
6455
 
        .cancelled_filenames=&cancelled_filenames,
 
6456
 
        .current_time=¤t_time,
 
6457
 
        .mandos_client_exited=&mandos_client_exited,
 
6458
 
        .password_is_read=&password_is_read,
 
6461
 
  g_assert_true(queue->next_run == 1);
 
6463
 
  g_assert_cmpint(unlink(question_filename), ==, 0);
 
6464
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
6467
 
static char *make_question_file_in_directory(const char
 
6469
 
  return make_temporary_prefixed_file_in_directory("ask.", dir);
 
6473
 
void test_add_existing_questions_two_questions(__attribute__((unused))
 
6474
 
                                               test_fixture *fixture,
 
6475
 
                                               __attribute__((unused))
 
6478
 
  __attribute__((cleanup(cleanup_queue)))
 
6479
 
    task_queue *queue = create_queue();
 
6480
 
  g_assert_nonnull(queue);
 
6481
 
  __attribute__((cleanup(cleanup_close)))
 
6482
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6483
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6484
 
  __attribute__((cleanup(cleanup_buffer)))
 
6485
 
    buffer password = {};
 
6486
 
  __attribute__((cleanup(string_set_clear)))
 
6487
 
    string_set cancelled_filenames = {};
 
6488
 
  const mono_microsecs current_time = 0;
 
6489
 
  bool mandos_client_exited = false;
 
6490
 
  bool password_is_read = false;
 
6491
 
  __attribute__((cleanup(cleanup_string)))
 
6492
 
    char *tempdir = make_temporary_directory();
 
6493
 
  g_assert_nonnull(tempdir);
 
6494
 
  __attribute__((cleanup(cleanup_string)))
 
6495
 
    char *question_filename1
 
6496
 
    = make_question_file_in_directory(tempdir);
 
6497
 
  g_assert_nonnull(question_filename1);
 
6498
 
  __attribute__((cleanup(cleanup_string)))
 
6499
 
    char *question_filename2
 
6500
 
    = make_question_file_in_directory(tempdir);
 
6501
 
  g_assert_nonnull(question_filename2);
 
6503
 
  g_assert_true(assert_add_existing_questions_to_devnull
 
6507
 
                 &cancelled_filenames,
 
6509
 
                 &mandos_client_exited,
 
6513
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 2);
 
6515
 
  g_assert_true(queue->next_run == 1);
 
6517
 
  __attribute__((cleanup(string_set_clear)))
 
6518
 
    string_set seen_questions = {};
 
6520
 
  bool queue_contains_question_opener(char *const question_filename){
 
6521
 
    return(find_matching_task(queue, (task_context){
 
6522
 
          .func=open_and_parse_question,
 
6524
 
          .question_filename=question_filename,
 
6525
 
          .password=&password,
 
6526
 
          .cancelled_filenames=&cancelled_filenames,
 
6527
 
          .current_time=¤t_time,
 
6528
 
          .mandos_client_exited=&mandos_client_exited,
 
6529
 
          .password_is_read=&password_is_read,
 
6533
 
  g_assert_true(queue_contains_question_opener(question_filename1));
 
6534
 
  g_assert_true(queue_contains_question_opener(question_filename2));
 
6536
 
  g_assert_true(queue->next_run == 1);
 
6538
 
  g_assert_cmpint(unlink(question_filename1), ==, 0);
 
6539
 
  g_assert_cmpint(unlink(question_filename2), ==, 0);
 
6540
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
6544
 
test_add_existing_questions_non_questions(__attribute__((unused))
 
6545
 
                                          test_fixture *fixture,
 
6546
 
                                          __attribute__((unused))
 
6547
 
                                          gconstpointer user_data){
 
6548
 
  __attribute__((cleanup(cleanup_queue)))
 
6549
 
    task_queue *queue = create_queue();
 
6550
 
  g_assert_nonnull(queue);
 
6551
 
  __attribute__((cleanup(cleanup_close)))
 
6552
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6553
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6554
 
  __attribute__((cleanup(string_set_clear)))
 
6555
 
    string_set cancelled_filenames = {};
 
6556
 
  __attribute__((cleanup(cleanup_string)))
 
6557
 
    char *tempdir = make_temporary_directory();
 
6558
 
  g_assert_nonnull(tempdir);
 
6559
 
  __attribute__((cleanup(cleanup_string)))
 
6560
 
    char *question_filename1
 
6561
 
    = make_temporary_file_in_directory(tempdir);
 
6562
 
  g_assert_nonnull(question_filename1);
 
6563
 
  __attribute__((cleanup(cleanup_string)))
 
6564
 
    char *question_filename2
 
6565
 
    = make_temporary_file_in_directory(tempdir);
 
6566
 
  g_assert_nonnull(question_filename2);
 
6568
 
  g_assert_false(assert_add_existing_questions_to_devnull
 
6571
 
                  (buffer[]){{}}, /* password */
 
6572
 
                  &cancelled_filenames,
 
6573
 
                  (mono_microsecs[]){0}, /* current_time */
 
6574
 
                  (bool[]){false},       /* mandos_client_exited */
 
6575
 
                  (bool[]){false},       /* password_is_read */
 
6578
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
6580
 
  g_assert_cmpint(unlink(question_filename1), ==, 0);
 
6581
 
  g_assert_cmpint(unlink(question_filename2), ==, 0);
 
6582
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
6586
 
test_add_existing_questions_both_types(__attribute__((unused))
 
6587
 
                                       test_fixture *fixture,
 
6588
 
                                       __attribute__((unused))
 
6589
 
                                       gconstpointer user_data){
 
6590
 
  __attribute__((cleanup(cleanup_queue)))
 
6591
 
    task_queue *queue = create_queue();
 
6592
 
  g_assert_nonnull(queue);
 
6593
 
  __attribute__((cleanup(cleanup_close)))
 
6594
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6595
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6596
 
  __attribute__((cleanup(cleanup_buffer)))
 
6597
 
    buffer password = {};
 
6598
 
  __attribute__((cleanup(string_set_clear)))
 
6599
 
    string_set cancelled_filenames = {};
 
6600
 
  const mono_microsecs current_time = 0;
 
6601
 
  bool mandos_client_exited = false;
 
6602
 
  bool password_is_read = false;
 
6603
 
  __attribute__((cleanup(cleanup_string)))
 
6604
 
    char *tempdir = make_temporary_directory();
 
6605
 
  g_assert_nonnull(tempdir);
 
6606
 
  __attribute__((cleanup(cleanup_string)))
 
6607
 
    char *tempfilename1 = make_temporary_file_in_directory(tempdir);
 
6608
 
  g_assert_nonnull(tempfilename1);
 
6609
 
  __attribute__((cleanup(cleanup_string)))
 
6610
 
    char *tempfilename2 = make_temporary_file_in_directory(tempdir);
 
6611
 
  g_assert_nonnull(tempfilename2);
 
6612
 
  __attribute__((cleanup(cleanup_string)))
 
6613
 
    char *question_filename
 
6614
 
    = make_question_file_in_directory(tempdir);
 
6615
 
  g_assert_nonnull(question_filename);
 
6617
 
  g_assert_true(assert_add_existing_questions_to_devnull
 
6621
 
                 &cancelled_filenames,
 
6623
 
                 &mandos_client_exited,
 
6627
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
6629
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
6630
 
        .func=open_and_parse_question,
 
6632
 
        .filename=question_filename,
 
6633
 
        .question_filename=question_filename,
 
6634
 
        .password=&password,
 
6635
 
        .cancelled_filenames=&cancelled_filenames,
 
6636
 
        .current_time=¤t_time,
 
6637
 
        .mandos_client_exited=&mandos_client_exited,
 
6638
 
        .password_is_read=&password_is_read,
 
6641
 
  g_assert_true(queue->next_run == 1);
 
6643
 
  g_assert_cmpint(unlink(tempfilename1), ==, 0);
 
6644
 
  g_assert_cmpint(unlink(tempfilename2), ==, 0);
 
6645
 
  g_assert_cmpint(unlink(question_filename), ==, 0);
 
6646
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
6649
 
static void test_wait_for_event_timeout(__attribute__((unused))
 
6650
 
                                        test_fixture *fixture,
 
6651
 
                                        __attribute__((unused))
 
6652
 
                                        gconstpointer user_data){
 
6653
 
  __attribute__((cleanup(cleanup_close)))
 
6654
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6655
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6657
 
  g_assert_true(wait_for_event(epoll_fd, 1, 0));
 
6660
 
static void test_wait_for_event_event(__attribute__((unused))
 
6661
 
                                      test_fixture *fixture,
 
6662
 
                                      __attribute__((unused))
 
6663
 
                                      gconstpointer user_data){
 
6664
 
  __attribute__((cleanup(cleanup_close)))
 
6665
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6666
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6668
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
6669
 
  __attribute__((cleanup(cleanup_close)))
 
6670
 
    const int read_pipe = pipefds[0];
 
6671
 
  __attribute__((cleanup(cleanup_close)))
 
6672
 
    const int write_pipe = pipefds[1];
 
6673
 
  g_assert_cmpint(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, read_pipe,
 
6674
 
                            &(struct epoll_event)
 
6675
 
                            { .events=EPOLLIN | EPOLLRDHUP }), ==, 0);
 
6676
 
  g_assert_cmpint((int)write(write_pipe, "x", 1), ==, 1);
 
6678
 
  g_assert_true(wait_for_event(epoll_fd, 0, 0));
 
6681
 
static void test_wait_for_event_sigchld(test_fixture *fixture,
 
6682
 
                                        __attribute__((unused))
 
6683
 
                                        gconstpointer user_data){
 
6684
 
  const pid_t pid = fork();
 
6685
 
  if(pid == 0){         /* Child */
 
6686
 
    if(not restore_signal_handler(&fixture->orig_sigaction)){
 
6687
 
      _exit(EXIT_FAILURE);
 
6689
 
    if(not restore_sigmask(&fixture->orig_sigmask)){
 
6690
 
      _exit(EXIT_FAILURE);
 
6694
 
  g_assert_true(pid != -1);
 
6695
 
  __attribute__((cleanup(cleanup_close)))
 
6696
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6697
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6699
 
  g_assert_true(wait_for_event(epoll_fd, 0, 0));
 
6702
 
  g_assert_true(waitpid(pid, &status, 0) == pid);
 
6703
 
  g_assert_true(WIFEXITED(status));
 
6704
 
  g_assert_cmpint(WEXITSTATUS(status), ==, EXIT_SUCCESS);
 
6707
 
static void test_run_queue_zeroes_next_run(__attribute__((unused))
 
6708
 
                                           test_fixture *fixture,
 
6709
 
                                           __attribute__((unused))
 
6710
 
                                           gconstpointer user_data){
 
6711
 
  __attribute__((cleanup(cleanup_queue)))
 
6712
 
    task_queue *queue = create_queue();
 
6713
 
  g_assert_nonnull(queue);
 
6714
 
  queue->next_run = 1;
 
6715
 
  __attribute__((cleanup(cleanup_close)))
 
6716
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6717
 
  __attribute__((cleanup(string_set_clear)))
 
6718
 
    string_set cancelled_filenames = {};
 
6719
 
  bool quit_now = false;
 
6721
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6722
 
  g_assert_false(quit_now);
 
6723
 
  g_assert_cmpuint((unsigned int)queue->next_run, ==, 0);
 
6727
 
void test_run_queue_clears_cancelled_filenames(__attribute__((unused))
 
6728
 
                                               test_fixture *fixture,
 
6729
 
                                               __attribute__((unused))
 
6732
 
  __attribute__((cleanup(cleanup_queue)))
 
6733
 
    task_queue *queue = create_queue();
 
6734
 
  g_assert_nonnull(queue);
 
6735
 
  __attribute__((cleanup(string_set_clear)))
 
6736
 
    string_set cancelled_filenames = {};
 
6737
 
  bool quit_now = false;
 
6738
 
  const char question_filename[] = "/nonexistent/question_filename";
 
6739
 
  g_assert_true(string_set_add(&cancelled_filenames,
 
6740
 
                               question_filename));
 
6742
 
  g_assert_true(add_to_queue(queue,
 
6743
 
                             (task_context){ .func=dummy_func }));
 
6745
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6746
 
  g_assert_false(quit_now);
 
6747
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
6748
 
  g_assert_false(string_set_contains(cancelled_filenames,
 
6749
 
                                     question_filename));
 
6753
 
void test_run_queue_skips_cancelled_filenames(__attribute__((unused))
 
6754
 
                                              test_fixture *fixture,
 
6755
 
                                              __attribute__((unused))
 
6758
 
  __attribute__((cleanup(cleanup_queue)))
 
6759
 
    task_queue *queue = create_queue();
 
6760
 
  g_assert_nonnull(queue);
 
6761
 
  __attribute__((cleanup(string_set_clear)))
 
6762
 
    string_set cancelled_filenames = {};
 
6763
 
  bool quit_now = false;
 
6765
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
6766
 
  __attribute__((cleanup(cleanup_close)))
 
6767
 
    const int read_pipe = pipefds[0];
 
6768
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
6769
 
  const char question_filename[] = "/nonexistent/question_filename";
 
6770
 
  g_assert_true(string_set_add(&cancelled_filenames,
 
6771
 
                               question_filename));
 
6772
 
  __attribute__((nonnull))
 
6773
 
    void quit_func(const task_context task,
 
6774
 
                   __attribute__((unused)) task_queue *const q){
 
6775
 
    g_assert_nonnull(task.quit_now);
 
6776
 
    *task.quit_now = true;
 
6778
 
  task_context task = {
 
6780
 
    .question_filename=strdup(question_filename),
 
6781
 
    .quit_now=&quit_now,
 
6784
 
  g_assert_nonnull(task.question_filename);
 
6786
 
  g_assert_true(add_to_queue(queue, task));
 
6788
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6789
 
  g_assert_false(quit_now);
 
6791
 
  /* read_pipe should be closed already */
 
6793
 
  bool read_pipe_closed = (close(read_pipe) == -1);
 
6794
 
  read_pipe_closed &= (errno == EBADF);
 
6795
 
  g_assert_true(read_pipe_closed);
 
6798
 
static void test_run_queue_one_task(__attribute__((unused))
 
6799
 
                                    test_fixture *fixture,
 
6800
 
                                    __attribute__((unused))
 
6801
 
                                    gconstpointer user_data){
 
6802
 
  __attribute__((cleanup(cleanup_queue)))
 
6803
 
    task_queue *queue = create_queue();
 
6804
 
  g_assert_nonnull(queue);
 
6805
 
  __attribute__((cleanup(string_set_clear)))
 
6806
 
    string_set cancelled_filenames = {};
 
6807
 
  bool quit_now = false;
 
6809
 
  __attribute__((nonnull))
 
6810
 
    void next_run_func(__attribute__((unused))
 
6811
 
                       const task_context task,
 
6812
 
                       task_queue *const q){
 
6816
 
  task_context task = {
 
6817
 
    .func=next_run_func,
 
6819
 
  g_assert_true(add_to_queue(queue, task));
 
6821
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6822
 
  g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
 
6823
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
6826
 
static void test_run_queue_two_tasks(__attribute__((unused))
 
6827
 
                                     test_fixture *fixture,
 
6828
 
                                     __attribute__((unused))
 
6829
 
                                     gconstpointer user_data){
 
6830
 
  __attribute__((cleanup(cleanup_queue)))
 
6831
 
    task_queue *queue = create_queue();
 
6832
 
  g_assert_nonnull(queue);
 
6833
 
  queue->next_run = 1;
 
6834
 
  __attribute__((cleanup(string_set_clear)))
 
6835
 
    string_set cancelled_filenames = {};
 
6836
 
  bool quit_now = false;
 
6837
 
  bool mandos_client_exited = false;
 
6839
 
  __attribute__((nonnull))
 
6840
 
    void next_run_func(__attribute__((unused))
 
6841
 
                       const task_context task,
 
6842
 
                       task_queue *const q){
 
6846
 
  __attribute__((nonnull))
 
6847
 
    void exited_func(const task_context task,
 
6848
 
                     __attribute__((unused)) task_queue *const q){
 
6849
 
    *task.mandos_client_exited = true;
 
6852
 
  task_context task1 = {
 
6853
 
    .func=next_run_func,
 
6855
 
  g_assert_true(add_to_queue(queue, task1));
 
6857
 
  task_context task2 = {
 
6859
 
    .mandos_client_exited=&mandos_client_exited,
 
6861
 
  g_assert_true(add_to_queue(queue, task2));
 
6863
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6864
 
  g_assert_false(quit_now);
 
6865
 
  g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
 
6866
 
  g_assert_true(mandos_client_exited);
 
6867
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
6870
 
static void test_run_queue_two_tasks_quit(__attribute__((unused))
 
6871
 
                                          test_fixture *fixture,
 
6872
 
                                          __attribute__((unused))
 
6873
 
                                          gconstpointer user_data){
 
6874
 
  __attribute__((cleanup(cleanup_queue)))
 
6875
 
    task_queue *queue = create_queue();
 
6876
 
  g_assert_nonnull(queue);
 
6877
 
  __attribute__((cleanup(string_set_clear)))
 
6878
 
    string_set cancelled_filenames = {};
 
6879
 
  bool quit_now = false;
 
6880
 
  bool mandos_client_exited = false;
 
6881
 
  bool password_is_read = false;
 
6883
 
  __attribute__((nonnull))
 
6884
 
    void set_exited_func(const task_context task,
 
6885
 
                         __attribute__((unused)) task_queue *const q){
 
6886
 
    *task.mandos_client_exited = true;
 
6887
 
    *task.quit_now = true;
 
6889
 
  task_context task1 = {
 
6890
 
    .func=set_exited_func,
 
6891
 
    .quit_now=&quit_now,
 
6892
 
    .mandos_client_exited=&mandos_client_exited,
 
6894
 
  g_assert_true(add_to_queue(queue, task1));
 
6896
 
  __attribute__((nonnull))
 
6897
 
    void set_read_func(const task_context task,
 
6898
 
                       __attribute__((unused)) task_queue *const q){
 
6899
 
    *task.quit_now = true;
 
6900
 
    *task.password_is_read = true;
 
6902
 
  task_context task2 = {
 
6903
 
    .func=set_read_func,
 
6904
 
    .quit_now=&quit_now,
 
6905
 
    .password_is_read=&password_is_read,
 
6907
 
  g_assert_true(add_to_queue(queue, task2));
 
6909
 
  g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6910
 
  g_assert_true(quit_now);
 
6911
 
  g_assert_true(mandos_client_exited xor password_is_read);
 
6912
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
6915
 
static void test_run_queue_two_tasks_cleanup(__attribute__((unused))
 
6916
 
                                             test_fixture *fixture,
 
6917
 
                                             __attribute__((unused))
 
6918
 
                                             gconstpointer user_data){
 
6919
 
  __attribute__((cleanup(cleanup_queue)))
 
6920
 
    task_queue *queue = create_queue();
 
6921
 
  g_assert_nonnull(queue);
 
6922
 
  __attribute__((cleanup(string_set_clear)))
 
6923
 
    string_set cancelled_filenames = {};
 
6925
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
6926
 
  __attribute__((cleanup(cleanup_close)))
 
6927
 
    const int read_pipe = pipefds[0];
 
6928
 
  __attribute__((cleanup(cleanup_close)))
 
6929
 
    const int write_pipe = pipefds[1];
 
6930
 
  bool quit_now = false;
 
6932
 
  __attribute__((nonnull))
 
6933
 
    void read_func(const task_context task,
 
6934
 
                   __attribute__((unused)) task_queue *const q){
 
6935
 
    *task.quit_now = true;
 
6937
 
  task_context task1 = {
 
6939
 
    .quit_now=&quit_now,
 
6942
 
  g_assert_true(add_to_queue(queue, task1));
 
6944
 
  __attribute__((nonnull))
 
6945
 
    void write_func(const task_context task,
 
6946
 
                    __attribute__((unused)) task_queue *const q){
 
6947
 
    *task.quit_now = true;
 
6949
 
  task_context task2 = {
 
6951
 
    .quit_now=&quit_now,
 
6954
 
  g_assert_true(add_to_queue(queue, task2));
 
6956
 
  g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6957
 
  g_assert_true(quit_now);
 
6959
 
  /* Either read_pipe or write_pipe should be closed already */
 
6961
 
  bool close_read_pipe = (close(read_pipe) == -1);
 
6962
 
  close_read_pipe &= (errno == EBADF);
 
6964
 
  bool close_write_pipe = (close(write_pipe) == -1);
 
6965
 
  close_write_pipe &= (errno == EBADF);
 
6966
 
  g_assert_true(close_read_pipe xor close_write_pipe);
 
6967
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
6970
 
static void test_setup_signal_handler(__attribute__((unused))
 
6971
 
                                      test_fixture *fixture,
 
6972
 
                                      __attribute__((unused))
 
6973
 
                                      gconstpointer user_data){
 
6974
 
  /* Save current SIGCHLD action, whatever it is */
 
6975
 
  struct sigaction expected_sigchld_action;
 
6976
 
  g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
 
6979
 
  /* Act; i.e. run the setup_signal_handler() function */
 
6980
 
  struct sigaction actual_old_sigchld_action;
 
6981
 
  g_assert_true(setup_signal_handler(&actual_old_sigchld_action));
 
6983
 
  /* Check that the function correctly set "actual_old_sigchld_action"
 
6984
 
     to the same values as the previously saved
 
6985
 
     "expected_sigchld_action" */
 
6986
 
  /* Check member sa_handler */
 
6987
 
  g_assert_true(actual_old_sigchld_action.sa_handler
 
6988
 
                == expected_sigchld_action.sa_handler);
 
6989
 
  /* Check member sa_mask */
 
6990
 
  for(int signum = 1; signum < NSIG; signum++){
 
6991
 
    const int expected_old_block_state
 
6992
 
      = sigismember(&expected_sigchld_action.sa_mask, signum);
 
6993
 
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
6994
 
    const int actual_old_block_state
 
6995
 
      = sigismember(&actual_old_sigchld_action.sa_mask, signum);
 
6996
 
    g_assert_cmpint(actual_old_block_state, >=, 0);
 
6997
 
    g_assert_cmpint(actual_old_block_state,
 
6998
 
                    ==, expected_old_block_state);
 
7000
 
  /* Check member sa_flags */
 
7001
 
  g_assert_true((actual_old_sigchld_action.sa_flags
 
7002
 
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
7003
 
                == (expected_sigchld_action.sa_flags
 
7004
 
                    & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
 
7006
 
  /* Retrieve the current signal handler for SIGCHLD as set by
 
7007
 
     setup_signal_handler() */
 
7008
 
  struct sigaction actual_new_sigchld_action;
 
7009
 
  g_assert_cmpint(sigaction(SIGCHLD, NULL,
 
7010
 
                            &actual_new_sigchld_action), ==, 0);
 
7011
 
  /* Check that the signal handler (member sa_handler) is correctly
 
7012
 
     set to the "handle_sigchld" function */
 
7013
 
  g_assert_true(actual_new_sigchld_action.sa_handler != SIG_DFL);
 
7014
 
  g_assert_true(actual_new_sigchld_action.sa_handler != SIG_IGN);
 
7015
 
  g_assert_true(actual_new_sigchld_action.sa_handler
 
7017
 
  /* Check (in member sa_mask) that at least a handful of signals are
 
7018
 
     actually blocked during the signal handler */
 
7019
 
  for(int signum = 1; signum < NSIG; signum++){
 
7020
 
    int actual_new_block_state;
 
7026
 
      actual_new_block_state
 
7027
 
        = sigismember(&actual_new_sigchld_action.sa_mask, signum);
 
7028
 
      g_assert_cmpint(actual_new_block_state, ==, 1);
 
7030
 
    case SIGKILL:               /* non-blockable */
 
7031
 
    case SIGSTOP:               /* non-blockable */
 
7032
 
    case SIGCHLD:               /* always blocked */
 
7037
 
  /* Check member sa_flags */
 
7038
 
  g_assert_true((actual_new_sigchld_action.sa_flags
 
7039
 
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
7040
 
                == (SA_NOCLDSTOP | SA_RESTART));
 
7042
 
  /* Restore signal handler */
 
7043
 
  g_assert_cmpint(sigaction(SIGCHLD, &expected_sigchld_action, NULL),
 
7047
 
static void test_restore_signal_handler(__attribute__((unused))
 
7048
 
                                        test_fixture *fixture,
 
7049
 
                                        __attribute__((unused))
 
7050
 
                                        gconstpointer user_data){
 
7051
 
  /* Save current SIGCHLD action, whatever it is */
 
7052
 
  struct sigaction expected_sigchld_action;
 
7053
 
  g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
 
7055
 
  /* Since we haven't established a signal handler yet, there should
 
7056
 
     not be one established.  But another test may have relied on
 
7057
 
     restore_signal_handler() to restore the signal handler, and if
 
7058
 
     restore_signal_handler() is buggy (which we should be prepared
 
7059
 
     for in this test) the signal handler may not have been restored
 
7060
 
     properly; check for this: */
 
7061
 
  g_assert_true(expected_sigchld_action.sa_handler != handle_sigchld);
 
7063
 
  /* Establish a signal handler */
 
7064
 
  struct sigaction sigchld_action = {
 
7065
 
    .sa_handler=handle_sigchld,
 
7066
 
    .sa_flags=SA_RESTART | SA_NOCLDSTOP,
 
7068
 
  g_assert_cmpint(sigfillset(&sigchld_action.sa_mask), ==, 0);
 
7069
 
  g_assert_cmpint(sigaction(SIGCHLD, &sigchld_action, NULL), ==, 0);
 
7071
 
  /* Act; i.e. run the restore_signal_handler() function */
 
7072
 
  g_assert_true(restore_signal_handler(&expected_sigchld_action));
 
7074
 
  /* Retrieve the restored signal handler data */
 
7075
 
  struct sigaction actual_restored_sigchld_action;
 
7076
 
  g_assert_cmpint(sigaction(SIGCHLD, NULL,
 
7077
 
                            &actual_restored_sigchld_action), ==, 0);
 
7079
 
  /* Check that the function correctly restored the signal action, as
 
7080
 
     saved in "actual_restored_sigchld_action", to the same values as
 
7081
 
     the previously saved "expected_sigchld_action" */
 
7082
 
  /* Check member sa_handler */
 
7083
 
  g_assert_true(actual_restored_sigchld_action.sa_handler
 
7084
 
                == expected_sigchld_action.sa_handler);
 
7085
 
  /* Check member sa_mask */
 
7086
 
  for(int signum = 1; signum < NSIG; signum++){
 
7087
 
    const int expected_old_block_state
 
7088
 
      = sigismember(&expected_sigchld_action.sa_mask, signum);
 
7089
 
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
7090
 
    const int actual_restored_block_state
 
7091
 
      = sigismember(&actual_restored_sigchld_action.sa_mask, signum);
 
7092
 
    g_assert_cmpint(actual_restored_block_state, >=, 0);
 
7093
 
    g_assert_cmpint(actual_restored_block_state,
 
7094
 
                    ==, expected_old_block_state);
 
7096
 
  /* Check member sa_flags */
 
7097
 
  g_assert_true((actual_restored_sigchld_action.sa_flags
 
7098
 
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
7099
 
                == (expected_sigchld_action.sa_flags
 
7100
 
                    & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
 
7103
 
static void test_block_sigchld(__attribute__((unused))
 
7104
 
                               test_fixture *fixture,
 
7105
 
                               __attribute__((unused))
 
7106
 
                               gconstpointer user_data){
 
7107
 
  /* Save original signal mask */
 
7108
 
  sigset_t expected_sigmask;
 
7109
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &expected_sigmask),
 
7112
 
  /* Make sure SIGCHLD is unblocked for this test */
 
7113
 
  sigset_t sigchld_sigmask;
 
7114
 
  g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
 
7115
 
  g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
 
7116
 
  g_assert_cmpint(pthread_sigmask(SIG_UNBLOCK, &sigchld_sigmask,
 
7119
 
  /* Act; i.e. run the block_sigchld() function */
 
7120
 
  sigset_t actual_old_sigmask;
 
7121
 
  g_assert_true(block_sigchld(&actual_old_sigmask));
 
7123
 
  /* Check the actual_old_sigmask; it should be the same as the
 
7124
 
     previously saved signal mask "expected_sigmask". */
 
7125
 
  for(int signum = 1; signum < NSIG; signum++){
 
7126
 
    const int expected_old_block_state
 
7127
 
      = sigismember(&expected_sigmask, signum);
 
7128
 
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
7129
 
    const int actual_old_block_state
 
7130
 
      = sigismember(&actual_old_sigmask, signum);
 
7131
 
    g_assert_cmpint(actual_old_block_state, >=, 0);
 
7132
 
    g_assert_cmpint(actual_old_block_state,
 
7133
 
                    ==, expected_old_block_state);
 
7136
 
  /* Retrieve the newly set signal mask */
 
7137
 
  sigset_t actual_sigmask;
 
7138
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &actual_sigmask), ==, 0);
 
7140
 
  /* SIGCHLD should be blocked */
 
7141
 
  g_assert_cmpint(sigismember(&actual_sigmask, SIGCHLD), ==, 1);
 
7143
 
  /* Restore signal mask */
 
7144
 
  g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &expected_sigmask,
 
7148
 
static void test_restore_sigmask(__attribute__((unused))
 
7149
 
                                 test_fixture *fixture,
 
7150
 
                                 __attribute__((unused))
 
7151
 
                                 gconstpointer user_data){
 
7152
 
  /* Save original signal mask */
 
7153
 
  sigset_t orig_sigmask;
 
7154
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &orig_sigmask), ==, 0);
 
7156
 
  /* Make sure SIGCHLD is blocked for this test */
 
7157
 
  sigset_t sigchld_sigmask;
 
7158
 
  g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
 
7159
 
  g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
 
7160
 
  g_assert_cmpint(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask,
 
7163
 
  /* Act; i.e. run the restore_sigmask() function */
 
7164
 
  g_assert_true(restore_sigmask(&orig_sigmask));
 
7166
 
  /* Retrieve the newly restored signal mask */
 
7167
 
  sigset_t restored_sigmask;
 
7168
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &restored_sigmask),
 
7171
 
  /* Check the restored_sigmask; it should be the same as the
 
7172
 
     previously saved signal mask "orig_sigmask". */
 
7173
 
  for(int signum = 1; signum < NSIG; signum++){
 
7174
 
    const int orig_block_state = sigismember(&orig_sigmask, signum);
 
7175
 
    g_assert_cmpint(orig_block_state, >=, 0);
 
7176
 
    const int restored_block_state = sigismember(&restored_sigmask,
 
7178
 
    g_assert_cmpint(restored_block_state, >=, 0);
 
7179
 
    g_assert_cmpint(restored_block_state, ==, orig_block_state);
 
7182
 
  /* Restore signal mask */
 
7183
 
  g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &orig_sigmask,
 
7187
 
static void test_parse_arguments_noargs(__attribute__((unused))
 
7188
 
                                        test_fixture *fixture,
 
7189
 
                                        __attribute__((unused))
 
7190
 
                                        gconstpointer user_data){
 
7194
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7196
 
  char *agent_directory = NULL;
 
7197
 
  char *helper_directory = NULL;
 
7200
 
  char *mandos_argz = NULL;
 
7201
 
  size_t mandos_argz_length = 0;
 
7203
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7204
 
                                &helper_directory, &user, &group,
 
7205
 
                                &mandos_argz, &mandos_argz_length));
 
7206
 
  g_assert_null(agent_directory);
 
7207
 
  g_assert_null(helper_directory);
 
7208
 
  g_assert_true(user == 0);
 
7209
 
  g_assert_true(group == 0);
 
7210
 
  g_assert_null(mandos_argz);
 
7211
 
  g_assert_true(mandos_argz_length == 0);
 
7213
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7218
 
__attribute__((nonnull))
 
7219
 
static bool parse_arguments_devnull(int argc, char *argv[],
 
7220
 
                                    const bool exit_failure,
 
7221
 
                                    char **agent_directory,
 
7222
 
                                    char **helper_directory,
 
7226
 
                                    size_t *mandos_argz_length){
 
7228
 
  FILE *real_stderr = stderr;
 
7229
 
  FILE *devnull = fopen("/dev/null", "we");
 
7230
 
  g_assert_nonnull(devnull);
 
7233
 
  const bool ret = parse_arguments(argc, argv, exit_failure,
 
7235
 
                                   helper_directory, user, group,
 
7236
 
                                   mandos_argz, mandos_argz_length);
 
7237
 
  const error_t saved_errno = errno;
 
7239
 
  stderr = real_stderr;
 
7240
 
  g_assert_cmpint(fclose(devnull), ==, 0);
 
7242
 
  errno = saved_errno;
 
7247
 
static void test_parse_arguments_invalid(__attribute__((unused))
 
7248
 
                                         test_fixture *fixture,
 
7249
 
                                         __attribute__((unused))
 
7250
 
                                         gconstpointer user_data){
 
7253
 
    strdup("--invalid"),
 
7255
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7257
 
  char *agent_directory = NULL;
 
7258
 
  char *helper_directory = NULL;
 
7261
 
  char *mandos_argz = NULL;
 
7262
 
  size_t mandos_argz_length = 0;
 
7264
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
7266
 
                                         &helper_directory, &user,
 
7267
 
                                         &group, &mandos_argz,
 
7268
 
                                         &mandos_argz_length));
 
7270
 
  g_assert_true(errno == EINVAL);
 
7271
 
  g_assert_null(agent_directory);
 
7272
 
  g_assert_null(helper_directory);
 
7273
 
  g_assert_null(mandos_argz);
 
7274
 
  g_assert_true(mandos_argz_length == 0);
 
7276
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7281
 
static void test_parse_arguments_long_dir(__attribute__((unused))
 
7282
 
                                          test_fixture *fixture,
 
7283
 
                                          __attribute__((unused))
 
7284
 
                                          gconstpointer user_data){
 
7287
 
    strdup("--agent-directory"),
 
7290
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7292
 
  __attribute__((cleanup(cleanup_string)))
 
7293
 
    char *agent_directory = NULL;
 
7294
 
  char *helper_directory = NULL;
 
7297
 
  __attribute__((cleanup(cleanup_string)))
 
7298
 
    char *mandos_argz = NULL;
 
7299
 
  size_t mandos_argz_length = 0;
 
7301
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7302
 
                                &helper_directory, &user, &group,
 
7303
 
                                &mandos_argz, &mandos_argz_length));
 
7305
 
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
7306
 
  g_assert_null(helper_directory);
 
7307
 
  g_assert_true(user == 0);
 
7308
 
  g_assert_true(group == 0);
 
7309
 
  g_assert_null(mandos_argz);
 
7310
 
  g_assert_true(mandos_argz_length == 0);
 
7312
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7317
 
static void test_parse_arguments_short_dir(__attribute__((unused))
 
7318
 
                                           test_fixture *fixture,
 
7319
 
                                           __attribute__((unused))
 
7320
 
                                           gconstpointer user_data){
 
7326
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7328
 
  __attribute__((cleanup(cleanup_string)))
 
7329
 
    char *agent_directory = NULL;
 
7330
 
  char *helper_directory = NULL;
 
7333
 
  __attribute__((cleanup(cleanup_string)))
 
7334
 
    char *mandos_argz = NULL;
 
7335
 
  size_t mandos_argz_length = 0;
 
7337
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7338
 
                                &helper_directory, &user, &group,
 
7339
 
                                &mandos_argz, &mandos_argz_length));
 
7341
 
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
7342
 
  g_assert_null(helper_directory);
 
7343
 
  g_assert_true(user == 0);
 
7344
 
  g_assert_true(group == 0);
 
7345
 
  g_assert_null(mandos_argz);
 
7346
 
  g_assert_true(mandos_argz_length == 0);
 
7348
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7354
 
void test_parse_arguments_helper_directory(__attribute__((unused))
 
7355
 
                                           test_fixture *fixture,
 
7356
 
                                           __attribute__((unused))
 
7357
 
                                           gconstpointer user_data){
 
7360
 
    strdup("--helper-directory"),
 
7363
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7365
 
  char *agent_directory = NULL;
 
7366
 
  __attribute__((cleanup(cleanup_string)))
 
7367
 
    char *helper_directory = NULL;
 
7370
 
  __attribute__((cleanup(cleanup_string)))
 
7371
 
    char *mandos_argz = NULL;
 
7372
 
  size_t mandos_argz_length = 0;
 
7374
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7375
 
                                &helper_directory, &user, &group,
 
7376
 
                                &mandos_argz, &mandos_argz_length));
 
7378
 
  g_assert_cmpstr(helper_directory, ==, "/tmp");
 
7379
 
  g_assert_null(agent_directory);
 
7380
 
  g_assert_true(user == 0);
 
7381
 
  g_assert_true(group == 0);
 
7382
 
  g_assert_null(mandos_argz);
 
7383
 
  g_assert_true(mandos_argz_length == 0);
 
7385
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7391
 
void test_parse_arguments_plugin_helper_dir(__attribute__((unused))
 
7392
 
                                            test_fixture *fixture,
 
7393
 
                                            __attribute__((unused))
 
7394
 
                                            gconstpointer user_data){
 
7397
 
    strdup("--plugin-helper-dir"),
 
7400
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7402
 
  char *agent_directory = NULL;
 
7403
 
  __attribute__((cleanup(cleanup_string)))
 
7404
 
    char *helper_directory = NULL;
 
7407
 
  __attribute__((cleanup(cleanup_string)))
 
7408
 
    char *mandos_argz = NULL;
 
7409
 
  size_t mandos_argz_length = 0;
 
7411
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7412
 
                                &helper_directory, &user, &group,
 
7413
 
                                &mandos_argz, &mandos_argz_length));
 
7415
 
  g_assert_cmpstr(helper_directory, ==, "/tmp");
 
7416
 
  g_assert_null(agent_directory);
 
7417
 
  g_assert_true(user == 0);
 
7418
 
  g_assert_true(group == 0);
 
7419
 
  g_assert_null(mandos_argz);
 
7420
 
  g_assert_true(mandos_argz_length == 0);
 
7422
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7427
 
static void test_parse_arguments_user(__attribute__((unused))
 
7428
 
                                      test_fixture *fixture,
 
7429
 
                                      __attribute__((unused))
 
7430
 
                                      gconstpointer user_data){
 
7436
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7438
 
  char *agent_directory = NULL;
 
7439
 
  __attribute__((cleanup(cleanup_string)))
 
7440
 
    char *helper_directory = NULL;
 
7443
 
  __attribute__((cleanup(cleanup_string)))
 
7444
 
    char *mandos_argz = NULL;
 
7445
 
  size_t mandos_argz_length = 0;
 
7447
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7448
 
                                &helper_directory, &user, &group,
 
7449
 
                                &mandos_argz, &mandos_argz_length));
 
7451
 
  g_assert_null(helper_directory);
 
7452
 
  g_assert_null(agent_directory);
 
7453
 
  g_assert_cmpuint((unsigned int)user, ==, 1000);
 
7454
 
  g_assert_true(group == 0);
 
7455
 
  g_assert_null(mandos_argz);
 
7456
 
  g_assert_true(mandos_argz_length == 0);
 
7458
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7463
 
static void test_parse_arguments_user_invalid(__attribute__((unused))
 
7464
 
                                              test_fixture *fixture,
 
7465
 
                                              __attribute__((unused))
 
7473
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7475
 
  char *agent_directory = NULL;
 
7476
 
  __attribute__((cleanup(cleanup_string)))
 
7477
 
    char *helper_directory = NULL;
 
7480
 
  __attribute__((cleanup(cleanup_string)))
 
7481
 
    char *mandos_argz = NULL;
 
7482
 
  size_t mandos_argz_length = 0;
 
7484
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
7486
 
                                         &helper_directory, &user,
 
7487
 
                                         &group, &mandos_argz,
 
7488
 
                                         &mandos_argz_length));
 
7490
 
  g_assert_null(helper_directory);
 
7491
 
  g_assert_null(agent_directory);
 
7492
 
  g_assert_cmpuint((unsigned int)user, ==, 0);
 
7493
 
  g_assert_true(group == 0);
 
7494
 
  g_assert_null(mandos_argz);
 
7495
 
  g_assert_true(mandos_argz_length == 0);
 
7497
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7503
 
void test_parse_arguments_user_zero_invalid(__attribute__((unused))
 
7504
 
                                            test_fixture *fixture,
 
7505
 
                                            __attribute__((unused))
 
7506
 
                                            gconstpointer user_data){
 
7512
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7514
 
  char *agent_directory = NULL;
 
7515
 
  __attribute__((cleanup(cleanup_string)))
 
7516
 
    char *helper_directory = NULL;
 
7519
 
  __attribute__((cleanup(cleanup_string)))
 
7520
 
    char *mandos_argz = NULL;
 
7521
 
  size_t mandos_argz_length = 0;
 
7523
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
7525
 
                                         &helper_directory, &user,
 
7526
 
                                         &group, &mandos_argz,
 
7527
 
                                         &mandos_argz_length));
 
7529
 
  g_assert_null(helper_directory);
 
7530
 
  g_assert_null(agent_directory);
 
7531
 
  g_assert_cmpuint((unsigned int)user, ==, 0);
 
7532
 
  g_assert_true(group == 0);
 
7533
 
  g_assert_null(mandos_argz);
 
7534
 
  g_assert_true(mandos_argz_length == 0);
 
7536
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7541
 
static void test_parse_arguments_group(__attribute__((unused))
 
7542
 
                                       test_fixture *fixture,
 
7543
 
                                       __attribute__((unused))
 
7544
 
                                       gconstpointer user_data){
 
7550
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7552
 
  char *agent_directory = NULL;
 
7553
 
  __attribute__((cleanup(cleanup_string)))
 
7554
 
    char *helper_directory = NULL;
 
7557
 
  __attribute__((cleanup(cleanup_string)))
 
7558
 
    char *mandos_argz = NULL;
 
7559
 
  size_t mandos_argz_length = 0;
 
7561
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7562
 
                                &helper_directory, &user, &group,
 
7563
 
                                &mandos_argz, &mandos_argz_length));
 
7565
 
  g_assert_null(helper_directory);
 
7566
 
  g_assert_null(agent_directory);
 
7567
 
  g_assert_true(user == 0);
 
7568
 
  g_assert_cmpuint((unsigned int)group, ==, 1000);
 
7569
 
  g_assert_null(mandos_argz);
 
7570
 
  g_assert_true(mandos_argz_length == 0);
 
7572
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7577
 
static void test_parse_arguments_group_invalid(__attribute__((unused))
 
7578
 
                                               test_fixture *fixture,
 
7579
 
                                               __attribute__((unused))
 
7587
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7589
 
  char *agent_directory = NULL;
 
7590
 
  __attribute__((cleanup(cleanup_string)))
 
7591
 
    char *helper_directory = NULL;
 
7594
 
  __attribute__((cleanup(cleanup_string)))
 
7595
 
    char *mandos_argz = NULL;
 
7596
 
  size_t mandos_argz_length = 0;
 
7598
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
7600
 
                                         &helper_directory, &user,
 
7601
 
                                         &group, &mandos_argz,
 
7602
 
                                         &mandos_argz_length));
 
7604
 
  g_assert_null(helper_directory);
 
7605
 
  g_assert_null(agent_directory);
 
7606
 
  g_assert_true(user == 0);
 
7607
 
  g_assert_true(group == 0);
 
7608
 
  g_assert_null(mandos_argz);
 
7609
 
  g_assert_true(mandos_argz_length == 0);
 
7611
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7617
 
void test_parse_arguments_group_zero_invalid(__attribute__((unused))
 
7618
 
                                             test_fixture *fixture,
 
7619
 
                                             __attribute__((unused))
 
7620
 
                                             gconstpointer user_data){
 
7626
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7628
 
  char *agent_directory = NULL;
 
7629
 
  __attribute__((cleanup(cleanup_string)))
 
7630
 
    char *helper_directory = NULL;
 
7633
 
  __attribute__((cleanup(cleanup_string)))
 
7634
 
    char *mandos_argz = NULL;
 
7635
 
  size_t mandos_argz_length = 0;
 
7637
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
7639
 
                                         &helper_directory, &user,
 
7640
 
                                         &group, &mandos_argz,
 
7641
 
                                         &mandos_argz_length));
 
7643
 
  g_assert_null(helper_directory);
 
7644
 
  g_assert_null(agent_directory);
 
7645
 
  g_assert_cmpuint((unsigned int)group, ==, 0);
 
7646
 
  g_assert_true(group == 0);
 
7647
 
  g_assert_null(mandos_argz);
 
7648
 
  g_assert_true(mandos_argz_length == 0);
 
7650
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7655
 
static void test_parse_arguments_mandos_noargs(__attribute__((unused))
 
7656
 
                                               test_fixture *fixture,
 
7657
 
                                               __attribute__((unused))
 
7662
 
    strdup("mandos-client"),
 
7664
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7666
 
  __attribute__((cleanup(cleanup_string)))
 
7667
 
    char *agent_directory = NULL;
 
7668
 
  __attribute__((cleanup(cleanup_string)))
 
7669
 
    char *helper_directory = NULL;
 
7672
 
  __attribute__((cleanup(cleanup_string)))
 
7673
 
    char *mandos_argz = NULL;
 
7674
 
  size_t mandos_argz_length = 0;
 
7676
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7677
 
                                &helper_directory, &user, &group,
 
7678
 
                                &mandos_argz, &mandos_argz_length));
 
7680
 
  g_assert_null(agent_directory);
 
7681
 
  g_assert_null(helper_directory);
 
7682
 
  g_assert_true(user == 0);
 
7683
 
  g_assert_true(group == 0);
 
7684
 
  g_assert_cmpstr(mandos_argz, ==, "mandos-client");
 
7685
 
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
7686
 
                                            mandos_argz_length),
 
7689
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7694
 
static void test_parse_arguments_mandos_args(__attribute__((unused))
 
7695
 
                                             test_fixture *fixture,
 
7696
 
                                             __attribute__((unused))
 
7697
 
                                             gconstpointer user_data){
 
7700
 
    strdup("mandos-client"),
 
7705
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7707
 
  __attribute__((cleanup(cleanup_string)))
 
7708
 
    char *agent_directory = NULL;
 
7709
 
  __attribute__((cleanup(cleanup_string)))
 
7710
 
    char *helper_directory = NULL;
 
7713
 
  __attribute__((cleanup(cleanup_string)))
 
7714
 
    char *mandos_argz = NULL;
 
7715
 
  size_t mandos_argz_length = 0;
 
7717
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7718
 
                                &helper_directory, &user, &group,
 
7719
 
                                &mandos_argz, &mandos_argz_length));
 
7721
 
  g_assert_null(agent_directory);
 
7722
 
  g_assert_null(helper_directory);
 
7723
 
  g_assert_true(user == 0);
 
7724
 
  g_assert_true(group == 0);
 
7725
 
  char *marg = mandos_argz;
 
7726
 
  g_assert_cmpstr(marg, ==, "mandos-client");
 
7727
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7728
 
  g_assert_cmpstr(marg, ==, "one");
 
7729
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7730
 
  g_assert_cmpstr(marg, ==, "two");
 
7731
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7732
 
  g_assert_cmpstr(marg, ==, "three");
 
7733
 
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
7734
 
                                            mandos_argz_length),
 
7737
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7742
 
static void test_parse_arguments_all_args(__attribute__((unused))
 
7743
 
                                          test_fixture *fixture,
 
7744
 
                                          __attribute__((unused))
 
7745
 
                                          gconstpointer user_data){
 
7748
 
    strdup("--agent-directory"),
 
7750
 
    strdup("--helper-directory"),
 
7756
 
    strdup("mandos-client"),
 
7761
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7763
 
  __attribute__((cleanup(cleanup_string)))
 
7764
 
    char *agent_directory = NULL;
 
7765
 
  __attribute__((cleanup(cleanup_string)))
 
7766
 
    char *helper_directory = NULL;
 
7769
 
  __attribute__((cleanup(cleanup_string)))
 
7770
 
    char *mandos_argz = NULL;
 
7771
 
  size_t mandos_argz_length = 0;
 
7773
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7774
 
                                &helper_directory, &user, &group,
 
7775
 
                                &mandos_argz, &mandos_argz_length));
 
7777
 
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
7778
 
  g_assert_cmpstr(helper_directory, ==, "/var/tmp");
 
7779
 
  g_assert_true(user == 1);
 
7780
 
  g_assert_true(group == 2);
 
7781
 
  char *marg = mandos_argz;
 
7782
 
  g_assert_cmpstr(marg, ==, "mandos-client");
 
7783
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7784
 
  g_assert_cmpstr(marg, ==, "one");
 
7785
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7786
 
  g_assert_cmpstr(marg, ==, "two");
 
7787
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7788
 
  g_assert_cmpstr(marg, ==, "three");
 
7789
 
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
7790
 
                                            mandos_argz_length),
 
7793
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7798
 
static void test_parse_arguments_mixed(__attribute__((unused))
 
7799
 
                                       test_fixture *fixture,
 
7800
 
                                       __attribute__((unused))
 
7801
 
                                       gconstpointer user_data){
 
7804
 
    strdup("mandos-client"),
 
7808
 
    strdup("--agent-directory"),
 
7812
 
    strdup("--helper-directory=/var/tmp"),
 
7814
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7816
 
  __attribute__((cleanup(cleanup_string)))
 
7817
 
    char *agent_directory = NULL;
 
7818
 
  __attribute__((cleanup(cleanup_string)))
 
7819
 
    char *helper_directory = NULL;
 
7822
 
  __attribute__((cleanup(cleanup_string)))
 
7823
 
    char *mandos_argz = NULL;
 
7824
 
  size_t mandos_argz_length = 0;
 
7826
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7827
 
                                &helper_directory, &user, &group,
 
7828
 
                                &mandos_argz, &mandos_argz_length));
 
7830
 
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
7831
 
  g_assert_cmpstr(helper_directory, ==, "/var/tmp");
 
7832
 
  g_assert_true(user == 1);
 
7833
 
  g_assert_true(group == 0);
 
7834
 
  char *marg = mandos_argz;
 
7835
 
  g_assert_cmpstr(marg, ==, "mandos-client");
 
7836
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7837
 
  g_assert_cmpstr(marg, ==, "one");
 
7838
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7839
 
  g_assert_cmpstr(marg, ==, "two");
 
7840
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7841
 
  g_assert_cmpstr(marg, ==, "three");
 
7842
 
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
7843
 
                                            mandos_argz_length),
 
7846
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7851
 
/* End of tests section */
 
7853
 
/* Test boilerplate section; New tests should be added to the test
 
7854
 
   suite definition here, in the "run_tests" function.
 
7856
 
   Finally, this section also contains the should_only_run_tests()
 
7857
 
   function used by main() for deciding if tests should be run or to
 
7860
 
__attribute__((cold))
 
7861
 
static bool run_tests(int argc, char *argv[]){
 
7862
 
  g_test_init(&argc, &argv, NULL);
 
7864
 
  /* A macro to add a test with no setup or teardown functions */
 
7865
 
#define test_add(testpath, testfunc)                    \
 
7867
 
    g_test_add((testpath), test_fixture, NULL, NULL,    \
 
7868
 
               (testfunc), NULL);                       \
 
7871
 
  /* Test the signal-related functions first, since some other tests
 
7872
 
     depend on these functions in their setups and teardowns */
 
7873
 
  test_add("/signal-handling/setup", test_setup_signal_handler);
 
7874
 
  test_add("/signal-handling/restore", test_restore_signal_handler);
 
7875
 
  test_add("/signal-handling/block", test_block_sigchld);
 
7876
 
  test_add("/signal-handling/restore-sigmask", test_restore_sigmask);
 
7878
 
  /* Regular non-signal-related tests; these use no setups or
 
7880
 
  test_add("/parse_arguments/noargs", test_parse_arguments_noargs);
 
7881
 
  test_add("/parse_arguments/invalid", test_parse_arguments_invalid);
 
7882
 
  test_add("/parse_arguments/long-dir",
 
7883
 
           test_parse_arguments_long_dir);
 
7884
 
  test_add("/parse_arguments/short-dir",
 
7885
 
           test_parse_arguments_short_dir);
 
7886
 
  test_add("/parse_arguments/helper-directory",
 
7887
 
           test_parse_arguments_helper_directory);
 
7888
 
  test_add("/parse_arguments/plugin-helper-dir",
 
7889
 
           test_parse_arguments_plugin_helper_dir);
 
7890
 
  test_add("/parse_arguments/user", test_parse_arguments_user);
 
7891
 
  test_add("/parse_arguments/user-invalid",
 
7892
 
           test_parse_arguments_user_invalid);
 
7893
 
  test_add("/parse_arguments/user-zero-invalid",
 
7894
 
           test_parse_arguments_user_zero_invalid);
 
7895
 
  test_add("/parse_arguments/group", test_parse_arguments_group);
 
7896
 
  test_add("/parse_arguments/group-invalid",
 
7897
 
           test_parse_arguments_group_invalid);
 
7898
 
  test_add("/parse_arguments/group-zero-invalid",
 
7899
 
           test_parse_arguments_group_zero_invalid);
 
7900
 
  test_add("/parse_arguments/mandos-noargs",
 
7901
 
           test_parse_arguments_mandos_noargs);
 
7902
 
  test_add("/parse_arguments/mandos-args",
 
7903
 
           test_parse_arguments_mandos_args);
 
7904
 
  test_add("/parse_arguments/all-args",
 
7905
 
           test_parse_arguments_all_args);
 
7906
 
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
 
7907
 
  test_add("/queue/create", test_create_queue);
 
7908
 
  test_add("/queue/add", test_add_to_queue);
 
7909
 
  test_add("/queue/add/overflow", test_add_to_queue_overflow);
 
7910
 
  test_add("/queue/has_question/empty",
 
7911
 
           test_queue_has_question_empty);
 
7912
 
  test_add("/queue/has_question/false",
 
7913
 
           test_queue_has_question_false);
 
7914
 
  test_add("/queue/has_question/true", test_queue_has_question_true);
 
7915
 
  test_add("/queue/has_question/false2",
 
7916
 
           test_queue_has_question_false2);
 
7917
 
  test_add("/queue/has_question/true2",
 
7918
 
           test_queue_has_question_true2);
 
7919
 
  test_add("/buffer/cleanup", test_cleanup_buffer);
 
7920
 
  test_add("/string_set/net-set-contains-nothing",
 
7921
 
           test_string_set_new_set_contains_nothing);
 
7922
 
  test_add("/string_set/with-added-string-contains-it",
 
7923
 
           test_string_set_with_added_string_contains_it);
 
7924
 
  test_add("/string_set/cleared-does-not-contain-string",
 
7925
 
           test_string_set_cleared_does_not_contain_str);
 
7926
 
  test_add("/string_set/swap/one-with-empty",
 
7927
 
           test_string_set_swap_one_with_empty);
 
7928
 
  test_add("/string_set/swap/empty-with-one",
 
7929
 
           test_string_set_swap_empty_with_one);
 
7930
 
  test_add("/string_set/swap/one-with-one",
 
7931
 
           test_string_set_swap_one_with_one);
 
7933
 
  /* A macro to add a test using the setup and teardown functions */
 
7934
 
#define test_add_st(path, func)                                 \
 
7936
 
    g_test_add((path), test_fixture, NULL, test_setup, (func),  \
 
7940
 
  /* Signal-related tests; these use setups and teardowns which
 
7941
 
     establish, during each test run, a signal handler for, and a
 
7942
 
     signal mask blocking, the SIGCHLD signal, just like main() */
 
7943
 
  test_add_st("/wait_for_event/timeout", test_wait_for_event_timeout);
 
7944
 
  test_add_st("/wait_for_event/event", test_wait_for_event_event);
 
7945
 
  test_add_st("/wait_for_event/sigchld", test_wait_for_event_sigchld);
 
7946
 
  test_add_st("/run_queue/zeroes-next-run",
 
7947
 
              test_run_queue_zeroes_next_run);
 
7948
 
  test_add_st("/run_queue/clears-cancelled_filenames",
 
7949
 
              test_run_queue_clears_cancelled_filenames);
 
7950
 
  test_add_st("/run_queue/skips-cancelled-filenames",
 
7951
 
              test_run_queue_skips_cancelled_filenames);
 
7952
 
  test_add_st("/run_queue/one-task", test_run_queue_one_task);
 
7953
 
  test_add_st("/run_queue/two-tasks", test_run_queue_two_tasks);
 
7954
 
  test_add_st("/run_queue/two-tasks/quit",
 
7955
 
              test_run_queue_two_tasks_quit);
 
7956
 
  test_add_st("/run_queue/two-tasks-cleanup",
 
7957
 
              test_run_queue_two_tasks_cleanup);
 
7958
 
  test_add_st("/task-creators/start_mandos_client",
 
7959
 
              test_start_mandos_client);
 
7960
 
  test_add_st("/task-creators/start_mandos_client/execv",
 
7961
 
              test_start_mandos_client_execv);
 
7962
 
  test_add_st("/task-creators/start_mandos_client/suid/euid",
 
7963
 
              test_start_mandos_client_suid_euid);
 
7964
 
  test_add_st("/task-creators/start_mandos_client/suid/egid",
 
7965
 
              test_start_mandos_client_suid_egid);
 
7966
 
  test_add_st("/task-creators/start_mandos_client/suid/ruid",
 
7967
 
              test_start_mandos_client_suid_ruid);
 
7968
 
  test_add_st("/task-creators/start_mandos_client/suid/rgid",
 
7969
 
              test_start_mandos_client_suid_rgid);
 
7970
 
  test_add_st("/task-creators/start_mandos_client/read",
 
7971
 
              test_start_mandos_client_read);
 
7972
 
  test_add_st("/task-creators/start_mandos_client/helper-directory",
 
7973
 
              test_start_mandos_client_helper_directory);
 
7974
 
  test_add_st("/task-creators/start_mandos_client/sigmask",
 
7975
 
              test_start_mandos_client_sigmask);
 
7976
 
  test_add_st("/task/wait_for_mandos_client_exit/badpid",
 
7977
 
              test_wait_for_mandos_client_exit_badpid);
 
7978
 
  test_add_st("/task/wait_for_mandos_client_exit/noexit",
 
7979
 
              test_wait_for_mandos_client_exit_noexit);
 
7980
 
  test_add_st("/task/wait_for_mandos_client_exit/success",
 
7981
 
              test_wait_for_mandos_client_exit_success);
 
7982
 
  test_add_st("/task/wait_for_mandos_client_exit/failure",
 
7983
 
              test_wait_for_mandos_client_exit_failure);
 
7984
 
  test_add_st("/task/wait_for_mandos_client_exit/killed",
 
7985
 
              test_wait_for_mandos_client_exit_killed);
 
7986
 
  test_add_st("/task/read_mandos_client_output/readerror",
 
7987
 
              test_read_mandos_client_output_readerror);
 
7988
 
  test_add_st("/task/read_mandos_client_output/nodata",
 
7989
 
              test_read_mandos_client_output_nodata);
 
7990
 
  test_add_st("/task/read_mandos_client_output/eof",
 
7991
 
              test_read_mandos_client_output_eof);
 
7992
 
  test_add_st("/task/read_mandos_client_output/once",
 
7993
 
              test_read_mandos_client_output_once);
 
7994
 
  test_add_st("/task/read_mandos_client_output/malloc",
 
7995
 
              test_read_mandos_client_output_malloc);
 
7996
 
  test_add_st("/task/read_mandos_client_output/append",
 
7997
 
              test_read_mandos_client_output_append);
 
7998
 
  test_add_st("/task-creators/add_inotify_dir_watch",
 
7999
 
              test_add_inotify_dir_watch);
 
8000
 
  test_add_st("/task-creators/add_inotify_dir_watch/fail",
 
8001
 
              test_add_inotify_dir_watch_fail);
 
8002
 
  test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory",
 
8003
 
              test_add_inotify_dir_watch_nondir);
 
8004
 
  test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
 
8005
 
              test_add_inotify_dir_watch_EAGAIN);
 
8006
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
 
8007
 
              test_add_inotify_dir_watch_IN_CLOSE_WRITE);
 
8008
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
 
8009
 
              test_add_inotify_dir_watch_IN_MOVED_TO);
 
8010
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
 
8011
 
              test_add_inotify_dir_watch_IN_MOVED_FROM);
 
8012
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
 
8013
 
              test_add_inotify_dir_watch_IN_EXCL_UNLINK);
 
8014
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
 
8015
 
              test_add_inotify_dir_watch_IN_DELETE);
 
8016
 
  test_add_st("/task/read_inotify_event/readerror",
 
8017
 
              test_read_inotify_event_readerror);
 
8018
 
  test_add_st("/task/read_inotify_event/bad-epoll",
 
8019
 
              test_read_inotify_event_bad_epoll);
 
8020
 
  test_add_st("/task/read_inotify_event/nodata",
 
8021
 
              test_read_inotify_event_nodata);
 
8022
 
  test_add_st("/task/read_inotify_event/eof",
 
8023
 
              test_read_inotify_event_eof);
 
8024
 
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE",
 
8025
 
              test_read_inotify_event_IN_CLOSE_WRITE);
 
8026
 
  test_add_st("/task/read_inotify_event/IN_MOVED_TO",
 
8027
 
              test_read_inotify_event_IN_MOVED_TO);
 
8028
 
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
 
8029
 
              test_read_inotify_event_IN_MOVED_FROM);
 
8030
 
  test_add_st("/task/read_inotify_event/IN_DELETE",
 
8031
 
              test_read_inotify_event_IN_DELETE);
 
8032
 
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
 
8033
 
              test_read_inotify_event_IN_CLOSE_WRITE_badname);
 
8034
 
  test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
 
8035
 
              test_read_inotify_event_IN_MOVED_TO_badname);
 
8036
 
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
 
8037
 
              test_read_inotify_event_IN_MOVED_FROM_badname);
 
8038
 
  test_add_st("/task/read_inotify_event/IN_DELETE/badname",
 
8039
 
              test_read_inotify_event_IN_DELETE_badname);
 
8040
 
  test_add_st("/task/open_and_parse_question/ENOENT",
 
8041
 
              test_open_and_parse_question_ENOENT);
 
8042
 
  test_add_st("/task/open_and_parse_question/EIO",
 
8043
 
              test_open_and_parse_question_EIO);
 
8044
 
  test_add_st("/task/open_and_parse_question/parse-error",
 
8045
 
              test_open_and_parse_question_parse_error);
 
8046
 
  test_add_st("/task/open_and_parse_question/nosocket",
 
8047
 
              test_open_and_parse_question_nosocket);
 
8048
 
  test_add_st("/task/open_and_parse_question/badsocket",
 
8049
 
              test_open_and_parse_question_badsocket);
 
8050
 
  test_add_st("/task/open_and_parse_question/nopid",
 
8051
 
              test_open_and_parse_question_nopid);
 
8052
 
  test_add_st("/task/open_and_parse_question/badpid",
 
8053
 
              test_open_and_parse_question_badpid);
 
8054
 
  test_add_st("/task/open_and_parse_question/noexist_pid",
 
8055
 
              test_open_and_parse_question_noexist_pid);
 
8056
 
  test_add_st("/task/open_and_parse_question/no-notafter",
 
8057
 
              test_open_and_parse_question_no_notafter);
 
8058
 
  test_add_st("/task/open_and_parse_question/bad-notafter",
 
8059
 
              test_open_and_parse_question_bad_notafter);
 
8060
 
  test_add_st("/task/open_and_parse_question/notafter-0",
 
8061
 
              test_open_and_parse_question_notafter_0);
 
8062
 
  test_add_st("/task/open_and_parse_question/notafter-1",
 
8063
 
              test_open_and_parse_question_notafter_1);
 
8064
 
  test_add_st("/task/open_and_parse_question/notafter-1-1",
 
8065
 
              test_open_and_parse_question_notafter_1_1);
 
8066
 
  test_add_st("/task/open_and_parse_question/notafter-1-2",
 
8067
 
              test_open_and_parse_question_notafter_1_2);
 
8068
 
  test_add_st("/task/open_and_parse_question/equal-notafter",
 
8069
 
              test_open_and_parse_question_equal_notafter);
 
8070
 
  test_add_st("/task/open_and_parse_question/late-notafter",
 
8071
 
              test_open_and_parse_question_late_notafter);
 
8072
 
  test_add_st("/task/cancel_old_question/0-1-2",
 
8073
 
              test_cancel_old_question_0_1_2);
 
8074
 
  test_add_st("/task/cancel_old_question/0-2-1",
 
8075
 
              test_cancel_old_question_0_2_1);
 
8076
 
  test_add_st("/task/cancel_old_question/1-2-3",
 
8077
 
              test_cancel_old_question_1_2_3);
 
8078
 
  test_add_st("/task/cancel_old_question/1-3-2",
 
8079
 
              test_cancel_old_question_1_3_2);
 
8080
 
  test_add_st("/task/cancel_old_question/2-1-3",
 
8081
 
              test_cancel_old_question_2_1_3);
 
8082
 
  test_add_st("/task/cancel_old_question/2-3-1",
 
8083
 
              test_cancel_old_question_2_3_1);
 
8084
 
  test_add_st("/task/cancel_old_question/3-1-2",
 
8085
 
              test_cancel_old_question_3_1_2);
 
8086
 
  test_add_st("/task/cancel_old_question/3-2-1",
 
8087
 
              test_cancel_old_question_3_2_1);
 
8088
 
  test_add_st("/task/connect_question_socket/name-too-long",
 
8089
 
              test_connect_question_socket_name_too_long);
 
8090
 
  test_add_st("/task/connect_question_socket/connect-fail",
 
8091
 
              test_connect_question_socket_connect_fail);
 
8092
 
  test_add_st("/task/connect_question_socket/bad-epoll",
 
8093
 
              test_connect_question_socket_bad_epoll);
 
8094
 
  test_add_st("/task/connect_question_socket/usable",
 
8095
 
              test_connect_question_socket_usable);
 
8096
 
  test_add_st("/task/send_password_to_socket/client-not-exited",
 
8097
 
              test_send_password_to_socket_client_not_exited);
 
8098
 
  test_add_st("/task/send_password_to_socket/password-not-read",
 
8099
 
              test_send_password_to_socket_password_not_read);
 
8100
 
  test_add_st("/task/send_password_to_socket/EMSGSIZE",
 
8101
 
              test_send_password_to_socket_EMSGSIZE);
 
8102
 
  test_add_st("/task/send_password_to_socket/retry",
 
8103
 
              test_send_password_to_socket_retry);
 
8104
 
  test_add_st("/task/send_password_to_socket/bad-epoll",
 
8105
 
              test_send_password_to_socket_bad_epoll);
 
8106
 
  test_add_st("/task/send_password_to_socket/null-password",
 
8107
 
              test_send_password_to_socket_null_password);
 
8108
 
  test_add_st("/task/send_password_to_socket/empty-password",
 
8109
 
              test_send_password_to_socket_empty_password);
 
8110
 
  test_add_st("/task/send_password_to_socket/empty-str-password",
 
8111
 
              test_send_password_to_socket_empty_str_pass);
 
8112
 
  test_add_st("/task/send_password_to_socket/text-password",
 
8113
 
              test_send_password_to_socket_text_password);
 
8114
 
  test_add_st("/task/send_password_to_socket/binary-password",
 
8115
 
              test_send_password_to_socket_binary_password);
 
8116
 
  test_add_st("/task/send_password_to_socket/nuls-in-password",
 
8117
 
              test_send_password_to_socket_nuls_in_password);
 
8118
 
  test_add_st("/task-creators/add_existing_questions/ENOENT",
 
8119
 
              test_add_existing_questions_ENOENT);
 
8120
 
  test_add_st("/task-creators/add_existing_questions/no-questions",
 
8121
 
              test_add_existing_questions_no_questions);
 
8122
 
  test_add_st("/task-creators/add_existing_questions/one-question",
 
8123
 
              test_add_existing_questions_one_question);
 
8124
 
  test_add_st("/task-creators/add_existing_questions/two-questions",
 
8125
 
              test_add_existing_questions_two_questions);
 
8126
 
  test_add_st("/task-creators/add_existing_questions/non-questions",
 
8127
 
              test_add_existing_questions_non_questions);
 
8128
 
  test_add_st("/task-creators/add_existing_questions/both-types",
 
8129
 
              test_add_existing_questions_both_types);
 
8131
 
  return g_test_run() == 0;
 
8134
 
static bool should_only_run_tests(int *argc_p, char **argv_p[]){
 
8135
 
  GOptionContext *context = g_option_context_new("");
 
8137
 
  g_option_context_set_help_enabled(context, FALSE);
 
8138
 
  g_option_context_set_ignore_unknown_options(context, TRUE);
 
8140
 
  gboolean should_run_tests = FALSE;
 
8141
 
  GOptionEntry entries[] = {
 
8142
 
    { "test", 0, 0, G_OPTION_ARG_NONE,
 
8143
 
      &should_run_tests, "Run tests", NULL },
 
8146
 
  g_option_context_add_main_entries(context, entries, NULL);
 
8148
 
  GError *error = NULL;
 
8150
 
  if(g_option_context_parse(context, argc_p, argv_p, &error) != TRUE){
 
8151
 
    g_option_context_free(context);
 
8152
 
    g_error("Failed to parse options: %s", error->message);
 
8155
 
  g_option_context_free(context);
 
8156
 
  return should_run_tests != FALSE;