/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to dracut-module/password-agent.c

  • Committer: Teddy Hogeborn
  • Date: 2012-06-17 22:58:07 UTC
  • mfrom: (237.4.33 release)
  • Revision ID: teddy@recompile.se-20120617225807-6o4z294a5fxfvlyf
Merge from release branch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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)))))); -*- */
2
 
/*
3
 
 * Mandos password agent - Simple password agent to run Mandos client
4
 
 *
5
 
 * Copyright © 2019 Teddy Hogeborn
6
 
 * Copyright © 2019 Björn Påhlsson
7
 
 * 
8
 
 * This file is part of Mandos.
9
 
 * 
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.
14
 
 * 
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.
19
 
 * 
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/>.
22
 
 * 
23
 
 * Contact the authors at <mandos@recompile.se>.
24
 
 */
25
 
 
26
 
#define _GNU_SOURCE
27
 
#include <inttypes.h>           /* uintmax_t, PRIuMAX, PRIdMAX,
28
 
                                   intmax_t, uint32_t, SCNx32,
29
 
                                   SCNuMAX, SCNxMAX */
30
 
#include <stddef.h>             /* size_t */
31
 
#include <sys/types.h>          /* pid_t, uid_t, gid_t, getuid(),
32
 
                                   getpid() */
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, EEXIST,
52
 
                                   ECHILD, EPERM, ENOMEM, EAGAIN,
53
 
                                   EINTR, ENOBUFS, EADDRINUSE,
54
 
                                   ECONNREFUSED, ECONNRESET,
55
 
                                   ETOOMANYREFS, EMSGSIZE, EBADF,
56
 
                                   EINVAL */
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(),
63
 
                                   argz_add() */
64
 
#include <sys/epoll.h>          /* epoll_create1(), EPOLL_CLOEXEC,
65
 
                                   epoll_ctl(), EPOLL_CTL_ADD,
66
 
                                   struct epoll_event, EPOLLIN,
67
 
                                   EPOLLRDHUP, EPOLLOUT,
68
 
                                   epoll_pwait() */
69
 
#include <time.h>               /* struct timespec, clock_gettime(),
70
 
                                   CLOCK_MONOTONIC */
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(),
75
 
                                   ARGP_NO_EXIT */
76
 
#include <unistd.h>             /* uid_t, gid_t, close(), pipe2(),
77
 
                                   fork(), _exit(), dup2(),
78
 
                                   STDOUT_FILENO, setresgid(),
79
 
                                   setresuid(), execv(), ssize_t,
80
 
                                   read(), dup3(), getuid(), dup(),
81
 
                                   STDERR_FILENO, pause(), write(),
82
 
                                   rmdir(), unlink(), getpid() */
83
 
#include <sys/mman.h>           /* munlock(), mlock() */
84
 
#include <fcntl.h>              /* O_CLOEXEC, O_NONBLOCK, fcntl(),
85
 
                                   F_GETFD, F_GETFL, FD_CLOEXEC,
86
 
                                   open(), O_WRONLY, O_RDONLY */
87
 
#include <sys/wait.h>           /* waitpid(), WNOHANG, WIFEXITED(),
88
 
                                   WEXITSTATUS() */
89
 
#include <limits.h>             /* PIPE_BUF, NAME_MAX, INT_MAX */
90
 
#include <sys/inotify.h>        /* inotify_init1(), IN_NONBLOCK,
91
 
                                   IN_CLOEXEC, inotify_add_watch(),
92
 
                                   IN_CLOSE_WRITE, IN_MOVED_TO,
93
 
                                   IN_DELETE, struct inotify_event */
94
 
#include <fnmatch.h>            /* fnmatch(), FNM_FILE_NAME */
95
 
#include <stdio.h>              /* asprintf(), FILE, fopen(),
96
 
                                   getline(), sscanf(), feof(),
97
 
                                   ferror(), fclose(), stderr,
98
 
                                   rename(), fdopen(), fprintf(),
99
 
                                   fscanf() */
100
 
#include <glib.h>    /* GKeyFile, g_key_file_free(), g_key_file_new(),
101
 
                        GError, g_key_file_load_from_file(),
102
 
                        G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
103
 
                        g_key_file_get_string(), guint64,
104
 
                        g_key_file_get_uint64(),
105
 
                        G_KEY_FILE_ERROR_KEY_NOT_FOUND, gconstpointer,
106
 
                        g_assert_true(), g_assert_nonnull(),
107
 
                        g_assert_null(), g_assert_false(),
108
 
                        g_assert_cmpint(), g_assert_cmpuint(),
109
 
                        g_test_skip(), g_assert_cmpstr(),
110
 
                        g_test_init(), g_test_add(), g_test_run(),
111
 
                        GOptionContext, g_option_context_new(),
112
 
                        g_option_context_set_help_enabled(), FALSE,
113
 
                        g_option_context_set_ignore_unknown_options(),
114
 
                        gboolean, GOptionEntry, G_OPTION_ARG_NONE,
115
 
                        g_option_context_add_main_entries(),
116
 
                        g_option_context_parse(),
117
 
                        g_option_context_free(), g_error() */
118
 
#include <sys/un.h>             /* struct sockaddr_un, SUN_LEN */
119
 
#include <sys/socket.h>         /* AF_LOCAL, socket(), PF_LOCAL,
120
 
                                   SOCK_DGRAM, SOCK_NONBLOCK,
121
 
                                   SOCK_CLOEXEC, connect(),
122
 
                                   struct sockaddr, socklen_t,
123
 
                                   shutdown(), SHUT_RD, send(),
124
 
                                   MSG_NOSIGNAL, bind(), recv(),
125
 
                                   socketpair() */
126
 
#include <glob.h>               /* globfree(), glob_t, glob(),
127
 
                                   GLOB_ERR, GLOB_NOSORT, GLOB_MARK,
128
 
                                   GLOB_ABORTED, GLOB_NOMATCH,
129
 
                                   GLOB_NOSPACE */
130
 
 
131
 
/* End of includes */
132
 
 
133
 
/* Start of declarations of private types and functions */
134
 
 
135
 
/* microseconds of CLOCK_MONOTONIC absolute time; 0 means unset */
136
 
typedef uintmax_t mono_microsecs;
137
 
 
138
 
/* "task_queue" - A queue of tasks to be run */
139
 
typedef struct {
140
 
  struct task_struct *tasks;    /* Tasks in this queue */
141
 
  size_t length;                /* Number of tasks */
142
 
  /* Memory allocated for "tasks", in bytes */
143
 
  size_t allocated;             
144
 
  /* Time when this queue should be run, at the latest */
145
 
  mono_microsecs next_run;
146
 
} __attribute__((designated_init)) task_queue;
147
 
 
148
 
/* "func_type" - A function type for task functions
149
 
 
150
 
   I.e. functions for the code which runs when a task is run, all have
151
 
   this type */
152
 
typedef void (task_func) (const struct task_struct,
153
 
                          task_queue *const)
154
 
  __attribute__((nonnull));
155
 
 
156
 
/* "buffer" - A data buffer for a growing array of bytes
157
 
 
158
 
   Used for the "password" variable */
159
 
typedef struct {
160
 
  char *data;
161
 
  size_t length;
162
 
  size_t allocated;
163
 
} __attribute__((designated_init)) buffer;
164
 
 
165
 
/* "string_set" - A set type which can contain strings
166
 
 
167
 
   Used by the "cancelled_filenames" variable */
168
 
typedef struct {
169
 
  char *argz;                   /* Do not access these except in */
170
 
  size_t argz_len;              /* the string_set_* functions */
171
 
} __attribute__((designated_init)) string_set;
172
 
 
173
 
/* "task_context" - local variables for tasks
174
 
 
175
 
   This data structure distinguishes between different tasks which are
176
 
   using the same function.  This data structure is passed to every
177
 
   task function when each task is run.
178
 
 
179
 
   Note that not every task uses every struct member. */
180
 
typedef struct task_struct {
181
 
  task_func *const func;         /* The function run by this task */
182
 
  char *const question_filename; /* The question file */
183
 
  const pid_t pid;               /* Mandos client process ID */
184
 
  const int epoll_fd;            /* The epoll set file descriptor */
185
 
  bool *const quit_now;          /* Set to true on fatal errors */
186
 
  const int fd;                  /* General purpose file descriptor */
187
 
  bool *const mandos_client_exited; /* Set true when client exits */
188
 
  buffer *const password;           /* As read from client process */
189
 
  bool *const password_is_read;     /* "password" is done growing */
190
 
  char *filename;                   /* General purpose file name */
191
 
  /* A set of strings of all the file names of questions which have
192
 
     been cancelled for any reason; tasks pertaining to these question
193
 
     files should not be run */
194
 
  string_set *const cancelled_filenames;
195
 
  const mono_microsecs notafter; /* "NotAfter" from question file */
196
 
  /* Updated before each queue run; is compared with queue.next_run */
197
 
  const mono_microsecs *const current_time;
198
 
} __attribute__((designated_init)) task_context;
199
 
 
200
 
/* Declare all our functions here so we can define them in any order
201
 
   below.  Note: test functions are *not* declared here, they are
202
 
   declared in the test section. */
203
 
__attribute__((warn_unused_result))
204
 
static bool should_only_run_tests(int *, char **[]);
205
 
__attribute__((warn_unused_result, cold))
206
 
static bool run_tests(int, char *[]);
207
 
static void handle_sigchld(__attribute__((unused)) int sig){}
208
 
__attribute__((warn_unused_result, malloc))
209
 
task_queue *create_queue(void);
210
 
__attribute__((nonnull, warn_unused_result))
211
 
bool add_to_queue(task_queue *const, const task_context);
212
 
__attribute__((nonnull))
213
 
void cleanup_task(const task_context *const);
214
 
__attribute__((nonnull))
215
 
void cleanup_queue(task_queue *const *const);
216
 
__attribute__((pure, nonnull, warn_unused_result))
217
 
bool queue_has_question(const task_queue *const);
218
 
__attribute__((nonnull))
219
 
void cleanup_close(const int *const);
220
 
__attribute__((nonnull))
221
 
void cleanup_string(char *const *const);
222
 
__attribute__((nonnull))
223
 
void cleanup_buffer(buffer *const);
224
 
__attribute__((pure, nonnull, warn_unused_result))
225
 
bool string_set_contains(const string_set, const char *const);
226
 
__attribute__((nonnull, warn_unused_result))
227
 
bool string_set_add(string_set *const, const char *const);
228
 
__attribute__((nonnull))
229
 
void string_set_clear(string_set *);
230
 
void string_set_swap(string_set *const, string_set *const);
231
 
__attribute__((nonnull, warn_unused_result))
232
 
bool start_mandos_client(task_queue *const, const int, bool *const,
233
 
                         bool *const, buffer *const, bool *const,
234
 
                         const struct sigaction *const,
235
 
                         const sigset_t, const char *const,
236
 
                         const uid_t, const gid_t,
237
 
                         const char *const *const);
238
 
__attribute__((nonnull))
239
 
task_func wait_for_mandos_client_exit;
240
 
__attribute__((nonnull))
241
 
task_func read_mandos_client_output;
242
 
__attribute__((warn_unused_result))
243
 
bool add_inotify_dir_watch(task_queue *const, const int, bool *const,
244
 
                           buffer *const, const char *const,
245
 
                           string_set *, const mono_microsecs *const,
246
 
                           bool *const, bool *const);
247
 
__attribute__((nonnull))
248
 
task_func read_inotify_event;
249
 
__attribute__((nonnull))
250
 
task_func open_and_parse_question;
251
 
__attribute__((nonnull))
252
 
task_func cancel_old_question;
253
 
__attribute__((nonnull))
254
 
task_func connect_question_socket;
255
 
__attribute__((nonnull))
256
 
task_func send_password_to_socket;
257
 
__attribute__((warn_unused_result))
258
 
bool add_existing_questions(task_queue *const, const int,
259
 
                            buffer *const, string_set *,
260
 
                            const mono_microsecs *const,
261
 
                            bool *const, bool *const,
262
 
                            const char *const);
263
 
__attribute__((nonnull, warn_unused_result))
264
 
bool wait_for_event(const int, const mono_microsecs,
265
 
                    const mono_microsecs);
266
 
bool run_queue(task_queue **const, string_set *const, bool *const);
267
 
bool clear_all_fds_from_epoll_set(const int);
268
 
mono_microsecs get_current_time(void);
269
 
__attribute__((nonnull, warn_unused_result))
270
 
bool setup_signal_handler(struct sigaction *const);
271
 
__attribute__((nonnull))
272
 
bool restore_signal_handler(const struct sigaction *const);
273
 
__attribute__((nonnull, warn_unused_result))
274
 
bool block_sigchld(sigset_t *const);
275
 
__attribute__((nonnull))
276
 
bool restore_sigmask(const sigset_t *const);
277
 
__attribute__((nonnull))
278
 
bool parse_arguments(int, char *[], const bool, char **, char **,
279
 
                     uid_t *const , gid_t *const, char **, size_t *);
280
 
 
281
 
/* End of declarations of private types and functions */
282
 
 
283
 
/* Start of "main" section; this section LACKS TESTS!
284
 
 
285
 
   Code here should be as simple as possible. */
286
 
 
287
 
/* These are required to be global by Argp */
288
 
const char *argp_program_version = "password-agent " VERSION;
289
 
const char *argp_program_bug_address = "<mandos@recompile.se>";
290
 
 
291
 
int main(int argc, char *argv[]){
292
 
 
293
 
  /* If the --test option is passed, skip all normal operations and
294
 
     instead only run the run_tests() function, which also does all
295
 
     its own option parsing, so we don't have to do anything here. */
296
 
  if(should_only_run_tests(&argc, &argv)){
297
 
    if(run_tests(argc, argv)){
298
 
      return EXIT_SUCCESS;      /* All tests successful */
299
 
    }
300
 
    return EXIT_FAILURE;        /* Some test(s) failed */
301
 
  }
302
 
 
303
 
  __attribute__((cleanup(cleanup_string)))
304
 
    char *agent_directory = NULL;
305
 
 
306
 
  __attribute__((cleanup(cleanup_string)))
307
 
    char *helper_directory = NULL;
308
 
 
309
 
  uid_t user = 0;
310
 
  gid_t group = 0;
311
 
 
312
 
  __attribute__((cleanup(cleanup_string)))
313
 
    char *mandos_argz = NULL;
314
 
  size_t mandos_argz_length = 0;
315
 
 
316
 
  if(not parse_arguments(argc, argv, true, &agent_directory,
317
 
                         &helper_directory, &user, &group,
318
 
                         &mandos_argz, &mandos_argz_length)){
319
 
    /* This should never happen, since "true" is passed as the third
320
 
       argument to parse_arguments() above, which should make
321
 
       argp_parse() call exit() if any parsing error occurs. */
322
 
    error(EX_USAGE, errno, "Failed to parse arguments");
323
 
  }
324
 
 
325
 
  const char default_agent_directory[] = "/run/systemd/ask-password";
326
 
  const char default_helper_directory[]
327
 
    = "/lib/mandos/plugin-helpers";
328
 
  const char *const default_argv[]
329
 
    = {"/lib/mandos/plugins.d/mandos-client", NULL };
330
 
 
331
 
  /* Set variables to default values if unset */
332
 
  if(agent_directory == NULL){
333
 
    agent_directory = strdup(default_agent_directory);
334
 
    if(agent_directory == NULL){
335
 
      error(EX_OSERR, errno, "Failed strdup()");
336
 
    }
337
 
  }
338
 
  if(helper_directory == NULL){
339
 
    helper_directory = strdup(default_helper_directory);
340
 
    if(helper_directory == NULL){
341
 
      error(EX_OSERR, errno, "Failed strdup()");
342
 
    }
343
 
  }
344
 
  if(user == 0){
345
 
    user = 65534;               /* nobody */
346
 
  }
347
 
  if(group == 0){
348
 
    group = 65534;              /* nogroup */
349
 
  }
350
 
  /* If parse_opt did not create an argz vector, create one with
351
 
     default values */
352
 
  if(mandos_argz == NULL){
353
 
#ifdef __GNUC__
354
 
#pragma GCC diagnostic push
355
 
    /* argz_create() takes a non-const argv for some unknown reason -
356
 
       argz_create() isn't modifying the strings, just copying them.
357
 
       Therefore, this cast to non-const should be safe. */
358
 
#pragma GCC diagnostic ignored "-Wcast-qual"
359
 
#endif
360
 
    errno = argz_create((char *const *)default_argv, &mandos_argz,
361
 
                        &mandos_argz_length);
362
 
#ifdef __GNUC__
363
 
#pragma GCC diagnostic pop
364
 
#endif
365
 
    if(errno != 0){
366
 
      error(EX_OSERR, errno, "Failed argz_create()");
367
 
    }
368
 
  }
369
 
  /* Use argz vector to create a normal argv, usable by execv() */
370
 
 
371
 
  char **mandos_argv = malloc((argz_count(mandos_argz,
372
 
                                          mandos_argz_length)
373
 
                               + 1) * sizeof(char *));
374
 
  if(mandos_argv == NULL){
375
 
    error_t saved_errno = errno;
376
 
    free(mandos_argz);
377
 
    error(EX_OSERR, saved_errno, "Failed malloc()");
378
 
  }
379
 
  argz_extract(mandos_argz, mandos_argz_length, mandos_argv);
380
 
 
381
 
  sigset_t orig_sigmask;
382
 
  if(not block_sigchld(&orig_sigmask)){
383
 
    return EX_OSERR;
384
 
  }
385
 
 
386
 
  struct sigaction old_sigchld_action;
387
 
  if(not setup_signal_handler(&old_sigchld_action)){
388
 
    return EX_OSERR;
389
 
  }
390
 
 
391
 
  mono_microsecs current_time = 0;
392
 
 
393
 
  bool mandos_client_exited = false;
394
 
  bool quit_now = false;
395
 
  __attribute__((cleanup(cleanup_close)))
396
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
397
 
  if(epoll_fd < 0){
398
 
    error(EX_OSERR, errno, "Failed to create epoll set fd");
399
 
  }
400
 
  __attribute__((cleanup(cleanup_queue)))
401
 
    task_queue *queue = create_queue();
402
 
  if(queue == NULL){
403
 
    error(EX_OSERR, errno, "Failed to create task queue");
404
 
  }
405
 
 
406
 
  __attribute__((cleanup(cleanup_buffer)))
407
 
    buffer password = {};
408
 
  bool password_is_read = false;
409
 
 
410
 
  __attribute__((cleanup(string_set_clear)))
411
 
    string_set cancelled_filenames = {};
412
 
 
413
 
  /* Add tasks to queue */
414
 
  if(not start_mandos_client(queue, epoll_fd, &mandos_client_exited,
415
 
                             &quit_now, &password, &password_is_read,
416
 
                             &old_sigchld_action, orig_sigmask,
417
 
                             helper_directory, user, group,
418
 
                             (const char *const *)mandos_argv)){
419
 
    return EX_OSERR;            /* Error has already been printed */
420
 
  }
421
 
  /* These variables were only for start_mandos_client() and are not
422
 
     needed anymore */
423
 
  free(mandos_argv);
424
 
  free(mandos_argz);
425
 
  mandos_argz = NULL;
426
 
  if(not add_inotify_dir_watch(queue, epoll_fd, &quit_now, &password,
427
 
                               agent_directory, &cancelled_filenames,
428
 
                               &current_time, &mandos_client_exited,
429
 
                               &password_is_read)){
430
 
    switch(errno){              /* Error has already been printed */
431
 
    case EACCES:
432
 
    case ENAMETOOLONG:
433
 
    case ENOENT:
434
 
      return EX_OSFILE;
435
 
    default:
436
 
      return EX_OSERR;
437
 
    }
438
 
  }
439
 
  if(not add_existing_questions(queue, epoll_fd, &password,
440
 
                                &cancelled_filenames, &current_time,
441
 
                                &mandos_client_exited,
442
 
                                &password_is_read, agent_directory)){
443
 
    return EXIT_FAILURE;        /* Error has already been printed */
444
 
  }
445
 
 
446
 
  /* Run queue */
447
 
  do {
448
 
    current_time = get_current_time();
449
 
    if(not wait_for_event(epoll_fd, queue->next_run, current_time)){
450
 
      const error_t saved_errno = errno;
451
 
      error(EXIT_FAILURE, saved_errno, "Failure while waiting for"
452
 
            " events");
453
 
    }
454
 
 
455
 
    current_time = get_current_time();
456
 
    if(not run_queue(&queue, &cancelled_filenames, &quit_now)){
457
 
      const error_t saved_errno = errno;
458
 
      error(EXIT_FAILURE, saved_errno, "Failure while running queue");
459
 
    }
460
 
 
461
 
    /*  When no tasks about questions are left in the queue, break out
462
 
        of the loop (and implicitly exit the program) */
463
 
  } while(queue_has_question(queue));
464
 
 
465
 
  restore_signal_handler(&old_sigchld_action);
466
 
  restore_sigmask(&orig_sigmask);
467
 
 
468
 
  return EXIT_SUCCESS;
469
 
}
470
 
 
471
 
__attribute__((warn_unused_result))
472
 
mono_microsecs get_current_time(void){
473
 
  struct timespec currtime;
474
 
  if(clock_gettime(CLOCK_MONOTONIC, &currtime) != 0){
475
 
    error(0, errno, "Failed to get current time");
476
 
    return 0;
477
 
  }
478
 
  return ((mono_microsecs)currtime.tv_sec * 1000000) /* seconds */
479
 
    + ((mono_microsecs)currtime.tv_nsec / 1000);     /* nanoseconds */
480
 
}
481
 
 
482
 
/* End of "main" section */
483
 
 
484
 
/* Start of regular code section; ALL this code has tests */
485
 
 
486
 
__attribute__((nonnull))
487
 
bool parse_arguments(int argc, char *argv[], const bool exit_failure,
488
 
                     char **agent_directory, char **helper_directory,
489
 
                     uid_t *const user, gid_t *const group,
490
 
                     char **mandos_argz, size_t *mandos_argz_length){
491
 
 
492
 
  const struct argp_option options[] = {
493
 
    { .name="agent-directory",.key='d', .arg="DIRECTORY",
494
 
      .doc="Systemd password agent directory" },
495
 
    { .name="helper-directory",.key=128, .arg="DIRECTORY",
496
 
      .doc="Mandos Client password helper directory" },
497
 
    { .name="plugin-helper-dir", .key=129, /* From plugin-runner */
498
 
      .flags=OPTION_HIDDEN | OPTION_ALIAS },
499
 
    { .name="user", .key='u', .arg="USERID",
500
 
      .doc="User ID the Mandos Client will use as its unprivileged"
501
 
      " user" },
502
 
    { .name="userid", .key=130, /* From plugin--runner */
503
 
      .flags=OPTION_HIDDEN | OPTION_ALIAS },
504
 
    { .name="group", .key='g', .arg="GROUPID",
505
 
      .doc="Group ID the Mandos Client will use as its unprivileged"
506
 
      " group" },
507
 
    { .name="groupid", .key=131, /* From plugin--runner */
508
 
      .flags=OPTION_HIDDEN | OPTION_ALIAS },
509
 
    { .name="test", .key=255, /* See should_only_run_tests() */
510
 
      .doc="Skip normal operation, and only run self-tests.  See"
511
 
      " --test --help.", .group=10, },
512
 
    { NULL },
513
 
  };
514
 
 
515
 
  __attribute__((nonnull(3)))
516
 
    error_t parse_opt(int key, char *arg, struct argp_state *state){
517
 
    errno = 0;
518
 
    switch(key){
519
 
    case 'd':                   /* --agent-directory */
520
 
      *agent_directory = strdup(arg);
521
 
      break;
522
 
    case 128:                   /* --helper-directory */
523
 
    case 129:                   /* --plugin-helper-dir */
524
 
      *helper_directory = strdup(arg);
525
 
      break;
526
 
    case 'u':                   /* --user */
527
 
    case 130:                   /* --userid */
528
 
      {
529
 
        char *tmp;
530
 
        uintmax_t tmp_id = 0;
531
 
        errno = 0;
532
 
        tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
533
 
        if(errno != 0 or tmp == arg or *tmp != '\0'
534
 
           or tmp_id != (uid_t)tmp_id or (uid_t)tmp_id == 0){
535
 
          return ARGP_ERR_UNKNOWN;
536
 
        }
537
 
        *user = (uid_t)tmp_id;
538
 
        errno = 0;
539
 
        break;
540
 
      }
541
 
    case 'g':                   /* --group */
542
 
    case 131:                   /* --groupid */
543
 
      {
544
 
        char *tmp;
545
 
        uintmax_t tmp_id = 0;
546
 
        errno = 0;
547
 
        tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
548
 
        if(errno != 0 or tmp == arg or *tmp != '\0'
549
 
           or tmp_id != (gid_t)tmp_id or (gid_t)tmp_id == 0){
550
 
          return ARGP_ERR_UNKNOWN;
551
 
        }
552
 
        *group = (gid_t)tmp_id;
553
 
        errno = 0;
554
 
        break;
555
 
      }
556
 
    case ARGP_KEY_ARGS:
557
 
      /* Copy arguments into argz vector */
558
 
      return argz_create(state->argv + state->next, mandos_argz,
559
 
                         mandos_argz_length);
560
 
    default:
561
 
      return ARGP_ERR_UNKNOWN;
562
 
    }
563
 
    return errno;
564
 
  }
565
 
 
566
 
  const struct argp argp = {
567
 
    .options=options,
568
 
    .parser=parse_opt,
569
 
    .args_doc="[MANDOS_CLIENT [OPTION...]]\n--test",
570
 
    .doc = "Mandos password agent -- runs Mandos client as a"
571
 
    " systemd password agent",
572
 
  };
573
 
 
574
 
  errno = argp_parse(&argp, argc, argv,
575
 
                     exit_failure ? 0 : ARGP_NO_EXIT, NULL, NULL);
576
 
 
577
 
  return errno == 0;
578
 
}
579
 
 
580
 
__attribute__((nonnull, warn_unused_result))
581
 
bool block_sigchld(sigset_t *const orig_sigmask){
582
 
  sigset_t sigchld_sigmask;
583
 
  if(sigemptyset(&sigchld_sigmask) < 0){
584
 
    error(0, errno, "Failed to empty signal set");
585
 
    return false;
586
 
  }
587
 
  if(sigaddset(&sigchld_sigmask, SIGCHLD) < 0){
588
 
    error(0, errno, "Failed to add SIGCHLD to signal set");
589
 
    return false;
590
 
  }
591
 
  if(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask, orig_sigmask) != 0){
592
 
    error(0, errno, "Failed to block SIGCHLD signal");
593
 
    return false;
594
 
  }
595
 
  return true;
596
 
}
597
 
 
598
 
__attribute__((nonnull, warn_unused_result, const))
599
 
bool restore_sigmask(const sigset_t *const orig_sigmask){
600
 
  if(pthread_sigmask(SIG_SETMASK, orig_sigmask, NULL) != 0){
601
 
    error(0, errno, "Failed to restore blocked signals");
602
 
    return false;
603
 
  }
604
 
  return true;
605
 
}
606
 
 
607
 
__attribute__((nonnull, warn_unused_result))
608
 
bool setup_signal_handler(struct sigaction *const old_sigchld_action){
609
 
  struct sigaction sigchld_action = {
610
 
    .sa_handler=handle_sigchld,
611
 
    .sa_flags=SA_RESTART | SA_NOCLDSTOP,
612
 
  };
613
 
  /* Set all signals in "sa_mask" struct member; this makes all
614
 
     signals automatically blocked during signal handler */
615
 
  if(sigfillset(&sigchld_action.sa_mask) != 0){
616
 
    error(0, errno, "Failed to do sigfillset()");
617
 
    return false;
618
 
  }
619
 
  if(sigaction(SIGCHLD, &sigchld_action, old_sigchld_action) != 0){
620
 
    error(0, errno, "Failed to set SIGCHLD signal handler");
621
 
    return false;
622
 
  }
623
 
  return true;
624
 
}
625
 
 
626
 
__attribute__((nonnull, warn_unused_result))
627
 
bool restore_signal_handler(const struct sigaction *const
628
 
                            old_sigchld_action){
629
 
  if(sigaction(SIGCHLD, old_sigchld_action, NULL) != 0){
630
 
    error(0, errno, "Failed to restore signal handler");
631
 
    return false;
632
 
  }
633
 
  return true;
634
 
}
635
 
 
636
 
__attribute__((warn_unused_result, malloc))
637
 
task_queue *create_queue(void){
638
 
  task_queue *queue = malloc(sizeof(task_queue));
639
 
  if(queue){
640
 
    queue->tasks = NULL;
641
 
    queue->length = 0;
642
 
    queue->allocated = 0;
643
 
    queue->next_run = 0;
644
 
  }
645
 
  return queue;
646
 
}
647
 
 
648
 
__attribute__((nonnull, warn_unused_result))
649
 
bool add_to_queue(task_queue *const queue, const task_context task){
650
 
  const size_t needed_size = sizeof(task_context)*(queue->length + 1);
651
 
  if(needed_size > (queue->allocated)){
652
 
    task_context *const new_tasks = realloc(queue->tasks,
653
 
                                            needed_size);
654
 
    if(new_tasks == NULL){
655
 
      error(0, errno, "Failed to allocate %" PRIuMAX
656
 
            " bytes for queue->tasks", (uintmax_t)needed_size);
657
 
      return false;
658
 
    }
659
 
    queue->tasks = new_tasks;
660
 
    queue->allocated = needed_size;
661
 
  }
662
 
  /* Using memcpy here is necessary because doing */
663
 
  /* queue->tasks[queue->length++] = task; */
664
 
  /* would violate const-ness of task members */
665
 
  memcpy(&(queue->tasks[queue->length++]), &task,
666
 
         sizeof(task_context));
667
 
  return true;
668
 
}
669
 
 
670
 
__attribute__((nonnull))
671
 
void cleanup_task(const task_context *const task){
672
 
  const error_t saved_errno = errno;
673
 
  /* free and close all task data */
674
 
  free(task->question_filename);
675
 
  if(task->filename != task->question_filename){
676
 
    free(task->filename);
677
 
  }
678
 
  if(task->pid > 0){
679
 
    kill(task->pid, SIGTERM);
680
 
  }
681
 
  if(task->fd > 0){
682
 
    close(task->fd);
683
 
  }
684
 
  errno = saved_errno;
685
 
}
686
 
 
687
 
__attribute__((nonnull))
688
 
void free_queue(task_queue *const queue){
689
 
  free(queue->tasks);
690
 
  free(queue);
691
 
}
692
 
 
693
 
__attribute__((nonnull))
694
 
void cleanup_queue(task_queue *const *const queue){
695
 
  if(*queue == NULL){
696
 
    return;
697
 
  }
698
 
  for(size_t i = 0; i < (*queue)->length; i++){
699
 
    const task_context *const task = ((*queue)->tasks)+i;
700
 
    cleanup_task(task);
701
 
  }
702
 
  free_queue(*queue);
703
 
}
704
 
 
705
 
__attribute__((pure, nonnull, warn_unused_result))
706
 
bool queue_has_question(const task_queue *const queue){
707
 
  for(size_t i=0; i < queue->length; i++){
708
 
    if(queue->tasks[i].question_filename != NULL){
709
 
      return true;
710
 
    }
711
 
  }
712
 
  return false;
713
 
}
714
 
 
715
 
__attribute__((nonnull))
716
 
void cleanup_close(const int *const fd){
717
 
  const error_t saved_errno = errno;
718
 
  close(*fd);
719
 
  errno = saved_errno;
720
 
}
721
 
 
722
 
__attribute__((nonnull))
723
 
void cleanup_string(char *const *const ptr){
724
 
  free(*ptr);
725
 
}
726
 
 
727
 
__attribute__((nonnull))
728
 
void cleanup_buffer(buffer *buf){
729
 
  if(buf->allocated > 0){
730
 
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
731
 
    explicit_bzero(buf->data, buf->allocated);
732
 
#else
733
 
    memset(buf->data, '\0', buf->allocated);
734
 
#endif
735
 
  }
736
 
  if(buf->data != NULL){
737
 
    if(munlock(buf->data, buf->allocated) != 0){
738
 
      error(0, errno, "Failed to unlock memory of old buffer");
739
 
    }
740
 
    free(buf->data);
741
 
    buf->data = NULL;
742
 
  }
743
 
  buf->length = 0;
744
 
  buf->allocated = 0;
745
 
}
746
 
 
747
 
__attribute__((pure, nonnull, warn_unused_result))
748
 
bool string_set_contains(const string_set set, const char *const str){
749
 
  for(const char *s = set.argz; s != NULL and set.argz_len > 0;
750
 
      s = argz_next(set.argz, set.argz_len, s)){
751
 
    if(strcmp(s, str) == 0){
752
 
      return true;
753
 
    }
754
 
  }
755
 
  return false;
756
 
}
757
 
 
758
 
__attribute__((nonnull, warn_unused_result))
759
 
bool string_set_add(string_set *const set, const char *const str){
760
 
  if(string_set_contains(*set, str)){
761
 
    return true;
762
 
  }
763
 
  error_t error = argz_add(&set->argz, &set->argz_len, str);
764
 
  if(error == 0){
765
 
    return true;
766
 
  }
767
 
  errno = error;
768
 
  return false;
769
 
}
770
 
 
771
 
__attribute__((nonnull))
772
 
void string_set_clear(string_set *set){
773
 
  free(set->argz);
774
 
  set->argz = NULL;
775
 
  set->argz_len = 0;
776
 
}
777
 
 
778
 
__attribute__((nonnull))
779
 
void string_set_swap(string_set *const set1, string_set *const set2){
780
 
  /* Swap contents of two string sets */
781
 
  {
782
 
    char *const tmp_argz = set1->argz;
783
 
    set1->argz = set2->argz;
784
 
    set2->argz = tmp_argz;
785
 
  }
786
 
  {
787
 
    const size_t tmp_argz_len = set1->argz_len;
788
 
    set1->argz_len = set2->argz_len;
789
 
    set2->argz_len = tmp_argz_len;
790
 
  }
791
 
}
792
 
 
793
 
__attribute__((nonnull, warn_unused_result))
794
 
bool start_mandos_client(task_queue *const queue,
795
 
                         const int epoll_fd,
796
 
                         bool *const mandos_client_exited,
797
 
                         bool *const quit_now, buffer *const password,
798
 
                         bool *const password_is_read,
799
 
                         const struct sigaction *const
800
 
                         old_sigchld_action, const sigset_t sigmask,
801
 
                         const char *const helper_directory,
802
 
                         const uid_t user, const gid_t group,
803
 
                         const char *const *const argv){
804
 
  int pipefds[2];
805
 
  if(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK) != 0){
806
 
    error(0, errno, "Failed to pipe2(..., O_CLOEXEC | O_NONBLOCK)");
807
 
    return false;
808
 
  }
809
 
 
810
 
  const pid_t pid = fork();
811
 
  if(pid == 0){
812
 
    if(not restore_signal_handler(old_sigchld_action)){
813
 
      _exit(EXIT_FAILURE);
814
 
    }
815
 
    if(not restore_sigmask(&sigmask)){
816
 
      _exit(EXIT_FAILURE);
817
 
    }
818
 
    if(close(pipefds[0]) != 0){
819
 
      error(0, errno, "Failed to close() parent pipe fd");
820
 
      _exit(EXIT_FAILURE);
821
 
    }
822
 
    if(dup2(pipefds[1], STDOUT_FILENO) == -1){
823
 
      error(0, errno, "Failed to dup2() pipe fd to stdout");
824
 
      _exit(EXIT_FAILURE);
825
 
    }
826
 
    if(close(pipefds[1]) != 0){
827
 
      error(0, errno, "Failed to close() old child pipe fd");
828
 
      _exit(EXIT_FAILURE);
829
 
    }
830
 
    if(setenv("MANDOSPLUGINHELPERDIR", helper_directory, 1) != 0){
831
 
      error(0, errno, "Failed to setenv(\"MANDOSPLUGINHELPERDIR\","
832
 
            " \"%s\", 1)", helper_directory);
833
 
      _exit(EXIT_FAILURE);
834
 
    }
835
 
    if(group != 0 and setresgid(group, 0, 0) == -1){
836
 
      error(0, errno, "Failed to setresgid(-1, %" PRIuMAX ", %"
837
 
            PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
838
 
      _exit(EXIT_FAILURE);
839
 
    }
840
 
    if(user != 0 and setresuid(user, 0, 0) == -1){
841
 
      error(0, errno, "Failed to setresuid(-1, %" PRIuMAX ", %"
842
 
            PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
843
 
      _exit(EXIT_FAILURE);
844
 
    }
845
 
#ifdef __GNUC__
846
 
#pragma GCC diagnostic push
847
 
    /* For historical reasons, the "argv" argument to execv() is not
848
 
       const, but it is safe to override this. */
849
 
#pragma GCC diagnostic ignored "-Wcast-qual"
850
 
#endif
851
 
    execv(argv[0], (char **)argv);
852
 
#ifdef __GNUC__
853
 
#pragma GCC diagnostic pop
854
 
#endif
855
 
    error(0, errno, "execv(\"%s\", ...) failed", argv[0]);
856
 
    _exit(EXIT_FAILURE);
857
 
  }
858
 
  close(pipefds[1]);
859
 
 
860
 
  if(not add_to_queue(queue, (task_context){
861
 
        .func=wait_for_mandos_client_exit,
862
 
        .pid=pid,
863
 
        .mandos_client_exited=mandos_client_exited,
864
 
        .quit_now=quit_now,
865
 
      })){
866
 
    error(0, errno, "Failed to add wait_for_mandos_client to queue");
867
 
    close(pipefds[0]);
868
 
    return false;
869
 
  }
870
 
 
871
 
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipefds[0],
872
 
                            &(struct epoll_event)
873
 
                            { .events=EPOLLIN | EPOLLRDHUP });
874
 
  if(ret != 0 and errno != EEXIST){
875
 
    error(0, errno, "Failed to add file descriptor to epoll set");
876
 
    close(pipefds[0]);
877
 
    return false;
878
 
  }
879
 
 
880
 
  return add_to_queue(queue, (task_context){
881
 
      .func=read_mandos_client_output,
882
 
      .epoll_fd=epoll_fd,
883
 
      .fd=pipefds[0],
884
 
      .quit_now=quit_now,
885
 
      .password=password,
886
 
      .password_is_read=password_is_read,
887
 
    });
888
 
}
889
 
 
890
 
__attribute__((nonnull))
891
 
void wait_for_mandos_client_exit(const task_context task,
892
 
                                 task_queue *const queue){
893
 
  const pid_t pid = task.pid;
894
 
  bool *const mandos_client_exited = task.mandos_client_exited;
895
 
  bool *const quit_now = task.quit_now;
896
 
 
897
 
  int status;
898
 
  switch(waitpid(pid, &status, WNOHANG)){
899
 
  case 0:                       /* Not exited yet */
900
 
    if(not add_to_queue(queue, task)){
901
 
      error(0, errno, "Failed to add myself to queue");
902
 
      *quit_now = true;
903
 
    }
904
 
    break;
905
 
  case -1:                      /* Error */
906
 
    error(0, errno, "waitpid(%" PRIdMAX ") failed", (intmax_t)pid);
907
 
    if(errno != ECHILD){
908
 
      kill(pid, SIGTERM);
909
 
    }
910
 
    *quit_now = true;
911
 
    break;
912
 
  default:                      /* Has exited */
913
 
    *mandos_client_exited = true;
914
 
    if((not WIFEXITED(status))
915
 
       or (WEXITSTATUS(status) != EXIT_SUCCESS)){
916
 
      error(0, 0, "Mandos client failed or was killed");
917
 
      *quit_now = true;
918
 
    }
919
 
  }
920
 
}
921
 
 
922
 
__attribute__((nonnull))
923
 
void read_mandos_client_output(const task_context task,
924
 
                               task_queue *const queue){
925
 
  buffer *const password = task.password;
926
 
  bool *const quit_now = task.quit_now;
927
 
  bool *const password_is_read = task.password_is_read;
928
 
  const int fd = task.fd;
929
 
  const int epoll_fd = task.epoll_fd;
930
 
 
931
 
  const size_t new_potential_size = (password->length + PIPE_BUF);
932
 
  if(password->allocated < new_potential_size){
933
 
    char *const new_buffer = calloc(new_potential_size, 1);
934
 
    if(new_buffer == NULL){
935
 
      error(0, errno, "Failed to allocate %" PRIuMAX
936
 
            " bytes for password", (uintmax_t)new_potential_size);
937
 
      *quit_now = true;
938
 
      close(fd);
939
 
      return;
940
 
    }
941
 
    if(mlock(new_buffer, new_potential_size) != 0){
942
 
      /* Warn but do not treat as fatal error */
943
 
      if(errno != EPERM and errno != ENOMEM){
944
 
        error(0, errno, "Failed to lock memory for password");
945
 
      }
946
 
    }
947
 
    if(password->length > 0){
948
 
      memcpy(new_buffer, password->data, password->length);
949
 
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
950
 
      explicit_bzero(password->data, password->allocated);
951
 
#else
952
 
      memset(password->data, '\0', password->allocated);
953
 
#endif
954
 
    }
955
 
    if(password->data != NULL){
956
 
      if(munlock(password->data, password->allocated) != 0){
957
 
        error(0, errno, "Failed to unlock memory of old buffer");
958
 
      }
959
 
      free(password->data);
960
 
    }
961
 
    password->data = new_buffer;
962
 
    password->allocated = new_potential_size;
963
 
  }
964
 
 
965
 
  const ssize_t read_length = read(fd, password->data
966
 
                                   + password->length, PIPE_BUF);
967
 
 
968
 
  if(read_length == 0){ /* EOF */
969
 
    *password_is_read = true;
970
 
    close(fd);
971
 
    return;
972
 
  }
973
 
  if(read_length < 0 and errno != EAGAIN){ /* Actual error */
974
 
    error(0, errno, "Failed to read password from Mandos client");
975
 
    *quit_now = true;
976
 
    close(fd);
977
 
    return;
978
 
  }
979
 
  if(read_length > 0){          /* Data has been read */
980
 
    password->length += (size_t)read_length;
981
 
  }
982
 
 
983
 
  /* Either data was read, or EAGAIN was indicated, meaning no data
984
 
     available yet */
985
 
 
986
 
  /* Re-add the fd to the epoll set */
987
 
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
988
 
                            &(struct epoll_event)
989
 
                            { .events=EPOLLIN | EPOLLRDHUP });
990
 
  if(ret != 0 and errno != EEXIST){
991
 
    error(0, errno, "Failed to re-add file descriptor to epoll set");
992
 
    *quit_now = true;
993
 
    close(fd);
994
 
    return;
995
 
  }
996
 
 
997
 
  /* Re-add myself to the queue */
998
 
  if(not add_to_queue(queue, task)){
999
 
    error(0, errno, "Failed to add myself to queue");
1000
 
    *quit_now = true;
1001
 
    close(fd);
1002
 
  }
1003
 
}
1004
 
 
1005
 
__attribute__((nonnull, warn_unused_result))
1006
 
bool add_inotify_dir_watch(task_queue *const queue,
1007
 
                           const int epoll_fd, bool *const quit_now,
1008
 
                           buffer *const password,
1009
 
                           const char *const dir,
1010
 
                           string_set *cancelled_filenames,
1011
 
                           const mono_microsecs *const current_time,
1012
 
                           bool *const mandos_client_exited,
1013
 
                           bool *const password_is_read){
1014
 
  const int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
1015
 
  if(fd == -1){
1016
 
    error(0, errno, "Failed to create inotify instance");
1017
 
    return false;
1018
 
  }
1019
 
 
1020
 
  if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO
1021
 
                       | IN_MOVED_FROM| IN_DELETE | IN_EXCL_UNLINK)
1022
 
     == -1){
1023
 
    error(0, errno, "Failed to create inotify watch on %s", dir);
1024
 
    return false;
1025
 
  }
1026
 
 
1027
 
  /* Add the inotify fd to the epoll set */
1028
 
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1029
 
                            &(struct epoll_event)
1030
 
                            { .events=EPOLLIN | EPOLLRDHUP });
1031
 
  if(ret != 0 and errno != EEXIST){
1032
 
    error(0, errno, "Failed to add file descriptor to epoll set");
1033
 
    close(fd);
1034
 
    return false;
1035
 
  }
1036
 
 
1037
 
  const task_context read_inotify_event_task = {
1038
 
    .func=read_inotify_event,
1039
 
    .epoll_fd=epoll_fd,
1040
 
    .quit_now=quit_now,
1041
 
    .password=password,
1042
 
    .fd=fd,
1043
 
    .filename=strdup(dir),
1044
 
    .cancelled_filenames=cancelled_filenames,
1045
 
    .current_time=current_time,
1046
 
    .mandos_client_exited=mandos_client_exited,
1047
 
    .password_is_read=password_is_read,
1048
 
  };
1049
 
  if(read_inotify_event_task.filename == NULL){
1050
 
    error(0, errno, "Failed to strdup(\"%s\")", dir);
1051
 
    close(fd);
1052
 
    return false;
1053
 
  }
1054
 
 
1055
 
  return add_to_queue(queue, read_inotify_event_task);
1056
 
}
1057
 
 
1058
 
__attribute__((nonnull))
1059
 
void read_inotify_event(const task_context task,
1060
 
                        task_queue *const queue){
1061
 
  const int fd = task.fd;
1062
 
  const int epoll_fd = task.epoll_fd;
1063
 
  char *const filename = task.filename;
1064
 
  bool *quit_now = task.quit_now;
1065
 
  buffer *const password = task.password;
1066
 
  string_set *const cancelled_filenames = task.cancelled_filenames;
1067
 
  const mono_microsecs *const current_time = task.current_time;
1068
 
  bool *const mandos_client_exited = task.mandos_client_exited;
1069
 
  bool *const password_is_read = task.password_is_read;
1070
 
 
1071
 
  /* "sufficient to read at least one event." - inotify(7) */
1072
 
  const size_t ievent_size = (sizeof(struct inotify_event)
1073
 
                              + NAME_MAX + 1);
1074
 
  struct {
1075
 
    struct inotify_event event;
1076
 
    char name_buffer[NAME_MAX + 1];
1077
 
  } ievent_buffer;
1078
 
  struct inotify_event *const ievent = &ievent_buffer.event;
1079
 
 
1080
 
  const ssize_t read_length = read(fd, ievent, ievent_size);
1081
 
  if(read_length == 0){ /* EOF */
1082
 
    error(0, 0, "Got EOF from inotify fd for directory %s", filename);
1083
 
    *quit_now = true;
1084
 
    cleanup_task(&task);
1085
 
    return;
1086
 
  }
1087
 
  if(read_length < 0 and errno != EAGAIN){ /* Actual error */
1088
 
    error(0, errno, "Failed to read from inotify fd for directory %s",
1089
 
          filename);
1090
 
    *quit_now = true;
1091
 
    cleanup_task(&task);
1092
 
    return;
1093
 
  }
1094
 
  if(read_length > 0            /* Data has been read */
1095
 
     and fnmatch("ask.*", ievent->name, FNM_FILE_NAME) == 0){
1096
 
    char *question_filename = NULL;
1097
 
    const ssize_t question_filename_length
1098
 
      = asprintf(&question_filename, "%s/%s", filename, ievent->name);
1099
 
    if(question_filename_length < 0){
1100
 
      error(0, errno, "Failed to create file name from directory name"
1101
 
            " %s and file name %s", filename, ievent->name);
1102
 
    } else {
1103
 
      if(ievent->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)){
1104
 
        if(not add_to_queue(queue, (task_context){
1105
 
              .func=open_and_parse_question,
1106
 
              .epoll_fd=epoll_fd,
1107
 
              .question_filename=question_filename,
1108
 
              .filename=question_filename,
1109
 
              .password=password,
1110
 
              .cancelled_filenames=cancelled_filenames,
1111
 
              .current_time=current_time,
1112
 
              .mandos_client_exited=mandos_client_exited,
1113
 
              .password_is_read=password_is_read,
1114
 
            })){
1115
 
          error(0, errno, "Failed to add open_and_parse_question task"
1116
 
                " for file name %s to queue", filename);
1117
 
        } else {
1118
 
          /* Force the added task (open_and_parse_question) to run
1119
 
             immediately */
1120
 
          queue->next_run = 1;
1121
 
        }
1122
 
      } else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
1123
 
        if(not string_set_add(cancelled_filenames,
1124
 
                              question_filename)){
1125
 
          error(0, errno, "Could not add question %s to"
1126
 
                " cancelled_questions", question_filename);
1127
 
          *quit_now = true;
1128
 
          free(question_filename);
1129
 
          cleanup_task(&task);
1130
 
          return;
1131
 
        }
1132
 
        free(question_filename);
1133
 
      }
1134
 
    }
1135
 
  }
1136
 
 
1137
 
  /* Either data was read, or EAGAIN was indicated, meaning no data
1138
 
     available yet */
1139
 
 
1140
 
  /* Re-add myself to the queue */
1141
 
  if(not add_to_queue(queue, task)){
1142
 
    error(0, errno, "Failed to re-add read_inotify_event(%s) to"
1143
 
          " queue", filename);
1144
 
    *quit_now = true;
1145
 
    cleanup_task(&task);
1146
 
    return;
1147
 
  }
1148
 
 
1149
 
  /* Re-add the fd to the epoll set */
1150
 
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1151
 
                            &(struct epoll_event)
1152
 
                            { .events=EPOLLIN | EPOLLRDHUP });
1153
 
  if(ret != 0 and errno != EEXIST){
1154
 
    error(0, errno, "Failed to re-add inotify file descriptor %d for"
1155
 
          " directory %s to epoll set", fd, filename);
1156
 
    /* Force the added task (read_inotify_event) to run again, at most
1157
 
       one second from now */
1158
 
    if((queue->next_run == 0)
1159
 
       or (queue->next_run > (*current_time + 1000000))){
1160
 
      queue->next_run = *current_time + 1000000;
1161
 
    }
1162
 
  }
1163
 
}
1164
 
 
1165
 
__attribute__((nonnull))
1166
 
void open_and_parse_question(const task_context task,
1167
 
                             task_queue *const queue){
1168
 
  __attribute__((cleanup(cleanup_string)))
1169
 
    char *question_filename = task.question_filename;
1170
 
  const int epoll_fd = task.epoll_fd;
1171
 
  buffer *const password = task.password;
1172
 
  string_set *const cancelled_filenames = task.cancelled_filenames;
1173
 
  const mono_microsecs *const current_time = task.current_time;
1174
 
  bool *const mandos_client_exited = task.mandos_client_exited;
1175
 
  bool *const password_is_read = task.password_is_read;
1176
 
 
1177
 
  /* We use the GLib "Key-value file parser" functions to parse the
1178
 
     question file.  See <https://www.freedesktop.org/wiki/Software
1179
 
     /systemd/PasswordAgents/> for specification of contents */
1180
 
  __attribute__((nonnull))
1181
 
    void cleanup_g_key_file(GKeyFile **key_file){
1182
 
    if(*key_file != NULL){
1183
 
      g_key_file_free(*key_file);
1184
 
    }
1185
 
  }
1186
 
 
1187
 
  __attribute__((cleanup(cleanup_g_key_file)))
1188
 
    GKeyFile *key_file = g_key_file_new();
1189
 
  if(key_file == NULL){
1190
 
    error(0, errno, "Failed g_key_file_new() for \"%s\"",
1191
 
          question_filename);
1192
 
    return;
1193
 
  }
1194
 
  GError *glib_error = NULL;
1195
 
  if(g_key_file_load_from_file(key_file, question_filename,
1196
 
                               G_KEY_FILE_NONE, &glib_error) != TRUE){
1197
 
    /* If a file was removed, we should ignore it, so */
1198
 
    /* only show error message if file actually existed */
1199
 
    if(glib_error->code != G_FILE_ERROR_NOENT){
1200
 
      error(0, 0, "Failed to load question data from file \"%s\": %s",
1201
 
            question_filename, glib_error->message);
1202
 
    }
1203
 
    return;
1204
 
  }
1205
 
 
1206
 
  __attribute__((cleanup(cleanup_string)))
1207
 
    char *socket_name = g_key_file_get_string(key_file, "Ask",
1208
 
                                              "Socket",
1209
 
                                              &glib_error);
1210
 
  if(socket_name == NULL){
1211
 
    error(0, 0, "Question file \"%s\" did not contain \"Socket\": %s",
1212
 
          question_filename, glib_error->message);
1213
 
    return;
1214
 
  }
1215
 
 
1216
 
  if(strlen(socket_name) == 0){
1217
 
    error(0, 0, "Question file \"%s\" had empty \"Socket\" value",
1218
 
          question_filename);
1219
 
    return;
1220
 
  }
1221
 
 
1222
 
  const guint64 pid = g_key_file_get_uint64(key_file, "Ask", "PID",
1223
 
                                            &glib_error);
1224
 
  if(glib_error != NULL){
1225
 
    error(0, 0, "Question file \"%s\" contained bad \"PID\": %s",
1226
 
          question_filename, glib_error->message);
1227
 
    return;
1228
 
  }
1229
 
 
1230
 
  if((pid != (guint64)((pid_t)pid))
1231
 
     or (kill((pid_t)pid, 0) != 0)){
1232
 
    error(0, 0, "PID %" PRIuMAX " in question file \"%s\" is bad or"
1233
 
          " does not exist", (uintmax_t)pid, question_filename);
1234
 
    return;
1235
 
  }
1236
 
 
1237
 
  guint64 notafter = g_key_file_get_uint64(key_file, "Ask",
1238
 
                                           "NotAfter", &glib_error);
1239
 
  if(glib_error != NULL){
1240
 
    if(glib_error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND){
1241
 
      error(0, 0, "Question file \"%s\" contained bad \"NotAfter\":"
1242
 
            " %s", question_filename, glib_error->message);
1243
 
    }
1244
 
    notafter = 0;
1245
 
  }
1246
 
  if(notafter != 0){
1247
 
    if(queue->next_run == 0 or (queue->next_run > notafter)){
1248
 
      queue->next_run = notafter;
1249
 
    }
1250
 
    if(*current_time >= notafter){
1251
 
      return;
1252
 
    }
1253
 
  }
1254
 
 
1255
 
  const task_context connect_question_socket_task = {
1256
 
    .func=connect_question_socket,
1257
 
    .question_filename=strdup(question_filename),
1258
 
    .epoll_fd=epoll_fd,
1259
 
    .password=password,
1260
 
    .filename=strdup(socket_name),
1261
 
    .cancelled_filenames=task.cancelled_filenames,
1262
 
    .mandos_client_exited=mandos_client_exited,
1263
 
    .password_is_read=password_is_read,
1264
 
    .current_time=current_time,
1265
 
  };
1266
 
  if(connect_question_socket_task.question_filename == NULL
1267
 
     or connect_question_socket_task.filename == NULL
1268
 
     or not add_to_queue(queue, connect_question_socket_task)){
1269
 
    error(0, errno, "Failed to add connect_question_socket for socket"
1270
 
          " %s (from \"%s\") to queue", socket_name,
1271
 
          question_filename);
1272
 
    cleanup_task(&connect_question_socket_task);
1273
 
    return;
1274
 
  }
1275
 
  /* Force the added task (connect_question_socket) to run
1276
 
     immediately */
1277
 
  queue->next_run = 1;
1278
 
 
1279
 
  if(notafter > 0){
1280
 
    char *const dup_filename = strdup(question_filename);
1281
 
    const task_context cancel_old_question_task = {
1282
 
      .func=cancel_old_question,
1283
 
      .question_filename=dup_filename,
1284
 
      .notafter=notafter,
1285
 
      .filename=dup_filename,
1286
 
      .cancelled_filenames=cancelled_filenames,
1287
 
      .current_time=current_time,
1288
 
    };
1289
 
    if(cancel_old_question_task.question_filename == NULL
1290
 
       or not add_to_queue(queue, cancel_old_question_task)){
1291
 
      error(0, errno, "Failed to add cancel_old_question for file "
1292
 
            "\"%s\" to queue", question_filename);
1293
 
      cleanup_task(&cancel_old_question_task);
1294
 
      return;
1295
 
    }
1296
 
  }
1297
 
}
1298
 
 
1299
 
__attribute__((nonnull))
1300
 
void cancel_old_question(const task_context task,
1301
 
                         task_queue *const queue){
1302
 
  char *const question_filename = task.question_filename;
1303
 
  string_set *const cancelled_filenames = task.cancelled_filenames;
1304
 
  const mono_microsecs notafter = task.notafter;
1305
 
  const mono_microsecs *const current_time = task.current_time;
1306
 
 
1307
 
  if(*current_time >= notafter){
1308
 
    if(not string_set_add(cancelled_filenames, question_filename)){
1309
 
      error(0, errno, "Failed to cancel question for file %s",
1310
 
            question_filename);
1311
 
    }
1312
 
    cleanup_task(&task);
1313
 
    return;
1314
 
  }
1315
 
 
1316
 
  if(not add_to_queue(queue, task)){
1317
 
    error(0, errno, "Failed to add cancel_old_question for file "
1318
 
          "%s to queue", question_filename);
1319
 
    cleanup_task(&task);
1320
 
    return;
1321
 
  }
1322
 
 
1323
 
  if((queue->next_run == 0) or (queue->next_run > notafter)){
1324
 
    queue->next_run = notafter;
1325
 
  }
1326
 
}
1327
 
 
1328
 
__attribute__((nonnull))
1329
 
void connect_question_socket(const task_context task,
1330
 
                             task_queue *const queue){
1331
 
  char *const question_filename = task.question_filename;
1332
 
  char *const filename = task.filename;
1333
 
  const int epoll_fd = task.epoll_fd;
1334
 
  buffer *const password = task.password;
1335
 
  string_set *const cancelled_filenames = task.cancelled_filenames;
1336
 
  bool *const mandos_client_exited = task.mandos_client_exited;
1337
 
  bool *const password_is_read = task.password_is_read;
1338
 
  const mono_microsecs *const current_time = task.current_time;
1339
 
 
1340
 
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
1341
 
 
1342
 
  if(sizeof(sock_name.sun_path) <= strlen(filename)){
1343
 
    error(0, 0, "Socket filename is larger than"
1344
 
          " sizeof(sockaddr_un.sun_path); %" PRIuMAX ": \"%s\"",
1345
 
          (uintmax_t)sizeof(sock_name.sun_path), filename);
1346
 
    if(not string_set_add(cancelled_filenames, question_filename)){
1347
 
      error(0, errno, "Failed to cancel question for file %s",
1348
 
            question_filename);
1349
 
    }
1350
 
    cleanup_task(&task);
1351
 
    return;
1352
 
  }
1353
 
 
1354
 
  const int fd = socket(PF_LOCAL, SOCK_DGRAM
1355
 
                        | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
1356
 
  if(fd < 0){
1357
 
    error(0, errno,
1358
 
          "Failed to create socket(PF_LOCAL, SOCK_DGRAM, 0)");
1359
 
    if(not add_to_queue(queue, task)){
1360
 
      error(0, errno, "Failed to add connect_question_socket for file"
1361
 
            " \"%s\" and socket \"%s\" to queue", question_filename,
1362
 
            filename);
1363
 
      cleanup_task(&task);
1364
 
    } else {
1365
 
      /* Force the added task (connect_question_socket) to run
1366
 
         immediately */
1367
 
      queue->next_run = 1;
1368
 
    }
1369
 
    return;
1370
 
  }
1371
 
 
1372
 
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
1373
 
  if(connect(fd, (struct sockaddr *)&sock_name,
1374
 
             (socklen_t)SUN_LEN(&sock_name)) != 0){
1375
 
    error(0, errno, "Failed to connect socket to \"%s\"", filename);
1376
 
    if(not add_to_queue(queue, task)){
1377
 
      error(0, errno, "Failed to add connect_question_socket for file"
1378
 
            " \"%s\" and socket \"%s\" to queue", question_filename,
1379
 
            filename);
1380
 
      cleanup_task(&task);
1381
 
    } else {
1382
 
      /* Force the added task (connect_question_socket) to run again,
1383
 
         at most one second from now */
1384
 
      if((queue->next_run == 0)
1385
 
         or (queue->next_run > (*current_time + 1000000))){
1386
 
        queue->next_run = *current_time + 1000000;
1387
 
      }
1388
 
    }
1389
 
    return;
1390
 
  }
1391
 
 
1392
 
  /* Not necessary, but we can try, and merely warn on failure */
1393
 
  if(shutdown(fd, SHUT_RD) != 0){
1394
 
    error(0, errno, "Failed to shutdown reading from socket \"%s\"",
1395
 
          filename);
1396
 
  }
1397
 
 
1398
 
  /* Add the fd to the epoll set */
1399
 
  if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1400
 
               &(struct epoll_event){ .events=EPOLLOUT })
1401
 
     != 0){
1402
 
    error(0, errno, "Failed to add inotify file descriptor %d for"
1403
 
          " socket %s to epoll set", fd, filename);
1404
 
    if(not add_to_queue(queue, task)){
1405
 
      error(0, errno, "Failed to add connect_question_socket for file"
1406
 
            " \"%s\" and socket \"%s\" to queue", question_filename,
1407
 
            filename);
1408
 
      cleanup_task(&task);
1409
 
    } else {
1410
 
      /* Force the added task (connect_question_socket) to run again,
1411
 
         at most one second from now */
1412
 
      if((queue->next_run == 0)
1413
 
         or (queue->next_run > (*current_time + 1000000))){
1414
 
        queue->next_run = *current_time + 1000000;
1415
 
      }
1416
 
    }
1417
 
    return;
1418
 
  }
1419
 
 
1420
 
  /* add task send_password_to_socket to queue */
1421
 
  const task_context send_password_to_socket_task = {
1422
 
    .func=send_password_to_socket,
1423
 
    .question_filename=question_filename,
1424
 
    .filename=filename,
1425
 
    .epoll_fd=epoll_fd,
1426
 
    .fd=fd,
1427
 
    .password=password,
1428
 
    .cancelled_filenames=cancelled_filenames,
1429
 
    .mandos_client_exited=mandos_client_exited,
1430
 
    .password_is_read=password_is_read,
1431
 
    .current_time=current_time,
1432
 
  };
1433
 
 
1434
 
  if(not add_to_queue(queue, send_password_to_socket_task)){
1435
 
    error(0, errno, "Failed to add send_password_to_socket for"
1436
 
          " file \"%s\" and socket \"%s\" to queue",
1437
 
          question_filename, filename);
1438
 
    cleanup_task(&send_password_to_socket_task);
1439
 
  }
1440
 
}
1441
 
 
1442
 
__attribute__((nonnull))
1443
 
void send_password_to_socket(const task_context task,
1444
 
                             task_queue *const queue){
1445
 
  char *const question_filename=task.question_filename;
1446
 
  char *const filename=task.filename;
1447
 
  const int epoll_fd=task.epoll_fd;
1448
 
  const int fd=task.fd;
1449
 
  buffer *const password=task.password;
1450
 
  string_set *const cancelled_filenames=task.cancelled_filenames;
1451
 
  bool *const mandos_client_exited = task.mandos_client_exited;
1452
 
  bool *const password_is_read = task.password_is_read;
1453
 
  const mono_microsecs *const current_time = task.current_time;
1454
 
 
1455
 
  if(*mandos_client_exited and *password_is_read){
1456
 
 
1457
 
    const size_t send_buffer_length = password->length + 2;
1458
 
    char *send_buffer = malloc(send_buffer_length);
1459
 
    if(send_buffer == NULL){
1460
 
      error(0, errno, "Failed to allocate send_buffer");
1461
 
    } else {
1462
 
      if(mlock(send_buffer, send_buffer_length) != 0){
1463
 
        /* Warn but do not treat as fatal error */
1464
 
        if(errno != EPERM and errno != ENOMEM){
1465
 
          error(0, errno, "Failed to lock memory for password"
1466
 
                " buffer");
1467
 
        }
1468
 
      }
1469
 
      /* “[…] send a single datagram to the socket consisting of the
1470
 
         password string either prefixed with "+" or with "-"
1471
 
         depending on whether the password entry was successful or
1472
 
         not. You may but don't have to include a final NUL byte in
1473
 
         your message.
1474
 
 
1475
 
         — <https://www.freedesktop.org/wiki/Software/systemd/
1476
 
         PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
1477
 
      */
1478
 
      send_buffer[0] = '+';     /* Prefix with "+" */
1479
 
      /* Always add an extra NUL */
1480
 
      send_buffer[password->length + 1] = '\0';
1481
 
      if(password->length > 0){
1482
 
        memcpy(send_buffer + 1, password->data, password->length);
1483
 
      }
1484
 
      errno = 0;
1485
 
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1486
 
                           MSG_NOSIGNAL);
1487
 
      const error_t saved_errno = errno;
1488
 
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1489
 
      explicit_bzero(send_buffer, send_buffer_length);
1490
 
#else
1491
 
      memset(send_buffer, '\0', send_buffer_length);
1492
 
#endif
1493
 
      if(munlock(send_buffer, send_buffer_length) != 0){
1494
 
        error(0, errno, "Failed to unlock memory of send buffer");
1495
 
      }
1496
 
      free(send_buffer);
1497
 
      if(ssret < 0 or ssret < (ssize_t)send_buffer_length){
1498
 
        switch(saved_errno){
1499
 
        case EINTR:
1500
 
        case ENOBUFS:
1501
 
        case ENOMEM:
1502
 
        case EADDRINUSE:
1503
 
        case ECONNREFUSED:
1504
 
        case ECONNRESET:
1505
 
        case ENOENT:
1506
 
        case ETOOMANYREFS:
1507
 
        case EAGAIN:
1508
 
          /* Retry, below */
1509
 
          break;
1510
 
        case EMSGSIZE:
1511
 
          error(0, 0, "Password of size %" PRIuMAX " is too big",
1512
 
                (uintmax_t)password->length);
1513
 
#if __GNUC__ < 7
1514
 
          /* FALLTHROUGH */
1515
 
#else
1516
 
          __attribute__((fallthrough));
1517
 
#endif
1518
 
        case 0:
1519
 
          if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1520
 
            error(0, 0, "Password only partially sent to socket");
1521
 
          }
1522
 
#if __GNUC__ < 7
1523
 
          /* FALLTHROUGH */
1524
 
#else
1525
 
          __attribute__((fallthrough));
1526
 
#endif
1527
 
        default:
1528
 
          error(0, saved_errno, "Failed to send() to socket %s",
1529
 
                filename);
1530
 
          if(not string_set_add(cancelled_filenames,
1531
 
                                question_filename)){
1532
 
            error(0, errno, "Failed to cancel question for file %s",
1533
 
                  question_filename);
1534
 
          }
1535
 
          cleanup_task(&task);
1536
 
          return;
1537
 
        }
1538
 
      } else {
1539
 
        /* Success */
1540
 
        cleanup_task(&task);
1541
 
        return;
1542
 
      }
1543
 
    }
1544
 
  }
1545
 
 
1546
 
  /* We failed or are not ready yet; retry later */
1547
 
 
1548
 
  if(not add_to_queue(queue, task)){
1549
 
    error(0, errno, "Failed to add send_password_to_socket for"
1550
 
          " file %s and socket %s to queue", question_filename,
1551
 
          filename);
1552
 
    cleanup_task(&task);
1553
 
  }
1554
 
 
1555
 
  /* Add the fd to the epoll set */
1556
 
  if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
1557
 
               &(struct epoll_event){ .events=EPOLLOUT })
1558
 
     != 0){
1559
 
    error(0, errno, "Failed to add socket file descriptor %d for"
1560
 
          " socket %s to epoll set", fd, filename);
1561
 
    /* Force the added task (send_password_to_socket) to run again, at
1562
 
       most one second from now */
1563
 
    if((queue->next_run == 0)
1564
 
       or (queue->next_run > (*current_time + 1000000))){
1565
 
      queue->next_run = *current_time + 1000000;
1566
 
    }
1567
 
  }
1568
 
}
1569
 
 
1570
 
__attribute__((warn_unused_result))
1571
 
bool add_existing_questions(task_queue *const queue,
1572
 
                            const int epoll_fd,
1573
 
                            buffer *const password,
1574
 
                            string_set *cancelled_filenames,
1575
 
                            const mono_microsecs *const current_time,
1576
 
                            bool *const mandos_client_exited,
1577
 
                            bool *const password_is_read,
1578
 
                            const char *const dirname){
1579
 
  __attribute__((cleanup(cleanup_string)))
1580
 
    char *dir_pattern = NULL;
1581
 
  const int ret = asprintf(&dir_pattern, "%s/ask.*", dirname);
1582
 
  if(ret < 0 or dir_pattern == NULL){
1583
 
    error(0, errno, "Could not create glob pattern for directory %s",
1584
 
          dirname);
1585
 
    return false;
1586
 
  }
1587
 
  __attribute__((cleanup(globfree)))
1588
 
    glob_t question_filenames = {};
1589
 
  switch(glob(dir_pattern, GLOB_ERR | GLOB_NOSORT | GLOB_MARK,
1590
 
              NULL, &question_filenames)){
1591
 
  case GLOB_ABORTED:
1592
 
  default:
1593
 
    error(0, errno, "Failed to open directory %s", dirname);
1594
 
    return false;
1595
 
  case GLOB_NOMATCH:
1596
 
    error(0, errno, "There are no question files in %s", dirname);
1597
 
    return false;
1598
 
  case GLOB_NOSPACE:
1599
 
    error(0, errno, "Could not allocate memory for question file"
1600
 
          " names in %s", dirname);
1601
 
#if __GNUC__ < 7
1602
 
    /* FALLTHROUGH */
1603
 
#else
1604
 
    __attribute__((fallthrough));
1605
 
#endif
1606
 
  case 0:
1607
 
    for(size_t i = 0; i < question_filenames.gl_pathc; i++){
1608
 
      char *const question_filename = strdup(question_filenames
1609
 
                                             .gl_pathv[i]);
1610
 
      const task_context task = {
1611
 
        .func=open_and_parse_question,
1612
 
        .epoll_fd=epoll_fd,
1613
 
        .question_filename=question_filename,
1614
 
        .filename=question_filename,
1615
 
        .password=password,
1616
 
        .cancelled_filenames=cancelled_filenames,
1617
 
        .current_time=current_time,
1618
 
        .mandos_client_exited=mandos_client_exited,
1619
 
        .password_is_read=password_is_read,
1620
 
      };
1621
 
 
1622
 
      if(question_filename == NULL
1623
 
         or not add_to_queue(queue, task)){
1624
 
        error(0, errno, "Failed to add open_and_parse_question for"
1625
 
              " file %s to queue",
1626
 
              question_filenames.gl_pathv[i]);
1627
 
        free(question_filename);
1628
 
      } else {
1629
 
        queue->next_run = 1;
1630
 
      }
1631
 
    }
1632
 
    return true;
1633
 
  }
1634
 
}
1635
 
 
1636
 
__attribute__((nonnull, warn_unused_result))
1637
 
bool wait_for_event(const int epoll_fd,
1638
 
                    const mono_microsecs queue_next_run,
1639
 
                    const mono_microsecs current_time){
1640
 
  __attribute__((const))
1641
 
    int milliseconds_to_wait(const mono_microsecs currtime,
1642
 
                             const mono_microsecs nextrun){
1643
 
    if(currtime >= nextrun){
1644
 
      return 0;
1645
 
    }
1646
 
    const uintmax_t wait_time_ms = (nextrun - currtime) / 1000;
1647
 
    if(wait_time_ms > (uintmax_t)INT_MAX){
1648
 
      return INT_MAX;
1649
 
    }
1650
 
    return (int)wait_time_ms;
1651
 
  }
1652
 
 
1653
 
  const int wait_time_ms = milliseconds_to_wait(current_time,
1654
 
                                                queue_next_run);
1655
 
 
1656
 
  /* Prepare unblocking of SIGCHLD during epoll_pwait */
1657
 
  sigset_t temporary_unblocked_sigmask;
1658
 
  /* Get current signal mask */
1659
 
  if(pthread_sigmask(-1, NULL, &temporary_unblocked_sigmask) != 0){
1660
 
    return false;
1661
 
  }
1662
 
  /* Remove SIGCHLD from the signal mask */
1663
 
  if(sigdelset(&temporary_unblocked_sigmask, SIGCHLD) != 0){
1664
 
    return false;
1665
 
  }
1666
 
  struct epoll_event events[8]; /* Ignored */
1667
 
  int ret = epoll_pwait(epoll_fd, events,
1668
 
                        sizeof(events) / sizeof(struct epoll_event),
1669
 
                        queue_next_run == 0 ? -1 : (int)wait_time_ms,
1670
 
                        &temporary_unblocked_sigmask);
1671
 
  if(ret < 0 and errno != EINTR){
1672
 
    error(0, errno, "Failed epoll_pwait(epfd=%d, ..., timeout=%d,"
1673
 
          " ...", epoll_fd,
1674
 
          queue_next_run == 0 ? -1 : (int)wait_time_ms);
1675
 
    return false;
1676
 
  }
1677
 
  return clear_all_fds_from_epoll_set(epoll_fd);
1678
 
}
1679
 
 
1680
 
bool clear_all_fds_from_epoll_set(const int epoll_fd){
1681
 
  /* Create a new empty epoll set */
1682
 
  __attribute__((cleanup(cleanup_close)))
1683
 
    const int new_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
1684
 
  if(new_epoll_fd < 0){
1685
 
    return false;
1686
 
  }
1687
 
  /* dup3() the new epoll set fd over the old one, replacing it */
1688
 
  if(dup3(new_epoll_fd, epoll_fd, O_CLOEXEC) < 0){
1689
 
    return false;
1690
 
  }
1691
 
  return true;
1692
 
}
1693
 
 
1694
 
__attribute__((nonnull, warn_unused_result))
1695
 
bool run_queue(task_queue **const queue,
1696
 
               string_set *const cancelled_filenames,
1697
 
               bool *const quit_now){
1698
 
 
1699
 
  task_queue *new_queue = create_queue();
1700
 
  if(new_queue == NULL){
1701
 
    return false;
1702
 
  }
1703
 
 
1704
 
  __attribute__((cleanup(string_set_clear)))
1705
 
    string_set old_cancelled_filenames = {};
1706
 
  string_set_swap(cancelled_filenames, &old_cancelled_filenames);
1707
 
 
1708
 
  /* Declare i outside the for loop, since we might need i after the
1709
 
     loop in case we aborted in the middle */
1710
 
  size_t i;
1711
 
  for(i=0; i < (*queue)->length and not *quit_now; i++){
1712
 
    task_context *const task = &((*queue)->tasks[i]);
1713
 
    const char *const question_filename = task->question_filename;
1714
 
    /* Skip any task referencing a cancelled question filename */
1715
 
    if(question_filename != NULL
1716
 
       and string_set_contains(old_cancelled_filenames,
1717
 
                               question_filename)){
1718
 
      cleanup_task(task);
1719
 
      continue;
1720
 
    }
1721
 
    task->func(*task, new_queue);
1722
 
  }
1723
 
 
1724
 
  if(*quit_now){
1725
 
    /* we might be in the middle of the queue, so clean up any
1726
 
       remaining tasks in the current queue */
1727
 
    for(; i < (*queue)->length; i++){
1728
 
      cleanup_task(&((*queue)->tasks[i]));
1729
 
    }
1730
 
    free_queue(*queue);
1731
 
    *queue = new_queue;
1732
 
    new_queue = NULL;
1733
 
    return false;
1734
 
  }
1735
 
  free_queue(*queue);
1736
 
  *queue = new_queue;
1737
 
  new_queue = NULL;
1738
 
 
1739
 
  return true;
1740
 
}
1741
 
 
1742
 
/* End of regular code section */
1743
 
 
1744
 
/* Start of tests section; here are the tests for the above code */
1745
 
 
1746
 
/* This "fixture" data structure is used by the test setup and
1747
 
   teardown functions */
1748
 
typedef struct {
1749
 
  struct sigaction orig_sigaction;
1750
 
  sigset_t orig_sigmask;
1751
 
} test_fixture;
1752
 
 
1753
 
static void test_setup(test_fixture *fixture,
1754
 
                       __attribute__((unused))
1755
 
                       gconstpointer user_data){
1756
 
  g_assert_true(setup_signal_handler(&fixture->orig_sigaction));
1757
 
  g_assert_true(block_sigchld(&fixture->orig_sigmask));
1758
 
}
1759
 
 
1760
 
static void test_teardown(test_fixture *fixture,
1761
 
                          __attribute__((unused))
1762
 
                          gconstpointer user_data){
1763
 
  g_assert_true(restore_signal_handler(&fixture->orig_sigaction));
1764
 
  g_assert_true(restore_sigmask(&fixture->orig_sigmask));
1765
 
}
1766
 
 
1767
 
/* Utility function used by tests to search queue for matching task */
1768
 
__attribute__((pure, nonnull, warn_unused_result))
1769
 
static task_context *find_matching_task(const task_queue *const queue,
1770
 
                                        const task_context task){
1771
 
  /* The argument "task" structure is a pattern to match; 0 in any
1772
 
     member means any value matches, otherwise the value must match.
1773
 
     The filename strings are compared by strcmp(), not by pointer. */
1774
 
  for(size_t i = 0; i < queue->length; i++){
1775
 
    task_context *const current_task = queue->tasks+i;
1776
 
    /* Check all members of task_context, if set to a non-zero value.
1777
 
       If a member does not match, continue to next task in queue */
1778
 
 
1779
 
    /* task_func *const func */
1780
 
    if(task.func != NULL and current_task->func != task.func){
1781
 
      continue;
1782
 
    }
1783
 
    /* char *const question_filename; */
1784
 
    if(task.question_filename != NULL
1785
 
       and (current_task->question_filename == NULL
1786
 
            or strcmp(current_task->question_filename,
1787
 
                      task.question_filename) != 0)){
1788
 
      continue;
1789
 
    }
1790
 
    /* const pid_t pid; */
1791
 
    if(task.pid != 0 and current_task->pid != task.pid){
1792
 
      continue;
1793
 
    }
1794
 
    /* const int epoll_fd; */
1795
 
    if(task.epoll_fd != 0
1796
 
       and current_task->epoll_fd != task.epoll_fd){
1797
 
      continue;
1798
 
    }
1799
 
    /* bool *const quit_now; */
1800
 
    if(task.quit_now != NULL
1801
 
       and current_task->quit_now != task.quit_now){
1802
 
      continue;
1803
 
    }
1804
 
    /* const int fd; */
1805
 
    if(task.fd != 0 and current_task->fd != task.fd){
1806
 
      continue;
1807
 
    }
1808
 
    /* bool *const mandos_client_exited; */
1809
 
    if(task.mandos_client_exited != NULL
1810
 
       and current_task->mandos_client_exited
1811
 
       != task.mandos_client_exited){
1812
 
      continue;
1813
 
    }
1814
 
    /* buffer *const password; */
1815
 
    if(task.password != NULL
1816
 
       and current_task->password != task.password){
1817
 
      continue;
1818
 
    }
1819
 
    /* bool *const password_is_read; */
1820
 
    if(task.password_is_read != NULL
1821
 
       and current_task->password_is_read != task.password_is_read){
1822
 
      continue;
1823
 
    }
1824
 
    /* char *filename; */
1825
 
    if(task.filename != NULL
1826
 
       and (current_task->filename == NULL
1827
 
            or strcmp(current_task->filename, task.filename) != 0)){
1828
 
      continue;
1829
 
    }
1830
 
    /* string_set *const cancelled_filenames; */
1831
 
    if(task.cancelled_filenames != NULL
1832
 
       and current_task->cancelled_filenames
1833
 
       != task.cancelled_filenames){
1834
 
      continue;
1835
 
    }
1836
 
    /* const mono_microsecs notafter; */
1837
 
    if(task.notafter != 0
1838
 
       and current_task->notafter != task.notafter){
1839
 
      continue;
1840
 
    }
1841
 
    /* const mono_microsecs *const current_time; */
1842
 
    if(task.current_time != NULL
1843
 
       and current_task->current_time != task.current_time){
1844
 
      continue;
1845
 
    }
1846
 
    /* Current task matches all members; return it */
1847
 
    return current_task;
1848
 
  }
1849
 
  /* No task in queue matches passed pattern task */
1850
 
  return NULL;
1851
 
}
1852
 
 
1853
 
static void test_create_queue(__attribute__((unused))
1854
 
                              test_fixture *fixture,
1855
 
                              __attribute__((unused))
1856
 
                              gconstpointer user_data){
1857
 
  __attribute__((cleanup(cleanup_queue)))
1858
 
    task_queue *const queue = create_queue();
1859
 
  g_assert_nonnull(queue);
1860
 
  g_assert_null(queue->tasks);
1861
 
  g_assert_true(queue->length == 0);
1862
 
  g_assert_true(queue->next_run == 0);
1863
 
}
1864
 
 
1865
 
static task_func dummy_func;
1866
 
 
1867
 
static void test_add_to_queue(__attribute__((unused))
1868
 
                              test_fixture *fixture,
1869
 
                              __attribute__((unused))
1870
 
                              gconstpointer user_data){
1871
 
  __attribute__((cleanup(cleanup_queue)))
1872
 
    task_queue *queue = create_queue();
1873
 
  g_assert_nonnull(queue);
1874
 
 
1875
 
  g_assert_true(add_to_queue(queue,
1876
 
                             (task_context){ .func=dummy_func }));
1877
 
  g_assert_true(queue->length == 1);
1878
 
  g_assert_nonnull(queue->tasks);
1879
 
  g_assert_true(queue->tasks[0].func == dummy_func);
1880
 
}
1881
 
 
1882
 
static void dummy_func(__attribute__((unused))
1883
 
                       const task_context task,
1884
 
                       __attribute__((unused))
1885
 
                       task_queue *const queue){
1886
 
}
1887
 
 
1888
 
static void test_queue_has_question_empty(__attribute__((unused))
1889
 
                                          test_fixture *fixture,
1890
 
                                          __attribute__((unused))
1891
 
                                          gconstpointer user_data){
1892
 
  __attribute__((cleanup(cleanup_queue)))
1893
 
    task_queue *queue = create_queue();
1894
 
  g_assert_nonnull(queue);
1895
 
  g_assert_false(queue_has_question(queue));
1896
 
}
1897
 
 
1898
 
static void test_queue_has_question_false(__attribute__((unused))
1899
 
                                          test_fixture *fixture,
1900
 
                                          __attribute__((unused))
1901
 
                                          gconstpointer user_data){
1902
 
  __attribute__((cleanup(cleanup_queue)))
1903
 
    task_queue *queue = create_queue();
1904
 
  g_assert_nonnull(queue);
1905
 
  g_assert_true(add_to_queue(queue,
1906
 
                             (task_context){ .func=dummy_func }));
1907
 
  g_assert_false(queue_has_question(queue));
1908
 
}
1909
 
 
1910
 
static void test_queue_has_question_true(__attribute__((unused))
1911
 
                                         test_fixture *fixture,
1912
 
                                         __attribute__((unused))
1913
 
                                         gconstpointer user_data){
1914
 
  __attribute__((cleanup(cleanup_queue)))
1915
 
    task_queue *queue = create_queue();
1916
 
  g_assert_nonnull(queue);
1917
 
  char *const question_filename
1918
 
    = strdup("/nonexistent/question_filename");
1919
 
  g_assert_nonnull(question_filename);
1920
 
  task_context task = {
1921
 
    .func=dummy_func,
1922
 
    .question_filename=question_filename,
1923
 
  };
1924
 
  g_assert_true(add_to_queue(queue, task));
1925
 
  g_assert_true(queue_has_question(queue));
1926
 
}
1927
 
 
1928
 
static void test_queue_has_question_false2(__attribute__((unused))
1929
 
                                           test_fixture *fixture,
1930
 
                                           __attribute__((unused))
1931
 
                                           gconstpointer user_data){
1932
 
  __attribute__((cleanup(cleanup_queue)))
1933
 
    task_queue *queue = create_queue();
1934
 
  g_assert_nonnull(queue);
1935
 
  task_context task = { .func=dummy_func };
1936
 
  g_assert_true(add_to_queue(queue, task));
1937
 
  g_assert_true(add_to_queue(queue, task));
1938
 
  g_assert_cmpint((int)queue->length, ==, 2);
1939
 
  g_assert_false(queue_has_question(queue));
1940
 
}
1941
 
 
1942
 
static void test_queue_has_question_true2(__attribute__((unused))
1943
 
                                          test_fixture *fixture,
1944
 
                                          __attribute__((unused))
1945
 
                                          gconstpointer user_data){
1946
 
  __attribute__((cleanup(cleanup_queue)))
1947
 
    task_queue *queue = create_queue();
1948
 
  g_assert_nonnull(queue);
1949
 
  task_context task1 = { .func=dummy_func };
1950
 
  g_assert_true(add_to_queue(queue, task1));
1951
 
  char *const question_filename
1952
 
    = strdup("/nonexistent/question_filename");
1953
 
  g_assert_nonnull(question_filename);
1954
 
  task_context task2 = {
1955
 
    .func=dummy_func,
1956
 
    .question_filename=question_filename,
1957
 
  };
1958
 
  g_assert_true(add_to_queue(queue, task2));
1959
 
  g_assert_cmpint((int)queue->length, ==, 2);
1960
 
  g_assert_true(queue_has_question(queue));
1961
 
}
1962
 
 
1963
 
static void test_cleanup_buffer(__attribute__((unused))
1964
 
                                test_fixture *fixture,
1965
 
                                __attribute__((unused))
1966
 
                                gconstpointer user_data){
1967
 
  buffer buf = {};
1968
 
 
1969
 
  const size_t buffersize = 10;
1970
 
 
1971
 
  buf.data = malloc(buffersize);
1972
 
  g_assert_nonnull(buf.data);
1973
 
  if(mlock(buf.data, buffersize) != 0){
1974
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
1975
 
  }
1976
 
 
1977
 
  cleanup_buffer(&buf);
1978
 
  g_assert_null(buf.data);
1979
 
}
1980
 
 
1981
 
static
1982
 
void test_string_set_new_set_contains_nothing(__attribute__((unused))
1983
 
                                              test_fixture *fixture,
1984
 
                                              __attribute__((unused))
1985
 
                                              gconstpointer
1986
 
                                              user_data){
1987
 
  __attribute__((cleanup(string_set_clear)))
1988
 
    string_set set = {};
1989
 
  g_assert_false(string_set_contains(set, "")); /* Empty string */
1990
 
  g_assert_false(string_set_contains(set, "test_string"));
1991
 
}
1992
 
 
1993
 
static void
1994
 
test_string_set_with_added_string_contains_it(__attribute__((unused))
1995
 
                                              test_fixture *fixture,
1996
 
                                              __attribute__((unused))
1997
 
                                              gconstpointer
1998
 
                                              user_data){
1999
 
  __attribute__((cleanup(string_set_clear)))
2000
 
    string_set set = {};
2001
 
  g_assert_true(string_set_add(&set, "test_string"));
2002
 
  g_assert_true(string_set_contains(set, "test_string"));
2003
 
}
2004
 
 
2005
 
static void
2006
 
test_string_set_cleared_does_not_contain_str(__attribute__((unused))
2007
 
                                             test_fixture *fixture,
2008
 
                                             __attribute__((unused))
2009
 
                                             gconstpointer user_data){
2010
 
  __attribute__((cleanup(string_set_clear)))
2011
 
    string_set set = {};
2012
 
  g_assert_true(string_set_add(&set, "test_string"));
2013
 
  string_set_clear(&set);
2014
 
  g_assert_false(string_set_contains(set, "test_string"));
2015
 
}
2016
 
 
2017
 
static
2018
 
void test_string_set_swap_one_with_empty(__attribute__((unused))
2019
 
                                         test_fixture *fixture,
2020
 
                                         __attribute__((unused))
2021
 
                                         gconstpointer user_data){
2022
 
  __attribute__((cleanup(string_set_clear)))
2023
 
    string_set set1 = {};
2024
 
  __attribute__((cleanup(string_set_clear)))
2025
 
    string_set set2 = {};
2026
 
  g_assert_true(string_set_add(&set1, "test_string1"));
2027
 
  string_set_swap(&set1, &set2);
2028
 
  g_assert_false(string_set_contains(set1, "test_string1"));
2029
 
  g_assert_true(string_set_contains(set2, "test_string1"));
2030
 
}
2031
 
 
2032
 
static
2033
 
void test_string_set_swap_empty_with_one(__attribute__((unused))
2034
 
                                         test_fixture *fixture,
2035
 
                                         __attribute__((unused))
2036
 
                                         gconstpointer user_data){
2037
 
  __attribute__((cleanup(string_set_clear)))
2038
 
    string_set set1 = {};
2039
 
  __attribute__((cleanup(string_set_clear)))
2040
 
    string_set set2 = {};
2041
 
  g_assert_true(string_set_add(&set2, "test_string2"));
2042
 
  string_set_swap(&set1, &set2);
2043
 
  g_assert_true(string_set_contains(set1, "test_string2"));
2044
 
  g_assert_false(string_set_contains(set2, "test_string2"));
2045
 
}
2046
 
 
2047
 
static void test_string_set_swap_one_with_one(__attribute__((unused))
2048
 
                                              test_fixture *fixture,
2049
 
                                              __attribute__((unused))
2050
 
                                              gconstpointer
2051
 
                                              user_data){
2052
 
  __attribute__((cleanup(string_set_clear)))
2053
 
    string_set set1 = {};
2054
 
  __attribute__((cleanup(string_set_clear)))
2055
 
    string_set set2 = {};
2056
 
  g_assert_true(string_set_add(&set1, "test_string1"));
2057
 
  g_assert_true(string_set_add(&set2, "test_string2"));
2058
 
  string_set_swap(&set1, &set2);
2059
 
  g_assert_false(string_set_contains(set1, "test_string1"));
2060
 
  g_assert_true(string_set_contains(set1, "test_string2"));
2061
 
  g_assert_false(string_set_contains(set2, "test_string2"));
2062
 
  g_assert_true(string_set_contains(set2, "test_string1"));
2063
 
}
2064
 
 
2065
 
static bool fd_has_cloexec_and_nonblock(const int);
2066
 
 
2067
 
static bool epoll_set_contains(int, int, uint32_t);
2068
 
 
2069
 
static void test_start_mandos_client(test_fixture *fixture,
2070
 
                                     __attribute__((unused))
2071
 
                                     gconstpointer user_data){
2072
 
 
2073
 
  bool mandos_client_exited = false;
2074
 
  bool quit_now = false;
2075
 
  __attribute__((cleanup(cleanup_close)))
2076
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2077
 
  g_assert_cmpint(epoll_fd, >=, 0);
2078
 
  __attribute__((cleanup(cleanup_queue)))
2079
 
    task_queue *queue = create_queue();
2080
 
  g_assert_nonnull(queue);
2081
 
  buffer password = {};
2082
 
  bool password_is_read = false;
2083
 
  const char helper_directory[] = "/nonexistent";
2084
 
  const char *const argv[] = { "/bin/true", NULL };
2085
 
 
2086
 
  g_assert_true(start_mandos_client(queue, epoll_fd,
2087
 
                                    &mandos_client_exited, &quit_now,
2088
 
                                    &password, &password_is_read,
2089
 
                                    &fixture->orig_sigaction,
2090
 
                                    fixture->orig_sigmask,
2091
 
                                    helper_directory, 0, 0, argv));
2092
 
 
2093
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
2094
 
 
2095
 
  const task_context *const added_wait_task
2096
 
    = find_matching_task(queue, (task_context){
2097
 
        .func=wait_for_mandos_client_exit,
2098
 
        .mandos_client_exited=&mandos_client_exited,
2099
 
        .quit_now=&quit_now,
2100
 
      });
2101
 
  g_assert_nonnull(added_wait_task);
2102
 
  g_assert_cmpint(added_wait_task->pid, >, 0);
2103
 
  g_assert_cmpint(kill(added_wait_task->pid, SIGKILL), ==, 0);
2104
 
  waitpid(added_wait_task->pid, NULL, 0);
2105
 
 
2106
 
  const task_context *const added_read_task
2107
 
    = find_matching_task(queue, (task_context){
2108
 
        .func=read_mandos_client_output,
2109
 
        .epoll_fd=epoll_fd,
2110
 
        .password=&password,
2111
 
        .password_is_read=&password_is_read,
2112
 
        .quit_now=&quit_now,
2113
 
      });
2114
 
  g_assert_nonnull(added_read_task);
2115
 
  g_assert_cmpint(added_read_task->fd, >, 2);
2116
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
2117
 
  g_assert_true(epoll_set_contains(epoll_fd, added_read_task->fd,
2118
 
                                   EPOLLIN | EPOLLRDHUP));
2119
 
}
2120
 
 
2121
 
static bool fd_has_cloexec_and_nonblock(const int fd){
2122
 
  const int socket_fd_flags = fcntl(fd, F_GETFD, 0);
2123
 
  const int socket_file_flags = fcntl(fd, F_GETFL, 0);
2124
 
  return ((socket_fd_flags >= 0)
2125
 
          and (socket_fd_flags & FD_CLOEXEC)
2126
 
          and (socket_file_flags >= 0)
2127
 
          and (socket_file_flags & O_NONBLOCK));
2128
 
}
2129
 
 
2130
 
__attribute__((const))
2131
 
bool is_privileged(void){
2132
 
  uid_t user = getuid() + 1;
2133
 
  if(user == 0){                /* Overflow check */
2134
 
    user++;
2135
 
  }
2136
 
  gid_t group = getuid() + 1;
2137
 
  if(group == 0){               /* Overflow check */
2138
 
    group++;
2139
 
  }
2140
 
  const pid_t pid = fork();
2141
 
  if(pid == 0){                 /* Child */
2142
 
    if(setresgid((uid_t)-1, group, group) == -1){
2143
 
      if(errno != EPERM){
2144
 
        error(EXIT_FAILURE, errno, "Failed to setresgid(-1, %" PRIuMAX
2145
 
              ", %" PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
2146
 
      }
2147
 
      exit(EXIT_FAILURE);
2148
 
    }
2149
 
    if(setresuid((uid_t)-1, user, user) == -1){
2150
 
      if(errno != EPERM){
2151
 
        error(EXIT_FAILURE, errno, "Failed to setresuid(-1, %" PRIuMAX
2152
 
              ", %" PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
2153
 
      }
2154
 
      exit(EXIT_FAILURE);
2155
 
    }
2156
 
    exit(EXIT_SUCCESS);
2157
 
  }
2158
 
  int status;
2159
 
  waitpid(pid, &status, 0);
2160
 
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2161
 
    return true;
2162
 
  }
2163
 
  return false;
2164
 
}
2165
 
 
2166
 
static bool epoll_set_contains(int epoll_fd, int fd, uint32_t events){
2167
 
  /* Only scan for events in this eventmask */
2168
 
  const uint32_t eventmask = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
2169
 
  __attribute__((cleanup(cleanup_string)))
2170
 
    char *fdinfo_name = NULL;
2171
 
  int ret = asprintf(&fdinfo_name, "/proc/self/fdinfo/%d", epoll_fd);
2172
 
  g_assert_cmpint(ret, >, 0);
2173
 
  g_assert_nonnull(fdinfo_name);
2174
 
 
2175
 
  FILE *fdinfo = fopen(fdinfo_name, "r");
2176
 
  g_assert_nonnull(fdinfo);
2177
 
  uint32_t reported_events;
2178
 
  buffer line = {};
2179
 
  int found_fd = -1;
2180
 
 
2181
 
  do {
2182
 
    if(getline(&line.data, &line.allocated, fdinfo) < 0){
2183
 
      break;
2184
 
    }
2185
 
    /* See proc(5) for format of /proc/PID/fdinfo/FD for epoll fd's */
2186
 
    if(sscanf(line.data, "tfd: %d events: %" SCNx32 " ",
2187
 
              &found_fd, &reported_events) == 2){
2188
 
      if(found_fd == fd){
2189
 
        break;
2190
 
      }
2191
 
    }
2192
 
  } while(not feof(fdinfo) and not ferror(fdinfo));
2193
 
  g_assert_cmpint(fclose(fdinfo), ==, 0);
2194
 
  free(line.data);
2195
 
  if(found_fd != fd){
2196
 
    return false;
2197
 
  }
2198
 
 
2199
 
  if(events == 0){
2200
 
    /* Don't check events if none are given */
2201
 
    return true;
2202
 
  }
2203
 
  return (reported_events & eventmask) == (events & eventmask);
2204
 
}
2205
 
 
2206
 
static void test_start_mandos_client_execv(test_fixture *fixture,
2207
 
                                           __attribute__((unused))
2208
 
                                           gconstpointer user_data){
2209
 
  bool mandos_client_exited = false;
2210
 
  bool quit_now = false;
2211
 
  __attribute__((cleanup(cleanup_close)))
2212
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2213
 
  g_assert_cmpint(epoll_fd, >=, 0);
2214
 
  __attribute__((cleanup(cleanup_queue)))
2215
 
    task_queue *queue = create_queue();
2216
 
  g_assert_nonnull(queue);
2217
 
  __attribute__((cleanup(cleanup_buffer)))
2218
 
    buffer password = {};
2219
 
  const char helper_directory[] = "/nonexistent";
2220
 
  /* Can't execv("/", ...), so this should fail */
2221
 
  const char *const argv[] = { "/", NULL };
2222
 
 
2223
 
  {
2224
 
    __attribute__((cleanup(cleanup_close)))
2225
 
      const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
2226
 
    g_assert_cmpint(devnull_fd, >=, 0);
2227
 
    __attribute__((cleanup(cleanup_close)))
2228
 
      const int real_stderr_fd = dup(STDERR_FILENO);
2229
 
    g_assert_cmpint(real_stderr_fd, >=, 0);
2230
 
    dup2(devnull_fd, STDERR_FILENO);
2231
 
 
2232
 
    const bool success = start_mandos_client(queue, epoll_fd,
2233
 
                                             &mandos_client_exited,
2234
 
                                             &quit_now,
2235
 
                                             &password,
2236
 
                                             (bool[]){false},
2237
 
                                             &fixture->orig_sigaction,
2238
 
                                             fixture->orig_sigmask,
2239
 
                                             helper_directory, 0, 0,
2240
 
                                             argv);
2241
 
    dup2(real_stderr_fd, STDERR_FILENO);
2242
 
    g_assert_true(success);
2243
 
  }
2244
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 2);
2245
 
 
2246
 
  struct timespec starttime, currtime;
2247
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2248
 
  do {
2249
 
    queue->next_run = 0;
2250
 
    string_set cancelled_filenames = {};
2251
 
 
2252
 
    {
2253
 
      __attribute__((cleanup(cleanup_close)))
2254
 
        const int devnull_fd = open("/dev/null",
2255
 
                                    O_WRONLY | O_CLOEXEC);
2256
 
      g_assert_cmpint(devnull_fd, >=, 0);
2257
 
      __attribute__((cleanup(cleanup_close)))
2258
 
        const int real_stderr_fd = dup(STDERR_FILENO);
2259
 
      g_assert_cmpint(real_stderr_fd, >=, 0);
2260
 
      g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2261
 
      dup2(devnull_fd, STDERR_FILENO);
2262
 
      const bool success = run_queue(&queue, &cancelled_filenames,
2263
 
                                     &quit_now);
2264
 
      dup2(real_stderr_fd, STDERR_FILENO);
2265
 
      if(not success){
2266
 
        break;
2267
 
      }
2268
 
    }
2269
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2270
 
  } while(((queue->length) > 0)
2271
 
          and (not quit_now)
2272
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2273
 
 
2274
 
  g_assert_true(quit_now);
2275
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2276
 
  g_assert_true(mandos_client_exited);
2277
 
}
2278
 
 
2279
 
static void test_start_mandos_client_suid_euid(test_fixture *fixture,
2280
 
                                               __attribute__((unused))
2281
 
                                               gconstpointer
2282
 
                                               user_data){
2283
 
  if(not is_privileged()){
2284
 
    g_test_skip("Not privileged");
2285
 
    return;
2286
 
  }
2287
 
 
2288
 
  bool mandos_client_exited = false;
2289
 
  bool quit_now = false;
2290
 
  __attribute__((cleanup(cleanup_close)))
2291
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2292
 
  g_assert_cmpint(epoll_fd, >=, 0);
2293
 
  __attribute__((cleanup(cleanup_queue)))
2294
 
    task_queue *queue = create_queue();
2295
 
  g_assert_nonnull(queue);
2296
 
  __attribute__((cleanup(cleanup_buffer)))
2297
 
    buffer password = {};
2298
 
  bool password_is_read = false;
2299
 
  const char helper_directory[] = "/nonexistent";
2300
 
  const char *const argv[] = { "/usr/bin/id", "--user", NULL };
2301
 
  uid_t user = 1000;
2302
 
  gid_t group = 1001;
2303
 
 
2304
 
  const bool success = start_mandos_client(queue, epoll_fd,
2305
 
                                           &mandos_client_exited,
2306
 
                                           &quit_now, &password,
2307
 
                                           &password_is_read,
2308
 
                                           &fixture->orig_sigaction,
2309
 
                                           fixture->orig_sigmask,
2310
 
                                           helper_directory, user,
2311
 
                                           group, argv);
2312
 
  g_assert_true(success);
2313
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
2314
 
 
2315
 
  struct timespec starttime, currtime;
2316
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2317
 
  do {
2318
 
    queue->next_run = 0;
2319
 
    string_set cancelled_filenames = {};
2320
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2321
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2322
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2323
 
  } while(((queue->length) > 0)
2324
 
          and (not quit_now)
2325
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2326
 
 
2327
 
  g_assert_false(quit_now);
2328
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2329
 
  g_assert_true(mandos_client_exited);
2330
 
 
2331
 
  g_assert_true(password_is_read);
2332
 
  g_assert_nonnull(password.data);
2333
 
 
2334
 
  uintmax_t id;
2335
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2336
 
                  ==, 1);
2337
 
  g_assert_true((uid_t)id == id);
2338
 
 
2339
 
  g_assert_cmpuint((unsigned int)id, ==, 0);
2340
 
}
2341
 
 
2342
 
static void test_start_mandos_client_suid_egid(test_fixture *fixture,
2343
 
                                               __attribute__((unused))
2344
 
                                               gconstpointer
2345
 
                                               user_data){
2346
 
  if(not is_privileged()){
2347
 
    g_test_skip("Not privileged");
2348
 
    return;
2349
 
  }
2350
 
 
2351
 
  bool mandos_client_exited = false;
2352
 
  bool quit_now = false;
2353
 
  __attribute__((cleanup(cleanup_close)))
2354
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2355
 
  g_assert_cmpint(epoll_fd, >=, 0);
2356
 
  __attribute__((cleanup(cleanup_queue)))
2357
 
    task_queue *queue = create_queue();
2358
 
  g_assert_nonnull(queue);
2359
 
  __attribute__((cleanup(cleanup_buffer)))
2360
 
    buffer password = {};
2361
 
  bool password_is_read = false;
2362
 
  const char helper_directory[] = "/nonexistent";
2363
 
  const char *const argv[] = { "/usr/bin/id", "--group", NULL };
2364
 
  uid_t user = 1000;
2365
 
  gid_t group = 1001;
2366
 
 
2367
 
  const bool success = start_mandos_client(queue, epoll_fd,
2368
 
                                           &mandos_client_exited,
2369
 
                                           &quit_now, &password,
2370
 
                                           &password_is_read,
2371
 
                                           &fixture->orig_sigaction,
2372
 
                                           fixture->orig_sigmask,
2373
 
                                           helper_directory, user,
2374
 
                                           group, argv);
2375
 
  g_assert_true(success);
2376
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
2377
 
 
2378
 
  struct timespec starttime, currtime;
2379
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2380
 
  do {
2381
 
    queue->next_run = 0;
2382
 
    string_set cancelled_filenames = {};
2383
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2384
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2385
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2386
 
  } while(((queue->length) > 0)
2387
 
          and (not quit_now)
2388
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2389
 
 
2390
 
  g_assert_false(quit_now);
2391
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2392
 
  g_assert_true(mandos_client_exited);
2393
 
 
2394
 
  g_assert_true(password_is_read);
2395
 
  g_assert_nonnull(password.data);
2396
 
 
2397
 
  uintmax_t id;
2398
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2399
 
                  ==, 1);
2400
 
  g_assert_true((gid_t)id == id);
2401
 
 
2402
 
  g_assert_cmpuint((unsigned int)id, ==, 0);
2403
 
}
2404
 
 
2405
 
static void test_start_mandos_client_suid_ruid(test_fixture *fixture,
2406
 
                                               __attribute__((unused))
2407
 
                                               gconstpointer
2408
 
                                               user_data){
2409
 
  if(not is_privileged()){
2410
 
    g_test_skip("Not privileged");
2411
 
    return;
2412
 
  }
2413
 
 
2414
 
  bool mandos_client_exited = false;
2415
 
  bool quit_now = false;
2416
 
  __attribute__((cleanup(cleanup_close)))
2417
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2418
 
  g_assert_cmpint(epoll_fd, >=, 0);
2419
 
  __attribute__((cleanup(cleanup_queue)))
2420
 
    task_queue *queue = create_queue();
2421
 
  g_assert_nonnull(queue);
2422
 
  __attribute__((cleanup(cleanup_buffer)))
2423
 
    buffer password = {};
2424
 
  bool password_is_read = false;
2425
 
  const char helper_directory[] = "/nonexistent";
2426
 
  const char *const argv[] = { "/usr/bin/id", "--user", "--real",
2427
 
    NULL };
2428
 
  uid_t user = 1000;
2429
 
  gid_t group = 1001;
2430
 
 
2431
 
  const bool success = start_mandos_client(queue, epoll_fd,
2432
 
                                           &mandos_client_exited,
2433
 
                                           &quit_now, &password,
2434
 
                                           &password_is_read,
2435
 
                                           &fixture->orig_sigaction,
2436
 
                                           fixture->orig_sigmask,
2437
 
                                           helper_directory, user,
2438
 
                                           group, argv);
2439
 
  g_assert_true(success);
2440
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
2441
 
 
2442
 
  struct timespec starttime, currtime;
2443
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2444
 
  do {
2445
 
    queue->next_run = 0;
2446
 
    string_set cancelled_filenames = {};
2447
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2448
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2449
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2450
 
  } while(((queue->length) > 0)
2451
 
          and (not quit_now)
2452
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2453
 
 
2454
 
  g_assert_false(quit_now);
2455
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2456
 
  g_assert_true(mandos_client_exited);
2457
 
 
2458
 
  g_assert_true(password_is_read);
2459
 
  g_assert_nonnull(password.data);
2460
 
 
2461
 
  uintmax_t id;
2462
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2463
 
                  ==, 1);
2464
 
  g_assert_true((uid_t)id == id);
2465
 
 
2466
 
  g_assert_cmpuint((unsigned int)id, ==, user);
2467
 
}
2468
 
 
2469
 
static void test_start_mandos_client_suid_rgid(test_fixture *fixture,
2470
 
                                               __attribute__((unused))
2471
 
                                               gconstpointer
2472
 
                                               user_data){
2473
 
  if(not is_privileged()){
2474
 
    g_test_skip("Not privileged");
2475
 
    return;
2476
 
  }
2477
 
 
2478
 
  bool mandos_client_exited = false;
2479
 
  bool quit_now = false;
2480
 
  __attribute__((cleanup(cleanup_close)))
2481
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2482
 
  g_assert_cmpint(epoll_fd, >=, 0);
2483
 
  __attribute__((cleanup(cleanup_queue)))
2484
 
    task_queue *queue = create_queue();
2485
 
  g_assert_nonnull(queue);
2486
 
  __attribute__((cleanup(cleanup_buffer)))
2487
 
    buffer password = {};
2488
 
  bool password_is_read = false;
2489
 
  const char helper_directory[] = "/nonexistent";
2490
 
  const char *const argv[] = { "/usr/bin/id", "--group", "--real",
2491
 
    NULL };
2492
 
  uid_t user = 1000;
2493
 
  gid_t group = 1001;
2494
 
 
2495
 
  const bool success = start_mandos_client(queue, epoll_fd,
2496
 
                                           &mandos_client_exited,
2497
 
                                           &quit_now, &password,
2498
 
                                           &password_is_read,
2499
 
                                           &fixture->orig_sigaction,
2500
 
                                           fixture->orig_sigmask,
2501
 
                                           helper_directory, user,
2502
 
                                           group, argv);
2503
 
  g_assert_true(success);
2504
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
2505
 
 
2506
 
  struct timespec starttime, currtime;
2507
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2508
 
  do {
2509
 
    queue->next_run = 0;
2510
 
    string_set cancelled_filenames = {};
2511
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2512
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2513
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2514
 
  } while(((queue->length) > 0)
2515
 
          and (not quit_now)
2516
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2517
 
 
2518
 
  g_assert_false(quit_now);
2519
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2520
 
  g_assert_true(mandos_client_exited);
2521
 
 
2522
 
  g_assert_true(password_is_read);
2523
 
  g_assert_nonnull(password.data);
2524
 
 
2525
 
  uintmax_t id;
2526
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
2527
 
                  ==, 1);
2528
 
  g_assert_true((gid_t)id == id);
2529
 
 
2530
 
  g_assert_cmpuint((unsigned int)id, ==, group);
2531
 
}
2532
 
 
2533
 
static void test_start_mandos_client_read(test_fixture *fixture,
2534
 
                                          __attribute__((unused))
2535
 
                                          gconstpointer user_data){
2536
 
  bool mandos_client_exited = false;
2537
 
  bool quit_now = false;
2538
 
  __attribute__((cleanup(cleanup_close)))
2539
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2540
 
  g_assert_cmpint(epoll_fd, >=, 0);
2541
 
  __attribute__((cleanup(cleanup_queue)))
2542
 
    task_queue *queue = create_queue();
2543
 
  g_assert_nonnull(queue);
2544
 
  __attribute__((cleanup(cleanup_buffer)))
2545
 
    buffer password = {};
2546
 
  bool password_is_read = false;
2547
 
  const char dummy_test_password[] = "dummy test password";
2548
 
  const char helper_directory[] = "/nonexistent";
2549
 
  const char *const argv[] = { "/bin/echo", "-n", dummy_test_password,
2550
 
    NULL };
2551
 
 
2552
 
  const bool success = start_mandos_client(queue, epoll_fd,
2553
 
                                           &mandos_client_exited,
2554
 
                                           &quit_now, &password,
2555
 
                                           &password_is_read,
2556
 
                                           &fixture->orig_sigaction,
2557
 
                                           fixture->orig_sigmask,
2558
 
                                           helper_directory, 0, 0,
2559
 
                                           argv);
2560
 
  g_assert_true(success);
2561
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
2562
 
 
2563
 
  struct timespec starttime, currtime;
2564
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2565
 
  do {
2566
 
    queue->next_run = 0;
2567
 
    string_set cancelled_filenames = {};
2568
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2569
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2570
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2571
 
  } while(((queue->length) > 0)
2572
 
          and (not quit_now)
2573
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2574
 
 
2575
 
  g_assert_false(quit_now);
2576
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2577
 
  g_assert_true(mandos_client_exited);
2578
 
 
2579
 
  g_assert_true(password_is_read);
2580
 
  g_assert_cmpint((int)password.length, ==,
2581
 
                  sizeof(dummy_test_password)-1);
2582
 
  g_assert_nonnull(password.data);
2583
 
  g_assert_cmpint(memcmp(dummy_test_password, password.data,
2584
 
                         sizeof(dummy_test_password)-1), ==, 0);
2585
 
}
2586
 
 
2587
 
static
2588
 
void test_start_mandos_client_helper_directory(test_fixture *fixture,
2589
 
                                               __attribute__((unused))
2590
 
                                               gconstpointer
2591
 
                                               user_data){
2592
 
  bool mandos_client_exited = false;
2593
 
  bool quit_now = false;
2594
 
  __attribute__((cleanup(cleanup_close)))
2595
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2596
 
  g_assert_cmpint(epoll_fd, >=, 0);
2597
 
  __attribute__((cleanup(cleanup_queue)))
2598
 
    task_queue *queue = create_queue();
2599
 
  g_assert_nonnull(queue);
2600
 
  __attribute__((cleanup(cleanup_buffer)))
2601
 
    buffer password = {};
2602
 
  bool password_is_read = false;
2603
 
  const char helper_directory[] = "/nonexistent";
2604
 
  const char *const argv[] = { "/bin/sh", "-c",
2605
 
    "echo -n ${MANDOSPLUGINHELPERDIR}", NULL };
2606
 
 
2607
 
  const bool success = start_mandos_client(queue, epoll_fd,
2608
 
                                           &mandos_client_exited,
2609
 
                                           &quit_now, &password,
2610
 
                                           &password_is_read,
2611
 
                                           &fixture->orig_sigaction,
2612
 
                                           fixture->orig_sigmask,
2613
 
                                           helper_directory, 0, 0,
2614
 
                                           argv);
2615
 
  g_assert_true(success);
2616
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
2617
 
 
2618
 
  struct timespec starttime, currtime;
2619
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2620
 
  do {
2621
 
    queue->next_run = 0;
2622
 
    string_set cancelled_filenames = {};
2623
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2624
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2625
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2626
 
  } while(((queue->length) > 0)
2627
 
          and (not quit_now)
2628
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2629
 
 
2630
 
  g_assert_false(quit_now);
2631
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2632
 
  g_assert_true(mandos_client_exited);
2633
 
 
2634
 
  g_assert_true(password_is_read);
2635
 
  g_assert_cmpint((int)password.length, ==,
2636
 
                  sizeof(helper_directory)-1);
2637
 
  g_assert_nonnull(password.data);
2638
 
  g_assert_cmpint(memcmp(helper_directory, password.data,
2639
 
                         sizeof(helper_directory)-1), ==, 0);
2640
 
}
2641
 
 
2642
 
__attribute__((nonnull, warn_unused_result))
2643
 
static bool proc_status_sigblk_to_sigset(const char *const,
2644
 
                                         sigset_t *const);
2645
 
 
2646
 
static void test_start_mandos_client_sigmask(test_fixture *fixture,
2647
 
                                             __attribute__((unused))
2648
 
                                             gconstpointer user_data){
2649
 
  bool mandos_client_exited = false;
2650
 
  bool quit_now = false;
2651
 
  __attribute__((cleanup(cleanup_close)))
2652
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2653
 
  g_assert_cmpint(epoll_fd, >=, 0);
2654
 
  __attribute__((cleanup(cleanup_queue)))
2655
 
    task_queue *queue = create_queue();
2656
 
  g_assert_nonnull(queue);
2657
 
  __attribute__((cleanup(cleanup_buffer)))
2658
 
    buffer password = {};
2659
 
  bool password_is_read = false;
2660
 
  const char helper_directory[] = "/nonexistent";
2661
 
  /* see proc(5) for format of /proc/self/status */
2662
 
  const char *const argv[] = { "/usr/bin/awk",
2663
 
    "$1==\"SigBlk:\"{ print $2 }", "/proc/self/status", NULL };
2664
 
 
2665
 
  g_assert_true(start_mandos_client(queue, epoll_fd,
2666
 
                                    &mandos_client_exited, &quit_now,
2667
 
                                    &password, &password_is_read,
2668
 
                                    &fixture->orig_sigaction,
2669
 
                                    fixture->orig_sigmask,
2670
 
                                    helper_directory, 0, 0, argv));
2671
 
 
2672
 
  struct timespec starttime, currtime;
2673
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2674
 
  do {
2675
 
    queue->next_run = 0;
2676
 
    string_set cancelled_filenames = {};
2677
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2678
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
2679
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2680
 
  } while((not (mandos_client_exited and password_is_read))
2681
 
          and (not quit_now)
2682
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2683
 
  g_assert_true(mandos_client_exited);
2684
 
  g_assert_true(password_is_read);
2685
 
 
2686
 
  sigset_t parsed_sigmask;
2687
 
  g_assert_true(proc_status_sigblk_to_sigset(password.data,
2688
 
                                             &parsed_sigmask));
2689
 
 
2690
 
  for(int signum = 1; signum < NSIG; signum++){
2691
 
    const bool has_signal = sigismember(&parsed_sigmask, signum);
2692
 
    if(sigismember(&fixture->orig_sigmask, signum)){
2693
 
      g_assert_true(has_signal);
2694
 
    } else {
2695
 
      g_assert_false(has_signal);
2696
 
    }
2697
 
  }
2698
 
}
2699
 
 
2700
 
__attribute__((nonnull, warn_unused_result))
2701
 
static bool proc_status_sigblk_to_sigset(const char *const sigblk,
2702
 
                                         sigset_t *const sigmask){
2703
 
  /* parse /proc/PID/status SigBlk value and convert to a sigset_t */
2704
 
  uintmax_t scanned_sigmask;
2705
 
  if(sscanf(sigblk, "%" SCNxMAX " ", &scanned_sigmask) != 1){
2706
 
    return false;
2707
 
  }
2708
 
  if(sigemptyset(sigmask) != 0){
2709
 
    return false;
2710
 
  }
2711
 
  for(int signum = 1; signum < NSIG; signum++){
2712
 
    if(scanned_sigmask & ((uintmax_t)1 << (signum-1))){
2713
 
      if(sigaddset(sigmask, signum) != 0){
2714
 
        return false;
2715
 
      }
2716
 
    }
2717
 
  }
2718
 
  return true;
2719
 
}
2720
 
 
2721
 
static void run_task_with_stderr_to_dev_null(const task_context task,
2722
 
                                             task_queue *const queue);
2723
 
 
2724
 
static
2725
 
void test_wait_for_mandos_client_exit_badpid(__attribute__((unused))
2726
 
                                             test_fixture *fixture,
2727
 
                                             __attribute__((unused))
2728
 
                                             gconstpointer user_data){
2729
 
 
2730
 
  bool mandos_client_exited = false;
2731
 
  bool quit_now = false;
2732
 
 
2733
 
  __attribute__((cleanup(cleanup_queue)))
2734
 
    task_queue *queue = create_queue();
2735
 
  g_assert_nonnull(queue);
2736
 
  const task_context task = {
2737
 
    .func=wait_for_mandos_client_exit,
2738
 
    .pid=1,
2739
 
    .mandos_client_exited=&mandos_client_exited,
2740
 
    .quit_now=&quit_now,
2741
 
  };
2742
 
  run_task_with_stderr_to_dev_null(task, queue);
2743
 
 
2744
 
  g_assert_false(mandos_client_exited);
2745
 
  g_assert_true(quit_now);
2746
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2747
 
}
2748
 
 
2749
 
static void run_task_with_stderr_to_dev_null(const task_context task,
2750
 
                                             task_queue *const queue){
2751
 
  FILE *real_stderr = stderr;
2752
 
  FILE *devnull = fopen("/dev/null", "we");
2753
 
  g_assert_nonnull(devnull);
2754
 
 
2755
 
  stderr = devnull;
2756
 
  task.func(task, queue);
2757
 
  stderr = real_stderr;
2758
 
 
2759
 
  g_assert_cmpint(fclose(devnull), ==, 0);
2760
 
}
2761
 
 
2762
 
static
2763
 
void test_wait_for_mandos_client_exit_noexit(test_fixture *fixture,
2764
 
                                             __attribute__((unused))
2765
 
                                             gconstpointer user_data){
2766
 
  bool mandos_client_exited = false;
2767
 
  bool quit_now = false;
2768
 
 
2769
 
  pid_t create_eternal_process(void){
2770
 
    const pid_t pid = fork();
2771
 
    if(pid == 0){               /* Child */
2772
 
      if(not restore_signal_handler(&fixture->orig_sigaction)){
2773
 
        _exit(EXIT_FAILURE);
2774
 
      }
2775
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
2776
 
        _exit(EXIT_FAILURE);
2777
 
      }
2778
 
      while(true){
2779
 
        pause();
2780
 
      }
2781
 
    }
2782
 
    return pid;
2783
 
  }
2784
 
  pid_t pid = create_eternal_process();
2785
 
  g_assert_true(pid != -1);
2786
 
 
2787
 
  __attribute__((cleanup(cleanup_queue)))
2788
 
    task_queue *queue = create_queue();
2789
 
  g_assert_nonnull(queue);
2790
 
  const task_context task = {
2791
 
    .func=wait_for_mandos_client_exit,
2792
 
    .pid=pid,
2793
 
    .mandos_client_exited=&mandos_client_exited,
2794
 
    .quit_now=&quit_now,
2795
 
  };
2796
 
  task.func(task, queue);
2797
 
 
2798
 
  g_assert_false(mandos_client_exited);
2799
 
  g_assert_false(quit_now);
2800
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
2801
 
 
2802
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
2803
 
        .func=wait_for_mandos_client_exit,
2804
 
        .pid=task.pid,
2805
 
        .mandos_client_exited=&mandos_client_exited,
2806
 
        .quit_now=&quit_now,
2807
 
      }));
2808
 
}
2809
 
 
2810
 
static
2811
 
void test_wait_for_mandos_client_exit_success(test_fixture *fixture,
2812
 
                                              __attribute__((unused))
2813
 
                                              gconstpointer
2814
 
                                              user_data){
2815
 
  bool mandos_client_exited = false;
2816
 
  bool quit_now = false;
2817
 
 
2818
 
  pid_t create_successful_process(void){
2819
 
    const pid_t pid = fork();
2820
 
    if(pid == 0){               /* Child */
2821
 
      if(not restore_signal_handler(&fixture->orig_sigaction)){
2822
 
        _exit(EXIT_FAILURE);
2823
 
      }
2824
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
2825
 
        _exit(EXIT_FAILURE);
2826
 
      }
2827
 
      exit(EXIT_SUCCESS);
2828
 
    }
2829
 
    return pid;
2830
 
  }
2831
 
  const pid_t pid = create_successful_process();
2832
 
  g_assert_true(pid != -1);
2833
 
 
2834
 
  __attribute__((cleanup(cleanup_queue)))
2835
 
    task_queue *queue = create_queue();
2836
 
  g_assert_nonnull(queue);
2837
 
  const task_context initial_task = {
2838
 
    .func=wait_for_mandos_client_exit,
2839
 
    .pid=pid,
2840
 
    .mandos_client_exited=&mandos_client_exited,
2841
 
    .quit_now=&quit_now,
2842
 
  };
2843
 
  g_assert_true(add_to_queue(queue, initial_task));
2844
 
 
2845
 
  struct timespec starttime, currtime;
2846
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2847
 
  __attribute__((cleanup(cleanup_close)))
2848
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2849
 
  do {
2850
 
    queue->next_run = 0;
2851
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2852
 
    g_assert_true(run_queue(&queue, (string_set[]){{}}, &quit_now));
2853
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2854
 
  } while((not mandos_client_exited)
2855
 
          and (not quit_now)
2856
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2857
 
 
2858
 
  g_assert_true(mandos_client_exited);
2859
 
  g_assert_false(quit_now);
2860
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2861
 
}
2862
 
 
2863
 
static
2864
 
void test_wait_for_mandos_client_exit_failure(test_fixture *fixture,
2865
 
                                              __attribute__((unused))
2866
 
                                              gconstpointer
2867
 
                                              user_data){
2868
 
  bool mandos_client_exited = false;
2869
 
  bool quit_now = false;
2870
 
 
2871
 
  pid_t create_failing_process(void){
2872
 
    const pid_t pid = fork();
2873
 
    if(pid == 0){               /* Child */
2874
 
      if(not restore_signal_handler(&fixture->orig_sigaction)){
2875
 
        _exit(EXIT_FAILURE);
2876
 
      }
2877
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
2878
 
        _exit(EXIT_FAILURE);
2879
 
      }
2880
 
      exit(EXIT_FAILURE);
2881
 
    }
2882
 
    return pid;
2883
 
  }
2884
 
  const pid_t pid = create_failing_process();
2885
 
  g_assert_true(pid != -1);
2886
 
 
2887
 
  __attribute__((cleanup(string_set_clear)))
2888
 
    string_set cancelled_filenames = {};
2889
 
  __attribute__((cleanup(cleanup_close)))
2890
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2891
 
  g_assert_cmpint(epoll_fd, >=, 0);
2892
 
  __attribute__((cleanup(cleanup_queue)))
2893
 
    task_queue *queue = create_queue();
2894
 
  g_assert_nonnull(queue);
2895
 
  g_assert_true(add_to_queue(queue, (task_context){
2896
 
        .func=wait_for_mandos_client_exit,
2897
 
        .pid=pid,
2898
 
        .mandos_client_exited=&mandos_client_exited,
2899
 
        .quit_now=&quit_now,
2900
 
      }));
2901
 
 
2902
 
  g_assert_true(sigismember(&fixture->orig_sigmask, SIGCHLD) == 0);
2903
 
 
2904
 
  __attribute__((cleanup(cleanup_close)))
2905
 
    const int devnull_fd = open("/dev/null",
2906
 
                                O_WRONLY | O_CLOEXEC);
2907
 
  g_assert_cmpint(devnull_fd, >=, 0);
2908
 
  __attribute__((cleanup(cleanup_close)))
2909
 
    const int real_stderr_fd = dup(STDERR_FILENO);
2910
 
  g_assert_cmpint(real_stderr_fd, >=, 0);
2911
 
 
2912
 
  struct timespec starttime, currtime;
2913
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2914
 
  do {
2915
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2916
 
    dup2(devnull_fd, STDERR_FILENO);
2917
 
    const bool success = run_queue(&queue, &cancelled_filenames,
2918
 
                                   &quit_now);
2919
 
    dup2(real_stderr_fd, STDERR_FILENO);
2920
 
    if(not success){
2921
 
      break;
2922
 
    }
2923
 
 
2924
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2925
 
  } while((not mandos_client_exited)
2926
 
          and (not quit_now)
2927
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2928
 
 
2929
 
  g_assert_true(quit_now);
2930
 
  g_assert_true(mandos_client_exited);
2931
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
2932
 
}
2933
 
 
2934
 
static
2935
 
void test_wait_for_mandos_client_exit_killed(test_fixture *fixture,
2936
 
                                             __attribute__((unused))
2937
 
                                             gconstpointer user_data){
2938
 
  bool mandos_client_exited = false;
2939
 
  bool quit_now = false;
2940
 
 
2941
 
  pid_t create_killed_process(void){
2942
 
    const pid_t pid = fork();
2943
 
    if(pid == 0){               /* Child */
2944
 
      if(not restore_signal_handler(&fixture->orig_sigaction)){
2945
 
        _exit(EXIT_FAILURE);
2946
 
      }
2947
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
2948
 
        _exit(EXIT_FAILURE);
2949
 
      }
2950
 
      while(true){
2951
 
        pause();
2952
 
      }
2953
 
    }
2954
 
    kill(pid, SIGKILL);
2955
 
    return pid;
2956
 
  }
2957
 
  const pid_t pid = create_killed_process();
2958
 
  g_assert_true(pid != -1);
2959
 
 
2960
 
  __attribute__((cleanup(string_set_clear)))
2961
 
    string_set cancelled_filenames = {};
2962
 
  __attribute__((cleanup(cleanup_close)))
2963
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
2964
 
  g_assert_cmpint(epoll_fd, >=, 0);
2965
 
  __attribute__((cleanup(cleanup_queue)))
2966
 
    task_queue *queue = create_queue();
2967
 
  g_assert_nonnull(queue);
2968
 
  g_assert_true(add_to_queue(queue, (task_context){
2969
 
        .func=wait_for_mandos_client_exit,
2970
 
        .pid=pid,
2971
 
        .mandos_client_exited=&mandos_client_exited,
2972
 
        .quit_now=&quit_now,
2973
 
      }));
2974
 
 
2975
 
  __attribute__((cleanup(cleanup_close)))
2976
 
    const int devnull_fd = open("/dev/null",
2977
 
                                O_WRONLY | O_CLOEXEC);
2978
 
  g_assert_cmpint(devnull_fd, >=, 0);
2979
 
  __attribute__((cleanup(cleanup_close)))
2980
 
    const int real_stderr_fd = dup(STDERR_FILENO);
2981
 
  g_assert_cmpint(real_stderr_fd, >=, 0);
2982
 
 
2983
 
  struct timespec starttime, currtime;
2984
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
2985
 
  do {
2986
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
2987
 
    dup2(devnull_fd, STDERR_FILENO);
2988
 
    const bool success = run_queue(&queue, &cancelled_filenames,
2989
 
                                   &quit_now);
2990
 
    dup2(real_stderr_fd, STDERR_FILENO);
2991
 
    if(not success){
2992
 
      break;
2993
 
    }
2994
 
 
2995
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
2996
 
  } while((not mandos_client_exited)
2997
 
          and (not quit_now)
2998
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
2999
 
 
3000
 
  g_assert_true(mandos_client_exited);
3001
 
  g_assert_true(quit_now);
3002
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3003
 
}
3004
 
 
3005
 
static bool epoll_set_does_not_contain(int, int);
3006
 
 
3007
 
static
3008
 
void test_read_mandos_client_output_readerror(__attribute__((unused))
3009
 
                                              test_fixture *fixture,
3010
 
                                              __attribute__((unused))
3011
 
                                              gconstpointer
3012
 
                                              user_data){
3013
 
  __attribute__((cleanup(cleanup_close)))
3014
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3015
 
  g_assert_cmpint(epoll_fd, >=, 0);
3016
 
 
3017
 
  __attribute__((cleanup(cleanup_buffer)))
3018
 
    buffer password = {};
3019
 
 
3020
 
  /* Reading /proc/self/mem from offset 0 will always give EIO */
3021
 
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
3022
 
 
3023
 
  bool password_is_read = false;
3024
 
  bool quit_now = false;
3025
 
  __attribute__((cleanup(cleanup_queue)))
3026
 
    task_queue *queue = create_queue();
3027
 
  g_assert_nonnull(queue);
3028
 
 
3029
 
  task_context task = {
3030
 
    .func=read_mandos_client_output,
3031
 
    .epoll_fd=epoll_fd,
3032
 
    .fd=fd,
3033
 
    .password=&password,
3034
 
    .password_is_read=&password_is_read,
3035
 
    .quit_now=&quit_now,
3036
 
  };
3037
 
  run_task_with_stderr_to_dev_null(task, queue);
3038
 
  g_assert_false(password_is_read);
3039
 
  g_assert_cmpint((int)password.length, ==, 0);
3040
 
  g_assert_true(quit_now);
3041
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3042
 
 
3043
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
3044
 
 
3045
 
  g_assert_cmpint(close(fd), ==, -1);
3046
 
}
3047
 
 
3048
 
static bool epoll_set_does_not_contain(int epoll_fd, int fd){
3049
 
  return not epoll_set_contains(epoll_fd, fd, 0);
3050
 
}
3051
 
 
3052
 
static
3053
 
void test_read_mandos_client_output_nodata(__attribute__((unused))
3054
 
                                           test_fixture *fixture,
3055
 
                                           __attribute__((unused))
3056
 
                                           gconstpointer user_data){
3057
 
  __attribute__((cleanup(cleanup_close)))
3058
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3059
 
  g_assert_cmpint(epoll_fd, >=, 0);
3060
 
 
3061
 
  int pipefds[2];
3062
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3063
 
 
3064
 
  __attribute__((cleanup(cleanup_buffer)))
3065
 
    buffer password = {};
3066
 
 
3067
 
  bool password_is_read = false;
3068
 
  bool quit_now = false;
3069
 
  __attribute__((cleanup(cleanup_queue)))
3070
 
    task_queue *queue = create_queue();
3071
 
  g_assert_nonnull(queue);
3072
 
 
3073
 
  task_context task = {
3074
 
    .func=read_mandos_client_output,
3075
 
    .epoll_fd=epoll_fd,
3076
 
    .fd=pipefds[0],
3077
 
    .password=&password,
3078
 
    .password_is_read=&password_is_read,
3079
 
    .quit_now=&quit_now,
3080
 
  };
3081
 
  task.func(task, queue);
3082
 
  g_assert_false(password_is_read);
3083
 
  g_assert_cmpint((int)password.length, ==, 0);
3084
 
  g_assert_false(quit_now);
3085
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3086
 
 
3087
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
3088
 
        .func=read_mandos_client_output,
3089
 
        .epoll_fd=epoll_fd,
3090
 
        .fd=pipefds[0],
3091
 
        .password=&password,
3092
 
        .password_is_read=&password_is_read,
3093
 
        .quit_now=&quit_now,
3094
 
      }));
3095
 
 
3096
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3097
 
                                   EPOLLIN | EPOLLRDHUP));
3098
 
 
3099
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
3100
 
}
3101
 
 
3102
 
static void test_read_mandos_client_output_eof(__attribute__((unused))
3103
 
                                               test_fixture *fixture,
3104
 
                                               __attribute__((unused))
3105
 
                                               gconstpointer
3106
 
                                               user_data){
3107
 
  __attribute__((cleanup(cleanup_close)))
3108
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3109
 
  g_assert_cmpint(epoll_fd, >=, 0);
3110
 
 
3111
 
  int pipefds[2];
3112
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3113
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
3114
 
 
3115
 
  __attribute__((cleanup(cleanup_buffer)))
3116
 
    buffer password = {};
3117
 
 
3118
 
  bool password_is_read = false;
3119
 
  bool quit_now = false;
3120
 
  __attribute__((cleanup(cleanup_queue)))
3121
 
    task_queue *queue = create_queue();
3122
 
  g_assert_nonnull(queue);
3123
 
 
3124
 
  task_context task = {
3125
 
    .func=read_mandos_client_output,
3126
 
    .epoll_fd=epoll_fd,
3127
 
    .fd=pipefds[0],
3128
 
    .password=&password,
3129
 
    .password_is_read=&password_is_read,
3130
 
    .quit_now=&quit_now,
3131
 
  };
3132
 
  task.func(task, queue);
3133
 
  g_assert_true(password_is_read);
3134
 
  g_assert_cmpint((int)password.length, ==, 0);
3135
 
  g_assert_false(quit_now);
3136
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3137
 
 
3138
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
3139
 
 
3140
 
  g_assert_cmpint(close(pipefds[0]), ==, -1);
3141
 
}
3142
 
 
3143
 
static
3144
 
void test_read_mandos_client_output_once(__attribute__((unused))
3145
 
                                         test_fixture *fixture,
3146
 
                                         __attribute__((unused))
3147
 
                                         gconstpointer user_data){
3148
 
  __attribute__((cleanup(cleanup_close)))
3149
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3150
 
  g_assert_cmpint(epoll_fd, >=, 0);
3151
 
 
3152
 
  int pipefds[2];
3153
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3154
 
 
3155
 
  const char dummy_test_password[] = "dummy test password";
3156
 
  /* Start with a pre-allocated buffer */
3157
 
  __attribute__((cleanup(cleanup_buffer)))
3158
 
    buffer password = {
3159
 
    .data=malloc(sizeof(dummy_test_password)),
3160
 
    .length=0,
3161
 
    .allocated=sizeof(dummy_test_password),
3162
 
  };
3163
 
  g_assert_nonnull(password.data);
3164
 
  if(mlock(password.data, password.allocated) != 0){
3165
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
3166
 
  }
3167
 
 
3168
 
  bool password_is_read = false;
3169
 
  bool quit_now = false;
3170
 
  __attribute__((cleanup(cleanup_queue)))
3171
 
    task_queue *queue = create_queue();
3172
 
  g_assert_nonnull(queue);
3173
 
 
3174
 
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3175
 
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3176
 
                             sizeof(dummy_test_password)),
3177
 
                  ==, (int)sizeof(dummy_test_password));
3178
 
 
3179
 
  task_context task = {
3180
 
    .func=read_mandos_client_output,
3181
 
    .epoll_fd=epoll_fd,
3182
 
    .fd=pipefds[0],
3183
 
    .password=&password,
3184
 
    .password_is_read=&password_is_read,
3185
 
    .quit_now=&quit_now,
3186
 
  };
3187
 
  task.func(task, queue);
3188
 
 
3189
 
  g_assert_false(password_is_read);
3190
 
  g_assert_cmpint((int)password.length, ==,
3191
 
                  (int)sizeof(dummy_test_password));
3192
 
  g_assert_nonnull(password.data);
3193
 
  g_assert_cmpint(memcmp(password.data, dummy_test_password,
3194
 
                         sizeof(dummy_test_password)), ==, 0);
3195
 
 
3196
 
  g_assert_false(quit_now);
3197
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3198
 
 
3199
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
3200
 
        .func=read_mandos_client_output,
3201
 
        .epoll_fd=epoll_fd,
3202
 
        .fd=pipefds[0],
3203
 
        .password=&password,
3204
 
        .password_is_read=&password_is_read,
3205
 
        .quit_now=&quit_now,
3206
 
      }));
3207
 
 
3208
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3209
 
                                   EPOLLIN | EPOLLRDHUP));
3210
 
 
3211
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
3212
 
}
3213
 
 
3214
 
static
3215
 
void test_read_mandos_client_output_malloc(__attribute__((unused))
3216
 
                                           test_fixture *fixture,
3217
 
                                           __attribute__((unused))
3218
 
                                           gconstpointer user_data){
3219
 
  __attribute__((cleanup(cleanup_close)))
3220
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3221
 
  g_assert_cmpint(epoll_fd, >=, 0);
3222
 
 
3223
 
  int pipefds[2];
3224
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3225
 
 
3226
 
  const char dummy_test_password[] = "dummy test password";
3227
 
  /* Start with an empty buffer */
3228
 
  __attribute__((cleanup(cleanup_buffer)))
3229
 
    buffer password = {};
3230
 
 
3231
 
  bool password_is_read = false;
3232
 
  bool quit_now = false;
3233
 
  __attribute__((cleanup(cleanup_queue)))
3234
 
    task_queue *queue = create_queue();
3235
 
  g_assert_nonnull(queue);
3236
 
 
3237
 
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3238
 
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3239
 
                             sizeof(dummy_test_password)),
3240
 
                  ==, (int)sizeof(dummy_test_password));
3241
 
 
3242
 
  task_context task = {
3243
 
    .func=read_mandos_client_output,
3244
 
    .epoll_fd=epoll_fd,
3245
 
    .fd=pipefds[0],
3246
 
    .password=&password,
3247
 
    .password_is_read=&password_is_read,
3248
 
    .quit_now=&quit_now,
3249
 
  };
3250
 
  task.func(task, queue);
3251
 
 
3252
 
  g_assert_false(password_is_read);
3253
 
  g_assert_cmpint((int)password.length, ==,
3254
 
                  (int)sizeof(dummy_test_password));
3255
 
  g_assert_nonnull(password.data);
3256
 
  g_assert_cmpint(memcmp(password.data, dummy_test_password,
3257
 
                         sizeof(dummy_test_password)), ==, 0);
3258
 
 
3259
 
  g_assert_false(quit_now);
3260
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3261
 
 
3262
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
3263
 
        .func=read_mandos_client_output,
3264
 
        .epoll_fd=epoll_fd,
3265
 
        .fd=pipefds[0],
3266
 
        .password=&password,
3267
 
        .password_is_read=&password_is_read,
3268
 
        .quit_now=&quit_now,
3269
 
      }));
3270
 
 
3271
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3272
 
                                   EPOLLIN | EPOLLRDHUP));
3273
 
 
3274
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
3275
 
}
3276
 
 
3277
 
static
3278
 
void test_read_mandos_client_output_append(__attribute__((unused))
3279
 
                                           test_fixture *fixture,
3280
 
                                           __attribute__((unused))
3281
 
                                           gconstpointer user_data){
3282
 
  __attribute__((cleanup(cleanup_close)))
3283
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3284
 
  g_assert_cmpint(epoll_fd, >=, 0);
3285
 
 
3286
 
  int pipefds[2];
3287
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3288
 
 
3289
 
  const char dummy_test_password[] = "dummy test password";
3290
 
  __attribute__((cleanup(cleanup_buffer)))
3291
 
    buffer password = {
3292
 
    .data=malloc(PIPE_BUF),
3293
 
    .length=PIPE_BUF,
3294
 
    .allocated=PIPE_BUF,
3295
 
  };
3296
 
  g_assert_nonnull(password.data);
3297
 
  if(mlock(password.data, password.allocated) != 0){
3298
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
3299
 
  }
3300
 
 
3301
 
  memset(password.data, 'x', PIPE_BUF);
3302
 
  char password_expected[PIPE_BUF];
3303
 
  memcpy(password_expected, password.data, PIPE_BUF);
3304
 
 
3305
 
  bool password_is_read = false;
3306
 
  bool quit_now = false;
3307
 
  __attribute__((cleanup(cleanup_queue)))
3308
 
    task_queue *queue = create_queue();
3309
 
  g_assert_nonnull(queue);
3310
 
 
3311
 
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
3312
 
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
3313
 
                             sizeof(dummy_test_password)),
3314
 
                  ==, (int)sizeof(dummy_test_password));
3315
 
 
3316
 
  task_context task = {
3317
 
    .func=read_mandos_client_output,
3318
 
    .epoll_fd=epoll_fd,
3319
 
    .fd=pipefds[0],
3320
 
    .password=&password,
3321
 
    .password_is_read=&password_is_read,
3322
 
    .quit_now=&quit_now,
3323
 
  };
3324
 
  task.func(task, queue);
3325
 
 
3326
 
  g_assert_false(password_is_read);
3327
 
  g_assert_cmpint((int)password.length, ==,
3328
 
                  PIPE_BUF + sizeof(dummy_test_password));
3329
 
  g_assert_nonnull(password.data);
3330
 
  g_assert_cmpint(memcmp(password_expected, password.data, PIPE_BUF),
3331
 
                  ==, 0);
3332
 
  g_assert_cmpint(memcmp(password.data + PIPE_BUF,
3333
 
                         dummy_test_password,
3334
 
                         sizeof(dummy_test_password)), ==, 0);
3335
 
  g_assert_false(quit_now);
3336
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
3337
 
 
3338
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
3339
 
        .func=read_mandos_client_output,
3340
 
        .epoll_fd=epoll_fd,
3341
 
        .fd=pipefds[0],
3342
 
        .password=&password,
3343
 
        .password_is_read=&password_is_read,
3344
 
        .quit_now=&quit_now,
3345
 
      }));
3346
 
 
3347
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
3348
 
                                   EPOLLIN | EPOLLRDHUP));
3349
 
}
3350
 
 
3351
 
static char *make_temporary_directory(void);
3352
 
 
3353
 
static void test_add_inotify_dir_watch(__attribute__((unused))
3354
 
                                       test_fixture *fixture,
3355
 
                                       __attribute__((unused))
3356
 
                                       gconstpointer user_data){
3357
 
  __attribute__((cleanup(cleanup_close)))
3358
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3359
 
  g_assert_cmpint(epoll_fd, >=, 0);
3360
 
  __attribute__((cleanup(cleanup_queue)))
3361
 
    task_queue *queue = create_queue();
3362
 
  g_assert_nonnull(queue);
3363
 
  __attribute__((cleanup(string_set_clear)))
3364
 
    string_set cancelled_filenames = {};
3365
 
  const mono_microsecs current_time = 0;
3366
 
 
3367
 
  bool quit_now = false;
3368
 
  buffer password = {};
3369
 
  bool mandos_client_exited = false;
3370
 
  bool password_is_read = false;
3371
 
 
3372
 
  __attribute__((cleanup(cleanup_string)))
3373
 
    char *tempdir = make_temporary_directory();
3374
 
  g_assert_nonnull(tempdir);
3375
 
 
3376
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3377
 
                                      &password, tempdir,
3378
 
                                      &cancelled_filenames,
3379
 
                                      &current_time,
3380
 
                                      &mandos_client_exited,
3381
 
                                      &password_is_read));
3382
 
 
3383
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
3384
 
 
3385
 
  const task_context *const added_read_task
3386
 
    = find_matching_task(queue, (task_context){
3387
 
        .func=read_inotify_event,
3388
 
        .epoll_fd=epoll_fd,
3389
 
        .quit_now=&quit_now,
3390
 
        .password=&password,
3391
 
        .filename=tempdir,
3392
 
        .cancelled_filenames=&cancelled_filenames,
3393
 
        .current_time=&current_time,
3394
 
        .mandos_client_exited=&mandos_client_exited,
3395
 
        .password_is_read=&password_is_read,
3396
 
      });
3397
 
  g_assert_nonnull(added_read_task);
3398
 
 
3399
 
  g_assert_cmpint(added_read_task->fd, >, 2);
3400
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3401
 
  g_assert_true(epoll_set_contains(added_read_task->epoll_fd,
3402
 
                                   added_read_task->fd,
3403
 
                                   EPOLLIN | EPOLLRDHUP));
3404
 
 
3405
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3406
 
}
3407
 
 
3408
 
static char *make_temporary_directory(void){
3409
 
  char *name = strdup("/tmp/mandosXXXXXX");
3410
 
  g_assert_nonnull(name);
3411
 
  char *result = mkdtemp(name);
3412
 
  if(result == NULL){
3413
 
    free(name);
3414
 
  }
3415
 
  return result;
3416
 
}
3417
 
 
3418
 
static void test_add_inotify_dir_watch_fail(__attribute__((unused))
3419
 
                                            test_fixture *fixture,
3420
 
                                            __attribute__((unused))
3421
 
                                            gconstpointer user_data){
3422
 
  __attribute__((cleanup(cleanup_close)))
3423
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3424
 
  g_assert_cmpint(epoll_fd, >=, 0);
3425
 
  __attribute__((cleanup(cleanup_queue)))
3426
 
    task_queue *queue = create_queue();
3427
 
  g_assert_nonnull(queue);
3428
 
  __attribute__((cleanup(string_set_clear)))
3429
 
    string_set cancelled_filenames = {};
3430
 
  const mono_microsecs current_time = 0;
3431
 
 
3432
 
  bool quit_now = false;
3433
 
  buffer password = {};
3434
 
  bool mandos_client_exited = false;
3435
 
  bool password_is_read = false;
3436
 
 
3437
 
  const char nonexistent_dir[] = "/nonexistent";
3438
 
 
3439
 
  FILE *real_stderr = stderr;
3440
 
  FILE *devnull = fopen("/dev/null", "we");
3441
 
  g_assert_nonnull(devnull);
3442
 
  stderr = devnull;
3443
 
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3444
 
                                       &password, nonexistent_dir,
3445
 
                                       &cancelled_filenames,
3446
 
                                       &current_time,
3447
 
                                       &mandos_client_exited,
3448
 
                                       &password_is_read));
3449
 
  stderr = real_stderr;
3450
 
  g_assert_cmpint(fclose(devnull), ==, 0);
3451
 
 
3452
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3453
 
}
3454
 
 
3455
 
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3456
 
                                              test_fixture *fixture,
3457
 
                                              __attribute__((unused))
3458
 
                                              gconstpointer
3459
 
                                              user_data){
3460
 
  __attribute__((cleanup(cleanup_close)))
3461
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3462
 
  g_assert_cmpint(epoll_fd, >=, 0);
3463
 
  __attribute__((cleanup(cleanup_queue)))
3464
 
    task_queue *queue = create_queue();
3465
 
  g_assert_nonnull(queue);
3466
 
  __attribute__((cleanup(string_set_clear)))
3467
 
    string_set cancelled_filenames = {};
3468
 
  const mono_microsecs current_time = 0;
3469
 
 
3470
 
  bool quit_now = false;
3471
 
  buffer password = {};
3472
 
  bool mandos_client_exited = false;
3473
 
  bool password_is_read = false;
3474
 
 
3475
 
  __attribute__((cleanup(cleanup_string)))
3476
 
    char *tempdir = make_temporary_directory();
3477
 
  g_assert_nonnull(tempdir);
3478
 
 
3479
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3480
 
                                      &password, tempdir,
3481
 
                                      &cancelled_filenames,
3482
 
                                      &current_time,
3483
 
                                      &mandos_client_exited,
3484
 
                                      &password_is_read));
3485
 
 
3486
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
3487
 
 
3488
 
  const task_context *const added_read_task
3489
 
    = find_matching_task(queue,
3490
 
                         (task_context){ .func=read_inotify_event });
3491
 
  g_assert_nonnull(added_read_task);
3492
 
 
3493
 
  g_assert_cmpint(added_read_task->fd, >, 2);
3494
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3495
 
 
3496
 
  /* "sufficient to read at least one event." - inotify(7) */
3497
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3498
 
                              + NAME_MAX + 1);
3499
 
  struct inotify_event *ievent = malloc(ievent_size);
3500
 
  g_assert_nonnull(ievent);
3501
 
 
3502
 
  g_assert_cmpint(read(added_read_task->fd, ievent, ievent_size), ==,
3503
 
                  -1);
3504
 
  g_assert_cmpint(errno, ==, EAGAIN);
3505
 
 
3506
 
  free(ievent);
3507
 
 
3508
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3509
 
}
3510
 
 
3511
 
static char *make_temporary_file_in_directory(const char
3512
 
                                              *const dir);
3513
 
 
3514
 
static
3515
 
void test_add_inotify_dir_watch_IN_CLOSE_WRITE(__attribute__((unused))
3516
 
                                               test_fixture *fixture,
3517
 
                                               __attribute__((unused))
3518
 
                                               gconstpointer
3519
 
                                               user_data){
3520
 
  __attribute__((cleanup(cleanup_close)))
3521
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3522
 
  g_assert_cmpint(epoll_fd, >=, 0);
3523
 
  __attribute__((cleanup(cleanup_queue)))
3524
 
    task_queue *queue = create_queue();
3525
 
  g_assert_nonnull(queue);
3526
 
  __attribute__((cleanup(string_set_clear)))
3527
 
    string_set cancelled_filenames = {};
3528
 
  const mono_microsecs current_time = 0;
3529
 
 
3530
 
  bool quit_now = false;
3531
 
  buffer password = {};
3532
 
  bool mandos_client_exited = false;
3533
 
  bool password_is_read = false;
3534
 
 
3535
 
  __attribute__((cleanup(cleanup_string)))
3536
 
    char *tempdir = make_temporary_directory();
3537
 
  g_assert_nonnull(tempdir);
3538
 
 
3539
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3540
 
                                      &password, tempdir,
3541
 
                                      &cancelled_filenames,
3542
 
                                      &current_time,
3543
 
                                      &mandos_client_exited,
3544
 
                                      &password_is_read));
3545
 
 
3546
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
3547
 
 
3548
 
  const task_context *const added_read_task
3549
 
    = find_matching_task(queue,
3550
 
                         (task_context){ .func=read_inotify_event });
3551
 
  g_assert_nonnull(added_read_task);
3552
 
 
3553
 
  g_assert_cmpint(added_read_task->fd, >, 2);
3554
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3555
 
 
3556
 
  __attribute__((cleanup(cleanup_string)))
3557
 
    char *filename = make_temporary_file_in_directory(tempdir);
3558
 
  g_assert_nonnull(filename);
3559
 
 
3560
 
  /* "sufficient to read at least one event." - inotify(7) */
3561
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3562
 
                              + NAME_MAX + 1);
3563
 
  struct inotify_event *ievent = malloc(ievent_size);
3564
 
  g_assert_nonnull(ievent);
3565
 
 
3566
 
  ssize_t read_size = 0;
3567
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
3568
 
 
3569
 
  g_assert_cmpint((int)read_size, >, 0);
3570
 
  g_assert_true(ievent->mask & IN_CLOSE_WRITE);
3571
 
  g_assert_cmpstr(ievent->name, ==, basename(filename));
3572
 
 
3573
 
  free(ievent);
3574
 
 
3575
 
  g_assert_cmpint(unlink(filename), ==, 0);
3576
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3577
 
}
3578
 
 
3579
 
static char *make_temporary_prefixed_file_in_directory(const char
3580
 
                                                       *const prefix,
3581
 
                                                       const char
3582
 
                                                       *const dir){
3583
 
  char *filename = NULL;
3584
 
  g_assert_cmpint(asprintf(&filename, "%s/%sXXXXXX", dir, prefix),
3585
 
                  >, 0);
3586
 
  g_assert_nonnull(filename);
3587
 
  const int fd = mkostemp(filename, O_CLOEXEC);
3588
 
  g_assert_cmpint(fd, >=, 0);
3589
 
  g_assert_cmpint(close(fd), ==, 0);
3590
 
  return filename;
3591
 
}
3592
 
 
3593
 
static char *make_temporary_file_in_directory(const char
3594
 
                                              *const dir){
3595
 
  return make_temporary_prefixed_file_in_directory("temp", dir);
3596
 
}
3597
 
 
3598
 
static
3599
 
void test_add_inotify_dir_watch_IN_MOVED_TO(__attribute__((unused))
3600
 
                                            test_fixture *fixture,
3601
 
                                            __attribute__((unused))
3602
 
                                            gconstpointer user_data){
3603
 
  __attribute__((cleanup(cleanup_close)))
3604
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3605
 
  g_assert_cmpint(epoll_fd, >=, 0);
3606
 
  __attribute__((cleanup(cleanup_queue)))
3607
 
    task_queue *queue = create_queue();
3608
 
  g_assert_nonnull(queue);
3609
 
  __attribute__((cleanup(string_set_clear)))
3610
 
    string_set cancelled_filenames = {};
3611
 
  const mono_microsecs current_time = 0;
3612
 
 
3613
 
  bool quit_now = false;
3614
 
  buffer password = {};
3615
 
  bool mandos_client_exited = false;
3616
 
  bool password_is_read = false;
3617
 
 
3618
 
  __attribute__((cleanup(cleanup_string)))
3619
 
    char *watchdir = make_temporary_directory();
3620
 
  g_assert_nonnull(watchdir);
3621
 
 
3622
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3623
 
                                      &password, watchdir,
3624
 
                                      &cancelled_filenames,
3625
 
                                      &current_time,
3626
 
                                      &mandos_client_exited,
3627
 
                                      &password_is_read));
3628
 
 
3629
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
3630
 
 
3631
 
  const task_context *const added_read_task
3632
 
    = find_matching_task(queue,
3633
 
                         (task_context){ .func=read_inotify_event });
3634
 
  g_assert_nonnull(added_read_task);
3635
 
 
3636
 
  g_assert_cmpint(added_read_task->fd, >, 2);
3637
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3638
 
 
3639
 
  char *sourcedir = make_temporary_directory();
3640
 
  g_assert_nonnull(sourcedir);
3641
 
 
3642
 
  __attribute__((cleanup(cleanup_string)))
3643
 
    char *filename = make_temporary_file_in_directory(sourcedir);
3644
 
  g_assert_nonnull(filename);
3645
 
 
3646
 
  __attribute__((cleanup(cleanup_string)))
3647
 
    char *targetfilename = NULL;
3648
 
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", watchdir,
3649
 
                           basename(filename)), >, 0);
3650
 
  g_assert_nonnull(targetfilename);
3651
 
 
3652
 
  g_assert_cmpint(rename(filename, targetfilename), ==, 0);
3653
 
  g_assert_cmpint(rmdir(sourcedir), ==, 0);
3654
 
  free(sourcedir);
3655
 
 
3656
 
  /* "sufficient to read at least one event." - inotify(7) */
3657
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3658
 
                              + NAME_MAX + 1);
3659
 
  struct inotify_event *ievent = malloc(ievent_size);
3660
 
  g_assert_nonnull(ievent);
3661
 
 
3662
 
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3663
 
 
3664
 
  g_assert_cmpint((int)read_size, >, 0);
3665
 
  g_assert_true(ievent->mask & IN_MOVED_TO);
3666
 
  g_assert_cmpstr(ievent->name, ==, basename(targetfilename));
3667
 
 
3668
 
  free(ievent);
3669
 
 
3670
 
  g_assert_cmpint(unlink(targetfilename), ==, 0);
3671
 
  g_assert_cmpint(rmdir(watchdir), ==, 0);
3672
 
}
3673
 
 
3674
 
static
3675
 
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
3676
 
                                              test_fixture *fixture,
3677
 
                                              __attribute__((unused))
3678
 
                                              gconstpointer
3679
 
                                              user_data){
3680
 
  __attribute__((cleanup(cleanup_close)))
3681
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3682
 
  g_assert_cmpint(epoll_fd, >=, 0);
3683
 
  __attribute__((cleanup(cleanup_queue)))
3684
 
    task_queue *queue = create_queue();
3685
 
  g_assert_nonnull(queue);
3686
 
  __attribute__((cleanup(string_set_clear)))
3687
 
    string_set cancelled_filenames = {};
3688
 
  const mono_microsecs current_time = 0;
3689
 
 
3690
 
  bool quit_now = false;
3691
 
  buffer password = {};
3692
 
  bool mandos_client_exited = false;
3693
 
  bool password_is_read = false;
3694
 
 
3695
 
  __attribute__((cleanup(cleanup_string)))
3696
 
    char *tempdir = make_temporary_directory();
3697
 
  g_assert_nonnull(tempdir);
3698
 
 
3699
 
  __attribute__((cleanup(cleanup_string)))
3700
 
    char *tempfilename = make_temporary_file_in_directory(tempdir);
3701
 
  g_assert_nonnull(tempfilename);
3702
 
 
3703
 
  __attribute__((cleanup(cleanup_string)))
3704
 
    char *targetdir = make_temporary_directory();
3705
 
  g_assert_nonnull(targetdir);
3706
 
 
3707
 
  __attribute__((cleanup(cleanup_string)))
3708
 
    char *targetfilename = NULL;
3709
 
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
3710
 
                           basename(tempfilename)), >, 0);
3711
 
  g_assert_nonnull(targetfilename);
3712
 
 
3713
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3714
 
                                      &password, tempdir,
3715
 
                                      &cancelled_filenames,
3716
 
                                      &current_time,
3717
 
                                      &mandos_client_exited,
3718
 
                                      &password_is_read));
3719
 
 
3720
 
  g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
3721
 
 
3722
 
  const task_context *const added_read_task
3723
 
    = find_matching_task(queue,
3724
 
                         (task_context){ .func=read_inotify_event });
3725
 
  g_assert_nonnull(added_read_task);
3726
 
 
3727
 
  /* "sufficient to read at least one event." - inotify(7) */
3728
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3729
 
                              + NAME_MAX + 1);
3730
 
  struct inotify_event *ievent = malloc(ievent_size);
3731
 
  g_assert_nonnull(ievent);
3732
 
 
3733
 
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3734
 
 
3735
 
  g_assert_cmpint((int)read_size, >, 0);
3736
 
  g_assert_true(ievent->mask & IN_MOVED_FROM);
3737
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
3738
 
 
3739
 
  free(ievent);
3740
 
 
3741
 
  g_assert_cmpint(unlink(targetfilename), ==, 0);
3742
 
  g_assert_cmpint(rmdir(targetdir), ==, 0);
3743
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3744
 
}
3745
 
 
3746
 
static
3747
 
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3748
 
                                          test_fixture *fixture,
3749
 
                                          __attribute__((unused))
3750
 
                                          gconstpointer user_data){
3751
 
  __attribute__((cleanup(cleanup_close)))
3752
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3753
 
  g_assert_cmpint(epoll_fd, >=, 0);
3754
 
  __attribute__((cleanup(cleanup_queue)))
3755
 
    task_queue *queue = create_queue();
3756
 
  g_assert_nonnull(queue);
3757
 
  __attribute__((cleanup(string_set_clear)))
3758
 
    string_set cancelled_filenames = {};
3759
 
  const mono_microsecs current_time = 0;
3760
 
 
3761
 
  bool quit_now = false;
3762
 
  buffer password = {};
3763
 
  bool mandos_client_exited = false;
3764
 
  bool password_is_read = false;
3765
 
 
3766
 
  __attribute__((cleanup(cleanup_string)))
3767
 
    char *tempdir = make_temporary_directory();
3768
 
  g_assert_nonnull(tempdir);
3769
 
 
3770
 
  __attribute__((cleanup(cleanup_string)))
3771
 
    char *tempfile = make_temporary_file_in_directory(tempdir);
3772
 
  g_assert_nonnull(tempfile);
3773
 
 
3774
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3775
 
                                      &password, tempdir,
3776
 
                                      &cancelled_filenames,
3777
 
                                      &current_time,
3778
 
                                      &mandos_client_exited,
3779
 
                                      &password_is_read));
3780
 
  g_assert_cmpint(unlink(tempfile), ==, 0);
3781
 
 
3782
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
3783
 
 
3784
 
  const task_context *const added_read_task
3785
 
    = find_matching_task(queue,
3786
 
                         (task_context){ .func=read_inotify_event });
3787
 
  g_assert_nonnull(added_read_task);
3788
 
 
3789
 
  g_assert_cmpint(added_read_task->fd, >, 2);
3790
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3791
 
 
3792
 
  /* "sufficient to read at least one event." - inotify(7) */
3793
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3794
 
                              + NAME_MAX + 1);
3795
 
  struct inotify_event *ievent = malloc(ievent_size);
3796
 
  g_assert_nonnull(ievent);
3797
 
 
3798
 
  ssize_t read_size = 0;
3799
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
3800
 
 
3801
 
  g_assert_cmpint((int)read_size, >, 0);
3802
 
  g_assert_true(ievent->mask & IN_DELETE);
3803
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3804
 
 
3805
 
  free(ievent);
3806
 
 
3807
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3808
 
}
3809
 
 
3810
 
static
3811
 
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
3812
 
                                               test_fixture *fixture,
3813
 
                                               __attribute__((unused))
3814
 
                                               gconstpointer
3815
 
                                               user_data){
3816
 
  __attribute__((cleanup(cleanup_close)))
3817
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3818
 
  g_assert_cmpint(epoll_fd, >=, 0);
3819
 
  __attribute__((cleanup(cleanup_queue)))
3820
 
    task_queue *queue = create_queue();
3821
 
  g_assert_nonnull(queue);
3822
 
  __attribute__((cleanup(string_set_clear)))
3823
 
    string_set cancelled_filenames = {};
3824
 
  const mono_microsecs current_time = 0;
3825
 
 
3826
 
  bool quit_now = false;
3827
 
  buffer password = {};
3828
 
  bool mandos_client_exited = false;
3829
 
  bool password_is_read = false;
3830
 
 
3831
 
  __attribute__((cleanup(cleanup_string)))
3832
 
    char *tempdir = make_temporary_directory();
3833
 
  g_assert_nonnull(tempdir);
3834
 
 
3835
 
  __attribute__((cleanup(cleanup_string)))
3836
 
    char *tempfile = make_temporary_file_in_directory(tempdir);
3837
 
  g_assert_nonnull(tempfile);
3838
 
  int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
3839
 
                         | O_NOFOLLOW);
3840
 
  g_assert_cmpint(tempfile_fd, >, 2);
3841
 
 
3842
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3843
 
                                      &password, tempdir,
3844
 
                                      &cancelled_filenames,
3845
 
                                      &current_time,
3846
 
                                      &mandos_client_exited,
3847
 
                                      &password_is_read));
3848
 
  g_assert_cmpint(unlink(tempfile), ==, 0);
3849
 
 
3850
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
3851
 
 
3852
 
  const task_context *const added_read_task
3853
 
    = find_matching_task(queue,
3854
 
                         (task_context){ .func=read_inotify_event });
3855
 
  g_assert_nonnull(added_read_task);
3856
 
 
3857
 
  g_assert_cmpint(added_read_task->fd, >, 2);
3858
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3859
 
 
3860
 
  /* "sufficient to read at least one event." - inotify(7) */
3861
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3862
 
                              + NAME_MAX + 1);
3863
 
  struct inotify_event *ievent = malloc(ievent_size);
3864
 
  g_assert_nonnull(ievent);
3865
 
 
3866
 
  ssize_t read_size = 0;
3867
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
3868
 
 
3869
 
  g_assert_cmpint((int)read_size, >, 0);
3870
 
  g_assert_true(ievent->mask & IN_DELETE);
3871
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3872
 
 
3873
 
  g_assert_cmpint(close(tempfile_fd), ==, 0);
3874
 
 
3875
 
  /* IN_EXCL_UNLINK should make the closing of the previously unlinked
3876
 
     file not appear as an ievent, so we should not see it now. */
3877
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
3878
 
  g_assert_cmpint((int)read_size, ==, -1);
3879
 
  g_assert_true(errno == EAGAIN);
3880
 
 
3881
 
  free(ievent);
3882
 
 
3883
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3884
 
}
3885
 
 
3886
 
static void test_read_inotify_event_readerror(__attribute__((unused))
3887
 
                                              test_fixture *fixture,
3888
 
                                              __attribute__((unused))
3889
 
                                              gconstpointer
3890
 
                                              user_data){
3891
 
  __attribute__((cleanup(cleanup_close)))
3892
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3893
 
  g_assert_cmpint(epoll_fd, >=, 0);
3894
 
  const mono_microsecs current_time = 0;
3895
 
 
3896
 
  /* Reading /proc/self/mem from offset 0 will always result in EIO */
3897
 
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
3898
 
 
3899
 
  bool quit_now = false;
3900
 
  __attribute__((cleanup(cleanup_queue)))
3901
 
    task_queue *queue = create_queue();
3902
 
  g_assert_nonnull(queue);
3903
 
 
3904
 
  task_context task = {
3905
 
    .func=read_inotify_event,
3906
 
    .epoll_fd=epoll_fd,
3907
 
    .fd=fd,
3908
 
    .quit_now=&quit_now,
3909
 
    .filename=strdup("/nonexistent"),
3910
 
    .cancelled_filenames = &(string_set){},
3911
 
    .notafter=0,
3912
 
    .current_time=&current_time,
3913
 
  };
3914
 
  g_assert_nonnull(task.filename);
3915
 
  run_task_with_stderr_to_dev_null(task, queue);
3916
 
  g_assert_true(quit_now);
3917
 
  g_assert_true(queue->next_run == 0);
3918
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3919
 
 
3920
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
3921
 
 
3922
 
  g_assert_cmpint(close(fd), ==, -1);
3923
 
}
3924
 
 
3925
 
static void test_read_inotify_event_bad_epoll(__attribute__((unused))
3926
 
                                              test_fixture *fixture,
3927
 
                                              __attribute__((unused))
3928
 
                                              gconstpointer
3929
 
                                              user_data){
3930
 
  const mono_microsecs current_time = 17;
3931
 
 
3932
 
  int pipefds[2];
3933
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3934
 
  const int epoll_fd = pipefds[0]; /* This will obviously fail */
3935
 
 
3936
 
  bool quit_now = false;
3937
 
  buffer password = {};
3938
 
  bool mandos_client_exited = false;
3939
 
  bool password_is_read = false;
3940
 
  __attribute__((cleanup(cleanup_queue)))
3941
 
    task_queue *queue = create_queue();
3942
 
  g_assert_nonnull(queue);
3943
 
 
3944
 
  task_context task = {
3945
 
    .func=read_inotify_event,
3946
 
    .epoll_fd=epoll_fd,
3947
 
    .fd=pipefds[0],
3948
 
    .quit_now=&quit_now,
3949
 
    .password=&password,
3950
 
    .filename=strdup("/nonexistent"),
3951
 
    .cancelled_filenames = &(string_set){},
3952
 
    .notafter=0,
3953
 
    .current_time=&current_time,
3954
 
    .mandos_client_exited=&mandos_client_exited,
3955
 
    .password_is_read=&password_is_read,
3956
 
  };
3957
 
  g_assert_nonnull(task.filename);
3958
 
  run_task_with_stderr_to_dev_null(task, queue);
3959
 
 
3960
 
  g_assert_nonnull(find_matching_task(queue, task));
3961
 
  g_assert_true(queue->next_run == 1000000 + current_time);
3962
 
 
3963
 
  g_assert_cmpint(close(pipefds[0]), ==, 0);
3964
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
3965
 
}
3966
 
 
3967
 
static void test_read_inotify_event_nodata(__attribute__((unused))
3968
 
                                           test_fixture *fixture,
3969
 
                                           __attribute__((unused))
3970
 
                                           gconstpointer user_data){
3971
 
  __attribute__((cleanup(cleanup_close)))
3972
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3973
 
  g_assert_cmpint(epoll_fd, >=, 0);
3974
 
  const mono_microsecs current_time = 0;
3975
 
 
3976
 
  int pipefds[2];
3977
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
3978
 
 
3979
 
  bool quit_now = false;
3980
 
  buffer password = {};
3981
 
  bool mandos_client_exited = false;
3982
 
  bool password_is_read = false;
3983
 
  __attribute__((cleanup(cleanup_queue)))
3984
 
    task_queue *queue = create_queue();
3985
 
  g_assert_nonnull(queue);
3986
 
 
3987
 
  task_context task = {
3988
 
    .func=read_inotify_event,
3989
 
    .epoll_fd=epoll_fd,
3990
 
    .fd=pipefds[0],
3991
 
    .quit_now=&quit_now,
3992
 
    .password=&password,
3993
 
    .filename=strdup("/nonexistent"),
3994
 
    .cancelled_filenames = &(string_set){},
3995
 
    .notafter=0,
3996
 
    .current_time=&current_time,
3997
 
    .mandos_client_exited=&mandos_client_exited,
3998
 
    .password_is_read=&password_is_read,
3999
 
  };
4000
 
  g_assert_nonnull(task.filename);
4001
 
  task.func(task, queue);
4002
 
  g_assert_false(quit_now);
4003
 
  g_assert_true(queue->next_run == 0);
4004
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4005
 
 
4006
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4007
 
        .func=read_inotify_event,
4008
 
        .epoll_fd=epoll_fd,
4009
 
        .fd=pipefds[0],
4010
 
        .quit_now=&quit_now,
4011
 
        .password=&password,
4012
 
        .filename=task.filename,
4013
 
        .cancelled_filenames=task.cancelled_filenames,
4014
 
        .current_time=&current_time,
4015
 
        .mandos_client_exited=&mandos_client_exited,
4016
 
        .password_is_read=&password_is_read,
4017
 
      }));
4018
 
 
4019
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4020
 
                                   EPOLLIN | EPOLLRDHUP));
4021
 
 
4022
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4023
 
}
4024
 
 
4025
 
static void test_read_inotify_event_eof(__attribute__((unused))
4026
 
                                        test_fixture *fixture,
4027
 
                                        __attribute__((unused))
4028
 
                                        gconstpointer user_data){
4029
 
  __attribute__((cleanup(cleanup_close)))
4030
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4031
 
  g_assert_cmpint(epoll_fd, >=, 0);
4032
 
  const mono_microsecs current_time = 0;
4033
 
 
4034
 
  int pipefds[2];
4035
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4036
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4037
 
 
4038
 
  bool quit_now = false;
4039
 
  buffer password = {};
4040
 
  __attribute__((cleanup(cleanup_queue)))
4041
 
    task_queue *queue = create_queue();
4042
 
  g_assert_nonnull(queue);
4043
 
 
4044
 
  task_context task = {
4045
 
    .func=read_inotify_event,
4046
 
    .epoll_fd=epoll_fd,
4047
 
    .fd=pipefds[0],
4048
 
    .quit_now=&quit_now,
4049
 
    .password=&password,
4050
 
    .filename=strdup("/nonexistent"),
4051
 
    .cancelled_filenames = &(string_set){},
4052
 
    .notafter=0,
4053
 
    .current_time=&current_time,
4054
 
  };
4055
 
  run_task_with_stderr_to_dev_null(task, queue);
4056
 
  g_assert_true(quit_now);
4057
 
  g_assert_true(queue->next_run == 0);
4058
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4059
 
 
4060
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
4061
 
 
4062
 
  g_assert_cmpint(close(pipefds[0]), ==, -1);
4063
 
}
4064
 
 
4065
 
static
4066
 
void test_read_inotify_event_IN_CLOSE_WRITE(__attribute__((unused))
4067
 
                                            test_fixture *fixture,
4068
 
                                            __attribute__((unused))
4069
 
                                            gconstpointer user_data){
4070
 
  __attribute__((cleanup(cleanup_close)))
4071
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4072
 
  g_assert_cmpint(epoll_fd, >=, 0);
4073
 
  const mono_microsecs current_time = 0;
4074
 
 
4075
 
  int pipefds[2];
4076
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4077
 
 
4078
 
  /* "sufficient to read at least one event." - inotify(7) */
4079
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4080
 
                                  + NAME_MAX + 1);
4081
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4082
 
  struct {
4083
 
    struct inotify_event event;
4084
 
    char name_buffer[NAME_MAX + 1];
4085
 
  } ievent_buffer;
4086
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4087
 
 
4088
 
  const char dummy_file_name[] = "ask.dummy_file_name";
4089
 
  ievent->mask = IN_CLOSE_WRITE;
4090
 
  ievent->len = sizeof(dummy_file_name);
4091
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4092
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4093
 
                              + sizeof(dummy_file_name));
4094
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4095
 
                  ==, ievent_size);
4096
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4097
 
 
4098
 
  bool quit_now = false;
4099
 
  buffer password = {};
4100
 
  bool mandos_client_exited = false;
4101
 
  bool password_is_read = false;
4102
 
  __attribute__((cleanup(cleanup_queue)))
4103
 
    task_queue *queue = create_queue();
4104
 
  g_assert_nonnull(queue);
4105
 
 
4106
 
  task_context task = {
4107
 
    .func=read_inotify_event,
4108
 
    .epoll_fd=epoll_fd,
4109
 
    .fd=pipefds[0],
4110
 
    .quit_now=&quit_now,
4111
 
    .password=&password,
4112
 
    .filename=strdup("/nonexistent"),
4113
 
    .cancelled_filenames = &(string_set){},
4114
 
    .notafter=0,
4115
 
    .current_time=&current_time,
4116
 
    .mandos_client_exited=&mandos_client_exited,
4117
 
    .password_is_read=&password_is_read,
4118
 
  };
4119
 
  task.func(task, queue);
4120
 
  g_assert_false(quit_now);
4121
 
  g_assert_true(queue->next_run != 0);
4122
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4123
 
 
4124
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4125
 
        .func=read_inotify_event,
4126
 
        .epoll_fd=epoll_fd,
4127
 
        .fd=pipefds[0],
4128
 
        .quit_now=&quit_now,
4129
 
        .password=&password,
4130
 
        .filename=task.filename,
4131
 
        .cancelled_filenames=task.cancelled_filenames,
4132
 
        .current_time=&current_time,
4133
 
        .mandos_client_exited=&mandos_client_exited,
4134
 
        .password_is_read=&password_is_read,
4135
 
      }));
4136
 
 
4137
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4138
 
                                   EPOLLIN | EPOLLRDHUP));
4139
 
 
4140
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4141
 
 
4142
 
  __attribute__((cleanup(cleanup_string)))
4143
 
    char *filename = NULL;
4144
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4145
 
                           dummy_file_name), >, 0);
4146
 
  g_assert_nonnull(filename);
4147
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4148
 
        .func=open_and_parse_question,
4149
 
        .epoll_fd=epoll_fd,
4150
 
        .filename=filename,
4151
 
        .question_filename=filename,
4152
 
        .password=&password,
4153
 
        .cancelled_filenames=task.cancelled_filenames,
4154
 
        .current_time=&current_time,
4155
 
        .mandos_client_exited=&mandos_client_exited,
4156
 
        .password_is_read=&password_is_read,
4157
 
      }));
4158
 
}
4159
 
 
4160
 
static
4161
 
void test_read_inotify_event_IN_MOVED_TO(__attribute__((unused))
4162
 
                                         test_fixture *fixture,
4163
 
                                         __attribute__((unused))
4164
 
                                         gconstpointer user_data){
4165
 
  __attribute__((cleanup(cleanup_close)))
4166
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4167
 
  g_assert_cmpint(epoll_fd, >=, 0);
4168
 
  const mono_microsecs current_time = 0;
4169
 
 
4170
 
  int pipefds[2];
4171
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4172
 
 
4173
 
  /* "sufficient to read at least one event." - inotify(7) */
4174
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4175
 
                                  + NAME_MAX + 1);
4176
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4177
 
  struct {
4178
 
    struct inotify_event event;
4179
 
    char name_buffer[NAME_MAX + 1];
4180
 
  } ievent_buffer;
4181
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4182
 
 
4183
 
  const char dummy_file_name[] = "ask.dummy_file_name";
4184
 
  ievent->mask = IN_MOVED_TO;
4185
 
  ievent->len = sizeof(dummy_file_name);
4186
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4187
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4188
 
                              + sizeof(dummy_file_name));
4189
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4190
 
                  ==, ievent_size);
4191
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4192
 
 
4193
 
  bool quit_now = false;
4194
 
  buffer password = {};
4195
 
  bool mandos_client_exited = false;
4196
 
  bool password_is_read = false;
4197
 
  __attribute__((cleanup(cleanup_queue)))
4198
 
    task_queue *queue = create_queue();
4199
 
  g_assert_nonnull(queue);
4200
 
 
4201
 
  task_context task = {
4202
 
    .func=read_inotify_event,
4203
 
    .epoll_fd=epoll_fd,
4204
 
    .fd=pipefds[0],
4205
 
    .quit_now=&quit_now,
4206
 
    .password=&password,
4207
 
    .filename=strdup("/nonexistent"),
4208
 
    .cancelled_filenames = &(string_set){},
4209
 
    .notafter=0,
4210
 
    .current_time=&current_time,
4211
 
    .mandos_client_exited=&mandos_client_exited,
4212
 
    .password_is_read=&password_is_read,
4213
 
  };
4214
 
  task.func(task, queue);
4215
 
  g_assert_false(quit_now);
4216
 
  g_assert_true(queue->next_run != 0);
4217
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 1);
4218
 
 
4219
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4220
 
        .func=read_inotify_event,
4221
 
        .epoll_fd=epoll_fd,
4222
 
        .fd=pipefds[0],
4223
 
        .quit_now=&quit_now,
4224
 
        .password=&password,
4225
 
        .filename=task.filename,
4226
 
        .cancelled_filenames=task.cancelled_filenames,
4227
 
        .current_time=&current_time,
4228
 
        .mandos_client_exited=&mandos_client_exited,
4229
 
        .password_is_read=&password_is_read,
4230
 
      }));
4231
 
 
4232
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4233
 
                                   EPOLLIN | EPOLLRDHUP));
4234
 
 
4235
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
4236
 
 
4237
 
  __attribute__((cleanup(cleanup_string)))
4238
 
    char *filename = NULL;
4239
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4240
 
                           dummy_file_name), >, 0);
4241
 
  g_assert_nonnull(filename);
4242
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4243
 
        .func=open_and_parse_question,
4244
 
        .epoll_fd=epoll_fd,
4245
 
        .filename=filename,
4246
 
        .question_filename=filename,
4247
 
        .password=&password,
4248
 
        .cancelled_filenames=task.cancelled_filenames,
4249
 
        .current_time=&current_time,
4250
 
        .mandos_client_exited=&mandos_client_exited,
4251
 
        .password_is_read=&password_is_read,
4252
 
      }));
4253
 
}
4254
 
 
4255
 
static
4256
 
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
4257
 
                                           test_fixture *fixture,
4258
 
                                           __attribute__((unused))
4259
 
                                           gconstpointer user_data){
4260
 
  __attribute__((cleanup(cleanup_close)))
4261
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4262
 
  g_assert_cmpint(epoll_fd, >=, 0);
4263
 
  __attribute__((cleanup(string_set_clear)))
4264
 
    string_set cancelled_filenames = {};
4265
 
  const mono_microsecs current_time = 0;
4266
 
 
4267
 
  int pipefds[2];
4268
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4269
 
 
4270
 
  /* "sufficient to read at least one event." - inotify(7) */
4271
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4272
 
                                  + NAME_MAX + 1);
4273
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4274
 
  struct {
4275
 
    struct inotify_event event;
4276
 
    char name_buffer[NAME_MAX + 1];
4277
 
  } ievent_buffer;
4278
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4279
 
 
4280
 
  const char dummy_file_name[] = "ask.dummy_file_name";
4281
 
  ievent->mask = IN_MOVED_FROM;
4282
 
  ievent->len = sizeof(dummy_file_name);
4283
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4284
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4285
 
                              + sizeof(dummy_file_name));
4286
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4287
 
                  ==, ievent_size);
4288
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4289
 
 
4290
 
  bool quit_now = false;
4291
 
  buffer password = {};
4292
 
  bool mandos_client_exited = false;
4293
 
  bool password_is_read = false;
4294
 
  __attribute__((cleanup(cleanup_queue)))
4295
 
    task_queue *queue = create_queue();
4296
 
  g_assert_nonnull(queue);
4297
 
 
4298
 
  task_context task = {
4299
 
    .func=read_inotify_event,
4300
 
    .epoll_fd=epoll_fd,
4301
 
    .fd=pipefds[0],
4302
 
    .quit_now=&quit_now,
4303
 
    .password=&password,
4304
 
    .filename=strdup("/nonexistent"),
4305
 
    .cancelled_filenames=&cancelled_filenames,
4306
 
    .current_time=&current_time,
4307
 
    .mandos_client_exited=&mandos_client_exited,
4308
 
    .password_is_read=&password_is_read,
4309
 
  };
4310
 
  task.func(task, queue);
4311
 
  g_assert_false(quit_now);
4312
 
  g_assert_true(queue->next_run == 0);
4313
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4314
 
 
4315
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4316
 
        .func=read_inotify_event,
4317
 
        .epoll_fd=epoll_fd,
4318
 
        .fd=pipefds[0],
4319
 
        .quit_now=&quit_now,
4320
 
        .password=&password,
4321
 
        .filename=task.filename,
4322
 
        .cancelled_filenames=&cancelled_filenames,
4323
 
        .current_time=&current_time,
4324
 
        .mandos_client_exited=&mandos_client_exited,
4325
 
        .password_is_read=&password_is_read,
4326
 
      }));
4327
 
 
4328
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4329
 
                                   EPOLLIN | EPOLLRDHUP));
4330
 
 
4331
 
  __attribute__((cleanup(cleanup_string)))
4332
 
    char *filename = NULL;
4333
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4334
 
                           dummy_file_name), >, 0);
4335
 
  g_assert_nonnull(filename);
4336
 
  g_assert_true(string_set_contains(*task.cancelled_filenames,
4337
 
                                    filename));
4338
 
}
4339
 
 
4340
 
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4341
 
                                              test_fixture *fixture,
4342
 
                                              __attribute__((unused))
4343
 
                                              gconstpointer
4344
 
                                              user_data){
4345
 
  __attribute__((cleanup(cleanup_close)))
4346
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4347
 
  g_assert_cmpint(epoll_fd, >=, 0);
4348
 
  __attribute__((cleanup(string_set_clear)))
4349
 
    string_set cancelled_filenames = {};
4350
 
  const mono_microsecs current_time = 0;
4351
 
 
4352
 
  int pipefds[2];
4353
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4354
 
 
4355
 
  /* "sufficient to read at least one event." - inotify(7) */
4356
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4357
 
                                  + NAME_MAX + 1);
4358
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4359
 
  struct {
4360
 
    struct inotify_event event;
4361
 
    char name_buffer[NAME_MAX + 1];
4362
 
  } ievent_buffer;
4363
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4364
 
 
4365
 
  const char dummy_file_name[] = "ask.dummy_file_name";
4366
 
  ievent->mask = IN_DELETE;
4367
 
  ievent->len = sizeof(dummy_file_name);
4368
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4369
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4370
 
                              + sizeof(dummy_file_name));
4371
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4372
 
                  ==, ievent_size);
4373
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4374
 
 
4375
 
  bool quit_now = false;
4376
 
  buffer password = {};
4377
 
  bool mandos_client_exited = false;
4378
 
  bool password_is_read = false;
4379
 
  __attribute__((cleanup(cleanup_queue)))
4380
 
    task_queue *queue = create_queue();
4381
 
  g_assert_nonnull(queue);
4382
 
 
4383
 
  task_context task = {
4384
 
    .func=read_inotify_event,
4385
 
    .epoll_fd=epoll_fd,
4386
 
    .fd=pipefds[0],
4387
 
    .quit_now=&quit_now,
4388
 
    .password=&password,
4389
 
    .filename=strdup("/nonexistent"),
4390
 
    .cancelled_filenames=&cancelled_filenames,
4391
 
    .current_time=&current_time,
4392
 
    .mandos_client_exited=&mandos_client_exited,
4393
 
    .password_is_read=&password_is_read,
4394
 
  };
4395
 
  task.func(task, queue);
4396
 
  g_assert_false(quit_now);
4397
 
  g_assert_true(queue->next_run == 0);
4398
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4399
 
 
4400
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4401
 
        .func=read_inotify_event,
4402
 
        .epoll_fd=epoll_fd,
4403
 
        .fd=pipefds[0],
4404
 
        .quit_now=&quit_now,
4405
 
        .password=&password,
4406
 
        .filename=task.filename,
4407
 
        .cancelled_filenames=&cancelled_filenames,
4408
 
        .current_time=&current_time,
4409
 
        .mandos_client_exited=&mandos_client_exited,
4410
 
        .password_is_read=&password_is_read,
4411
 
      }));
4412
 
 
4413
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4414
 
                                   EPOLLIN | EPOLLRDHUP));
4415
 
 
4416
 
  __attribute__((cleanup(cleanup_string)))
4417
 
    char *filename = NULL;
4418
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4419
 
                           dummy_file_name), >, 0);
4420
 
  g_assert_nonnull(filename);
4421
 
  g_assert_true(string_set_contains(*task.cancelled_filenames,
4422
 
                                    filename));
4423
 
}
4424
 
 
4425
 
static void
4426
 
test_read_inotify_event_IN_CLOSE_WRITE_badname(__attribute__((unused))
4427
 
                                               test_fixture *fixture,
4428
 
                                               __attribute__((unused))
4429
 
                                               gconstpointer
4430
 
                                               user_data){
4431
 
  __attribute__((cleanup(cleanup_close)))
4432
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4433
 
  g_assert_cmpint(epoll_fd, >=, 0);
4434
 
  const mono_microsecs current_time = 0;
4435
 
 
4436
 
  int pipefds[2];
4437
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4438
 
 
4439
 
  /* "sufficient to read at least one event." - inotify(7) */
4440
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4441
 
                                  + NAME_MAX + 1);
4442
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4443
 
  struct {
4444
 
    struct inotify_event event;
4445
 
    char name_buffer[NAME_MAX + 1];
4446
 
  } ievent_buffer;
4447
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4448
 
 
4449
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
4450
 
  ievent->mask = IN_CLOSE_WRITE;
4451
 
  ievent->len = sizeof(dummy_file_name);
4452
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4453
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4454
 
                              + sizeof(dummy_file_name));
4455
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4456
 
                  ==, ievent_size);
4457
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4458
 
 
4459
 
  bool quit_now = false;
4460
 
  buffer password = {};
4461
 
  bool mandos_client_exited = false;
4462
 
  bool password_is_read = false;
4463
 
  __attribute__((cleanup(cleanup_queue)))
4464
 
    task_queue *queue = create_queue();
4465
 
  g_assert_nonnull(queue);
4466
 
 
4467
 
  task_context task = {
4468
 
    .func=read_inotify_event,
4469
 
    .epoll_fd=epoll_fd,
4470
 
    .fd=pipefds[0],
4471
 
    .quit_now=&quit_now,
4472
 
    .password=&password,
4473
 
    .filename=strdup("/nonexistent"),
4474
 
    .cancelled_filenames = &(string_set){},
4475
 
    .notafter=0,
4476
 
    .current_time=&current_time,
4477
 
    .mandos_client_exited=&mandos_client_exited,
4478
 
    .password_is_read=&password_is_read,
4479
 
  };
4480
 
  task.func(task, queue);
4481
 
  g_assert_false(quit_now);
4482
 
  g_assert_true(queue->next_run == 0);
4483
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4484
 
 
4485
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4486
 
        .func=read_inotify_event,
4487
 
        .epoll_fd=epoll_fd,
4488
 
        .fd=pipefds[0],
4489
 
        .quit_now=&quit_now,
4490
 
        .password=&password,
4491
 
        .filename=task.filename,
4492
 
        .cancelled_filenames=task.cancelled_filenames,
4493
 
        .current_time=&current_time,
4494
 
        .mandos_client_exited=&mandos_client_exited,
4495
 
        .password_is_read=&password_is_read,
4496
 
      }));
4497
 
 
4498
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4499
 
                                   EPOLLIN | EPOLLRDHUP));
4500
 
}
4501
 
 
4502
 
static void
4503
 
test_read_inotify_event_IN_MOVED_TO_badname(__attribute__((unused))
4504
 
                                            test_fixture *fixture,
4505
 
                                            __attribute__((unused))
4506
 
                                            gconstpointer user_data){
4507
 
  __attribute__((cleanup(cleanup_close)))
4508
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4509
 
  g_assert_cmpint(epoll_fd, >=, 0);
4510
 
  const mono_microsecs current_time = 0;
4511
 
 
4512
 
  int pipefds[2];
4513
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4514
 
 
4515
 
  /* "sufficient to read at least one event." - inotify(7) */
4516
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4517
 
                                  + NAME_MAX + 1);
4518
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4519
 
  struct {
4520
 
    struct inotify_event event;
4521
 
    char name_buffer[NAME_MAX + 1];
4522
 
  } ievent_buffer;
4523
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4524
 
 
4525
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
4526
 
  ievent->mask = IN_MOVED_TO;
4527
 
  ievent->len = sizeof(dummy_file_name);
4528
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4529
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4530
 
                              + sizeof(dummy_file_name));
4531
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4532
 
                  ==, ievent_size);
4533
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4534
 
 
4535
 
  bool quit_now = false;
4536
 
  buffer password = {};
4537
 
  bool mandos_client_exited = false;
4538
 
  bool password_is_read = false;
4539
 
  __attribute__((cleanup(cleanup_queue)))
4540
 
    task_queue *queue = create_queue();
4541
 
  g_assert_nonnull(queue);
4542
 
 
4543
 
  task_context task = {
4544
 
    .func=read_inotify_event,
4545
 
    .epoll_fd=epoll_fd,
4546
 
    .fd=pipefds[0],
4547
 
    .quit_now=&quit_now,
4548
 
    .password=&password,
4549
 
    .filename=strdup("/nonexistent"),
4550
 
    .cancelled_filenames = &(string_set){},
4551
 
    .notafter=0,
4552
 
    .current_time=&current_time,
4553
 
    .mandos_client_exited=&mandos_client_exited,
4554
 
    .password_is_read=&password_is_read,
4555
 
  };
4556
 
  task.func(task, queue);
4557
 
  g_assert_false(quit_now);
4558
 
  g_assert_true(queue->next_run == 0);
4559
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4560
 
 
4561
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4562
 
        .func=read_inotify_event,
4563
 
        .epoll_fd=epoll_fd,
4564
 
        .fd=pipefds[0],
4565
 
        .quit_now=&quit_now,
4566
 
        .password=&password,
4567
 
        .filename=task.filename,
4568
 
        .cancelled_filenames=task.cancelled_filenames,
4569
 
        .current_time=&current_time,
4570
 
        .mandos_client_exited=&mandos_client_exited,
4571
 
        .password_is_read=&password_is_read,
4572
 
      }));
4573
 
 
4574
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4575
 
                                   EPOLLIN | EPOLLRDHUP));
4576
 
}
4577
 
 
4578
 
static void
4579
 
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
4580
 
                                              test_fixture *fixture,
4581
 
                                              __attribute__((unused))
4582
 
                                              gconstpointer
4583
 
                                              user_data){
4584
 
  __attribute__((cleanup(cleanup_close)))
4585
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4586
 
  g_assert_cmpint(epoll_fd, >=, 0);
4587
 
  __attribute__((cleanup(string_set_clear)))
4588
 
    string_set cancelled_filenames = {};
4589
 
  const mono_microsecs current_time = 0;
4590
 
 
4591
 
  int pipefds[2];
4592
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4593
 
 
4594
 
  /* "sufficient to read at least one event." - inotify(7) */
4595
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4596
 
                                  + NAME_MAX + 1);
4597
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4598
 
  struct {
4599
 
    struct inotify_event event;
4600
 
    char name_buffer[NAME_MAX + 1];
4601
 
  } ievent_buffer;
4602
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4603
 
 
4604
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
4605
 
  ievent->mask = IN_MOVED_FROM;
4606
 
  ievent->len = sizeof(dummy_file_name);
4607
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4608
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4609
 
                              + sizeof(dummy_file_name));
4610
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4611
 
                  ==, ievent_size);
4612
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4613
 
 
4614
 
  bool quit_now = false;
4615
 
  buffer password = {};
4616
 
  bool mandos_client_exited = false;
4617
 
  bool password_is_read = false;
4618
 
  __attribute__((cleanup(cleanup_queue)))
4619
 
    task_queue *queue = create_queue();
4620
 
  g_assert_nonnull(queue);
4621
 
 
4622
 
  task_context task = {
4623
 
    .func=read_inotify_event,
4624
 
    .epoll_fd=epoll_fd,
4625
 
    .fd=pipefds[0],
4626
 
    .quit_now=&quit_now,
4627
 
    .password=&password,
4628
 
    .filename=strdup("/nonexistent"),
4629
 
    .cancelled_filenames=&cancelled_filenames,
4630
 
    .current_time=&current_time,
4631
 
    .mandos_client_exited=&mandos_client_exited,
4632
 
    .password_is_read=&password_is_read,
4633
 
  };
4634
 
  task.func(task, queue);
4635
 
  g_assert_false(quit_now);
4636
 
  g_assert_true(queue->next_run == 0);
4637
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4638
 
 
4639
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4640
 
        .func=read_inotify_event,
4641
 
        .epoll_fd=epoll_fd,
4642
 
        .fd=pipefds[0],
4643
 
        .quit_now=&quit_now,
4644
 
        .password=&password,
4645
 
        .filename=task.filename,
4646
 
        .cancelled_filenames=&cancelled_filenames,
4647
 
        .current_time=&current_time,
4648
 
        .mandos_client_exited=&mandos_client_exited,
4649
 
        .password_is_read=&password_is_read,
4650
 
      }));
4651
 
 
4652
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4653
 
                                   EPOLLIN | EPOLLRDHUP));
4654
 
 
4655
 
  __attribute__((cleanup(cleanup_string)))
4656
 
    char *filename = NULL;
4657
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4658
 
                           dummy_file_name), >, 0);
4659
 
  g_assert_nonnull(filename);
4660
 
  g_assert_false(string_set_contains(cancelled_filenames, filename));
4661
 
}
4662
 
 
4663
 
static
4664
 
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4665
 
                                               test_fixture *fixture,
4666
 
                                               __attribute__((unused))
4667
 
                                               gconstpointer
4668
 
                                               user_data){
4669
 
  __attribute__((cleanup(cleanup_close)))
4670
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4671
 
  g_assert_cmpint(epoll_fd, >=, 0);
4672
 
  __attribute__((cleanup(string_set_clear)))
4673
 
    string_set cancelled_filenames = {};
4674
 
  const mono_microsecs current_time = 0;
4675
 
 
4676
 
  int pipefds[2];
4677
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4678
 
 
4679
 
  /* "sufficient to read at least one event." - inotify(7) */
4680
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4681
 
                                  + NAME_MAX + 1);
4682
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4683
 
  struct {
4684
 
    struct inotify_event event;
4685
 
    char name_buffer[NAME_MAX + 1];
4686
 
  } ievent_buffer;
4687
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4688
 
 
4689
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
4690
 
  ievent->mask = IN_DELETE;
4691
 
  ievent->len = sizeof(dummy_file_name);
4692
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4693
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4694
 
                              + sizeof(dummy_file_name));
4695
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4696
 
                  ==, ievent_size);
4697
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4698
 
 
4699
 
  bool quit_now = false;
4700
 
  buffer password = {};
4701
 
  bool mandos_client_exited = false;
4702
 
  bool password_is_read = false;
4703
 
  __attribute__((cleanup(cleanup_queue)))
4704
 
    task_queue *queue = create_queue();
4705
 
  g_assert_nonnull(queue);
4706
 
 
4707
 
  task_context task = {
4708
 
    .func=read_inotify_event,
4709
 
    .epoll_fd=epoll_fd,
4710
 
    .fd=pipefds[0],
4711
 
    .quit_now=&quit_now,
4712
 
    .password=&password,
4713
 
    .filename=strdup("/nonexistent"),
4714
 
    .cancelled_filenames=&cancelled_filenames,
4715
 
    .current_time=&current_time,
4716
 
    .mandos_client_exited=&mandos_client_exited,
4717
 
    .password_is_read=&password_is_read,
4718
 
  };
4719
 
  task.func(task, queue);
4720
 
  g_assert_false(quit_now);
4721
 
  g_assert_true(queue->next_run == 0);
4722
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4723
 
 
4724
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4725
 
        .func=read_inotify_event,
4726
 
        .epoll_fd=epoll_fd,
4727
 
        .fd=pipefds[0],
4728
 
        .quit_now=&quit_now,
4729
 
        .password=&password,
4730
 
        .filename=task.filename,
4731
 
        .cancelled_filenames=&cancelled_filenames,
4732
 
        .current_time=&current_time,
4733
 
        .mandos_client_exited=&mandos_client_exited,
4734
 
        .password_is_read=&password_is_read,
4735
 
      }));
4736
 
 
4737
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4738
 
                                   EPOLLIN | EPOLLRDHUP));
4739
 
 
4740
 
  __attribute__((cleanup(cleanup_string)))
4741
 
    char *filename = NULL;
4742
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4743
 
                           dummy_file_name), >, 0);
4744
 
  g_assert_nonnull(filename);
4745
 
  g_assert_false(string_set_contains(cancelled_filenames, filename));
4746
 
}
4747
 
 
4748
 
static
4749
 
void test_open_and_parse_question_ENOENT(__attribute__((unused))
4750
 
                                         test_fixture *fixture,
4751
 
                                         __attribute__((unused))
4752
 
                                         gconstpointer user_data){
4753
 
  __attribute__((cleanup(cleanup_close)))
4754
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4755
 
  g_assert_cmpint(epoll_fd, >=, 0);
4756
 
  __attribute__((cleanup(string_set_clear)))
4757
 
    string_set cancelled_filenames = {};
4758
 
  bool mandos_client_exited = false;
4759
 
  bool password_is_read = false;
4760
 
  __attribute__((cleanup(cleanup_queue)))
4761
 
    task_queue *queue = create_queue();
4762
 
  g_assert_nonnull(queue);
4763
 
 
4764
 
  char *const filename = strdup("/nonexistent");
4765
 
  g_assert_nonnull(filename);
4766
 
  task_context task = {
4767
 
    .func=open_and_parse_question,
4768
 
    .question_filename=filename,
4769
 
    .epoll_fd=epoll_fd,
4770
 
    .password=(buffer[]){{}},
4771
 
    .filename=filename,
4772
 
    .cancelled_filenames=&cancelled_filenames,
4773
 
    .current_time=(mono_microsecs[]){0},
4774
 
    .mandos_client_exited=&mandos_client_exited,
4775
 
    .password_is_read=&password_is_read,
4776
 
  };
4777
 
  task.func(task, queue);
4778
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4779
 
}
4780
 
 
4781
 
static void test_open_and_parse_question_EIO(__attribute__((unused))
4782
 
                                             test_fixture *fixture,
4783
 
                                             __attribute__((unused))
4784
 
                                             gconstpointer user_data){
4785
 
  __attribute__((cleanup(cleanup_close)))
4786
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4787
 
  g_assert_cmpint(epoll_fd, >=, 0);
4788
 
  __attribute__((cleanup(string_set_clear)))
4789
 
    string_set cancelled_filenames = {};
4790
 
  buffer password = {};
4791
 
  bool mandos_client_exited = false;
4792
 
  bool password_is_read = false;
4793
 
  __attribute__((cleanup(cleanup_queue)))
4794
 
    task_queue *queue = create_queue();
4795
 
  g_assert_nonnull(queue);
4796
 
  const mono_microsecs current_time = 0;
4797
 
 
4798
 
  char *filename = strdup("/proc/self/mem");
4799
 
  task_context task = {
4800
 
    .func=open_and_parse_question,
4801
 
    .question_filename=filename,
4802
 
    .epoll_fd=epoll_fd,
4803
 
    .password=&password,
4804
 
    .filename=filename,
4805
 
    .cancelled_filenames=&cancelled_filenames,
4806
 
    .current_time=&current_time,
4807
 
    .mandos_client_exited=&mandos_client_exited,
4808
 
    .password_is_read=&password_is_read,
4809
 
  };
4810
 
  run_task_with_stderr_to_dev_null(task, queue);
4811
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4812
 
}
4813
 
 
4814
 
static void
4815
 
test_open_and_parse_question_parse_error(__attribute__((unused))
4816
 
                                         test_fixture *fixture,
4817
 
                                         __attribute__((unused))
4818
 
                                         gconstpointer user_data){
4819
 
  __attribute__((cleanup(cleanup_close)))
4820
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4821
 
  g_assert_cmpint(epoll_fd, >=, 0);
4822
 
  __attribute__((cleanup(string_set_clear)))
4823
 
    string_set cancelled_filenames = {};
4824
 
  __attribute__((cleanup(cleanup_queue)))
4825
 
    task_queue *queue = create_queue();
4826
 
  g_assert_nonnull(queue);
4827
 
 
4828
 
  __attribute__((cleanup(cleanup_string)))
4829
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
4830
 
  g_assert_nonnull(tempfilename);
4831
 
  int tempfile = mkostemp(tempfilename, O_CLOEXEC);
4832
 
  g_assert_cmpint(tempfile, >, 0);
4833
 
  const char bad_data[] = "this is bad syntax\n";
4834
 
  g_assert_cmpint(write(tempfile, bad_data, sizeof(bad_data)),
4835
 
                  ==, sizeof(bad_data));
4836
 
  g_assert_cmpint(close(tempfile), ==, 0);
4837
 
 
4838
 
  char *const filename = strdup(tempfilename);
4839
 
  g_assert_nonnull(filename);
4840
 
  task_context task = {
4841
 
    .func=open_and_parse_question,
4842
 
    .question_filename=filename,
4843
 
    .epoll_fd=epoll_fd,
4844
 
    .password=(buffer[]){{}},
4845
 
    .filename=filename,
4846
 
    .cancelled_filenames=&cancelled_filenames,
4847
 
    .current_time=(mono_microsecs[]){0},
4848
 
    .mandos_client_exited=(bool[]){false},
4849
 
    .password_is_read=(bool[]){false},
4850
 
  };
4851
 
  run_task_with_stderr_to_dev_null(task, queue);
4852
 
 
4853
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4854
 
 
4855
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
4856
 
}
4857
 
 
4858
 
static
4859
 
void test_open_and_parse_question_nosocket(__attribute__((unused))
4860
 
                                           test_fixture *fixture,
4861
 
                                           __attribute__((unused))
4862
 
                                           gconstpointer user_data){
4863
 
  __attribute__((cleanup(cleanup_close)))
4864
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4865
 
  g_assert_cmpint(epoll_fd, >=, 0);
4866
 
  __attribute__((cleanup(string_set_clear)))
4867
 
    string_set cancelled_filenames = {};
4868
 
  __attribute__((cleanup(cleanup_queue)))
4869
 
    task_queue *queue = create_queue();
4870
 
  g_assert_nonnull(queue);
4871
 
 
4872
 
  __attribute__((cleanup(cleanup_string)))
4873
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
4874
 
  g_assert_nonnull(tempfilename);
4875
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4876
 
  g_assert_cmpint(questionfile, >, 0);
4877
 
  FILE *qf = fdopen(questionfile, "w");
4878
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nPID=1\n"), >, 0);
4879
 
  g_assert_cmpint(fclose(qf), ==, 0);
4880
 
 
4881
 
  char *const filename = strdup(tempfilename);
4882
 
  g_assert_nonnull(filename);
4883
 
  task_context task = {
4884
 
    .func=open_and_parse_question,
4885
 
    .question_filename=filename,
4886
 
    .epoll_fd=epoll_fd,
4887
 
    .password=(buffer[]){{}},
4888
 
    .filename=filename,
4889
 
    .cancelled_filenames=&cancelled_filenames,
4890
 
    .current_time=(mono_microsecs[]){0},
4891
 
    .mandos_client_exited=(bool[]){false},
4892
 
    .password_is_read=(bool[]){false},
4893
 
  };
4894
 
  run_task_with_stderr_to_dev_null(task, queue);
4895
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4896
 
 
4897
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
4898
 
}
4899
 
 
4900
 
static
4901
 
void test_open_and_parse_question_badsocket(__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);
4913
 
 
4914
 
  __attribute__((cleanup(cleanup_string)))
4915
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
4916
 
  g_assert_nonnull(tempfilename);
4917
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4918
 
  g_assert_cmpint(questionfile, >, 0);
4919
 
  FILE *qf = fdopen(questionfile, "w");
4920
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=\nPID=1\n"), >, 0);
4921
 
  g_assert_cmpint(fclose(qf), ==, 0);
4922
 
 
4923
 
  char *const filename = strdup(tempfilename);
4924
 
  g_assert_nonnull(filename);
4925
 
  task_context task = {
4926
 
    .func=open_and_parse_question,
4927
 
    .question_filename=filename,
4928
 
    .epoll_fd=epoll_fd,
4929
 
    .password=(buffer[]){{}},
4930
 
    .filename=filename,
4931
 
    .cancelled_filenames=&cancelled_filenames,
4932
 
    .current_time=(mono_microsecs[]){0},
4933
 
    .mandos_client_exited=(bool[]){false},
4934
 
    .password_is_read=(bool[]){false},
4935
 
  };
4936
 
  run_task_with_stderr_to_dev_null(task, queue);
4937
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4938
 
 
4939
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
4940
 
}
4941
 
 
4942
 
static
4943
 
void test_open_and_parse_question_nopid(__attribute__((unused))
4944
 
                                        test_fixture *fixture,
4945
 
                                        __attribute__((unused))
4946
 
                                        gconstpointer user_data){
4947
 
  __attribute__((cleanup(cleanup_close)))
4948
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4949
 
  g_assert_cmpint(epoll_fd, >=, 0);
4950
 
  __attribute__((cleanup(string_set_clear)))
4951
 
    string_set cancelled_filenames = {};
4952
 
  __attribute__((cleanup(cleanup_queue)))
4953
 
    task_queue *queue = create_queue();
4954
 
  g_assert_nonnull(queue);
4955
 
 
4956
 
  __attribute__((cleanup(cleanup_string)))
4957
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
4958
 
  g_assert_nonnull(tempfilename);
4959
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
4960
 
  g_assert_cmpint(questionfile, >, 0);
4961
 
  FILE *qf = fdopen(questionfile, "w");
4962
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\n"), >, 0);
4963
 
  g_assert_cmpint(fclose(qf), ==, 0);
4964
 
 
4965
 
  char *const filename = strdup(tempfilename);
4966
 
  g_assert_nonnull(filename);
4967
 
  task_context task = {
4968
 
    .func=open_and_parse_question,
4969
 
    .question_filename=filename,
4970
 
    .epoll_fd=epoll_fd,
4971
 
    .password=(buffer[]){{}},
4972
 
    .filename=filename,
4973
 
    .cancelled_filenames=&cancelled_filenames,
4974
 
    .current_time=(mono_microsecs[]){0},
4975
 
    .mandos_client_exited=(bool[]){false},
4976
 
    .password_is_read=(bool[]){false},
4977
 
  };
4978
 
  run_task_with_stderr_to_dev_null(task, queue);
4979
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
4980
 
 
4981
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
4982
 
}
4983
 
 
4984
 
static
4985
 
void test_open_and_parse_question_badpid(__attribute__((unused))
4986
 
                                         test_fixture *fixture,
4987
 
                                         __attribute__((unused))
4988
 
                                         gconstpointer user_data){
4989
 
  __attribute__((cleanup(cleanup_close)))
4990
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4991
 
  g_assert_cmpint(epoll_fd, >=, 0);
4992
 
  __attribute__((cleanup(string_set_clear)))
4993
 
    string_set cancelled_filenames = {};
4994
 
  __attribute__((cleanup(cleanup_queue)))
4995
 
    task_queue *queue = create_queue();
4996
 
  g_assert_nonnull(queue);
4997
 
 
4998
 
  __attribute__((cleanup(cleanup_string)))
4999
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
5000
 
  g_assert_nonnull(tempfilename);
5001
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5002
 
  g_assert_cmpint(questionfile, >, 0);
5003
 
  FILE *qf = fdopen(questionfile, "w");
5004
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=\n"),
5005
 
                  >, 0);
5006
 
  g_assert_cmpint(fclose(qf), ==, 0);
5007
 
 
5008
 
  char *const filename = strdup(tempfilename);
5009
 
  g_assert_nonnull(filename);
5010
 
  task_context task = {
5011
 
    .func=open_and_parse_question,
5012
 
    .question_filename=filename,
5013
 
    .epoll_fd=epoll_fd,
5014
 
    .password=(buffer[]){{}},
5015
 
    .filename=filename,
5016
 
    .cancelled_filenames=&cancelled_filenames,
5017
 
    .current_time=(mono_microsecs[]){0},
5018
 
    .mandos_client_exited=(bool[]){false},
5019
 
    .password_is_read=(bool[]){false},
5020
 
  };
5021
 
  run_task_with_stderr_to_dev_null(task, queue);
5022
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5023
 
 
5024
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
5025
 
}
5026
 
 
5027
 
static void
5028
 
test_open_and_parse_question_noexist_pid(__attribute__((unused))
5029
 
                                         test_fixture *fixture,
5030
 
                                         __attribute__((unused))
5031
 
                                         gconstpointer user_data){
5032
 
  __attribute__((cleanup(cleanup_close)))
5033
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5034
 
  g_assert_cmpint(epoll_fd, >=, 0);
5035
 
  __attribute__((cleanup(string_set_clear)))
5036
 
    string_set cancelled_filenames = {};
5037
 
  buffer password = {};
5038
 
  bool mandos_client_exited = false;
5039
 
  bool password_is_read = false;
5040
 
  __attribute__((cleanup(cleanup_queue)))
5041
 
    task_queue *queue = create_queue();
5042
 
  g_assert_nonnull(queue);
5043
 
  const mono_microsecs current_time = 0;
5044
 
 
5045
 
  /* Find value of sysctl kernel.pid_max */
5046
 
  uintmax_t pid_max = 0;
5047
 
  FILE *sysctl_pid_max = fopen("/proc/sys/kernel/pid_max", "r");
5048
 
  g_assert_nonnull(sysctl_pid_max);
5049
 
  g_assert_cmpint(fscanf(sysctl_pid_max, "%" PRIuMAX, &pid_max),
5050
 
                  ==, 1);
5051
 
  g_assert_cmpint(fclose(sysctl_pid_max), ==, 0);
5052
 
 
5053
 
  pid_t nonexisting_pid = ((pid_t)pid_max)+1;
5054
 
  g_assert_true(nonexisting_pid > 0); /* Overflow check */
5055
 
 
5056
 
  __attribute__((cleanup(cleanup_string)))
5057
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
5058
 
  g_assert_nonnull(tempfilename);
5059
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5060
 
  g_assert_cmpint(questionfile, >, 0);
5061
 
  FILE *qf = fdopen(questionfile, "w");
5062
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5063
 
                          PRIuMAX"\n", (uintmax_t)nonexisting_pid),
5064
 
                  >, 0);
5065
 
  g_assert_cmpint(fclose(qf), ==, 0);
5066
 
 
5067
 
  char *const question_filename = strdup(tempfilename);
5068
 
  g_assert_nonnull(question_filename);
5069
 
  task_context task = {
5070
 
    .func=open_and_parse_question,
5071
 
    .question_filename=question_filename,
5072
 
    .epoll_fd=epoll_fd,
5073
 
    .password=&password,
5074
 
    .filename=question_filename,
5075
 
    .cancelled_filenames=&cancelled_filenames,
5076
 
    .current_time=&current_time,
5077
 
    .mandos_client_exited=&mandos_client_exited,
5078
 
    .password_is_read=&password_is_read,
5079
 
  };
5080
 
  run_task_with_stderr_to_dev_null(task, queue);
5081
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5082
 
 
5083
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
5084
 
}
5085
 
 
5086
 
static void
5087
 
test_open_and_parse_question_no_notafter(__attribute__((unused))
5088
 
                                         test_fixture *fixture,
5089
 
                                         __attribute__((unused))
5090
 
                                         gconstpointer user_data){
5091
 
  __attribute__((cleanup(cleanup_close)))
5092
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5093
 
  g_assert_cmpint(epoll_fd, >=, 0);
5094
 
  __attribute__((cleanup(string_set_clear)))
5095
 
    string_set cancelled_filenames = {};
5096
 
  buffer password = {};
5097
 
  bool mandos_client_exited = false;
5098
 
  bool password_is_read = false;
5099
 
  __attribute__((cleanup(cleanup_queue)))
5100
 
    task_queue *queue = create_queue();
5101
 
  g_assert_nonnull(queue);
5102
 
  const mono_microsecs current_time = 0;
5103
 
 
5104
 
  __attribute__((cleanup(cleanup_string)))
5105
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
5106
 
  g_assert_nonnull(tempfilename);
5107
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5108
 
  g_assert_cmpint(questionfile, >, 0);
5109
 
  FILE *qf = fdopen(questionfile, "w");
5110
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5111
 
                          PRIuMAX "\n", (uintmax_t)getpid()), >, 0);
5112
 
  g_assert_cmpint(fclose(qf), ==, 0);
5113
 
 
5114
 
  char *const filename = strdup(tempfilename);
5115
 
  g_assert_nonnull(filename);
5116
 
  task_context task = {
5117
 
    .func=open_and_parse_question,
5118
 
    .question_filename=filename,
5119
 
    .epoll_fd=epoll_fd,
5120
 
    .password=&password,
5121
 
    .filename=filename,
5122
 
    .cancelled_filenames=&cancelled_filenames,
5123
 
    .current_time=&current_time,
5124
 
    .mandos_client_exited=&mandos_client_exited,
5125
 
    .password_is_read=&password_is_read,
5126
 
  };
5127
 
  task.func(task, queue);
5128
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5129
 
 
5130
 
  __attribute__((cleanup(cleanup_string)))
5131
 
    char *socket_filename = strdup("/nonexistent");
5132
 
  g_assert_nonnull(socket_filename);
5133
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
5134
 
        .func=connect_question_socket,
5135
 
        .question_filename=tempfilename,
5136
 
        .filename=socket_filename,
5137
 
        .epoll_fd=epoll_fd,
5138
 
        .password=&password,
5139
 
        .current_time=&current_time,
5140
 
        .mandos_client_exited=&mandos_client_exited,
5141
 
        .password_is_read=&password_is_read,
5142
 
      }));
5143
 
 
5144
 
  g_assert_true(queue->next_run != 0);
5145
 
 
5146
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
5147
 
}
5148
 
 
5149
 
static void
5150
 
test_open_and_parse_question_bad_notafter(__attribute__((unused))
5151
 
                                          test_fixture *fixture,
5152
 
                                          __attribute__((unused))
5153
 
                                          gconstpointer user_data){
5154
 
  __attribute__((cleanup(cleanup_close)))
5155
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5156
 
  g_assert_cmpint(epoll_fd, >=, 0);
5157
 
  __attribute__((cleanup(string_set_clear)))
5158
 
    string_set cancelled_filenames = {};
5159
 
  buffer password = {};
5160
 
  bool mandos_client_exited = false;
5161
 
  bool password_is_read = false;
5162
 
  __attribute__((cleanup(cleanup_queue)))
5163
 
    task_queue *queue = create_queue();
5164
 
  g_assert_nonnull(queue);
5165
 
  const mono_microsecs current_time = 0;
5166
 
 
5167
 
  __attribute__((cleanup(cleanup_string)))
5168
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
5169
 
  g_assert_nonnull(tempfilename);
5170
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5171
 
  g_assert_cmpint(questionfile, >, 0);
5172
 
  FILE *qf = fdopen(questionfile, "w");
5173
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5174
 
                          PRIuMAX "\nNotAfter=\n",
5175
 
                          (uintmax_t)getpid()), >, 0);
5176
 
  g_assert_cmpint(fclose(qf), ==, 0);
5177
 
 
5178
 
  char *const filename = strdup(tempfilename);
5179
 
  g_assert_nonnull(filename);
5180
 
  task_context task = {
5181
 
    .func=open_and_parse_question,
5182
 
    .question_filename=filename,
5183
 
    .epoll_fd=epoll_fd,
5184
 
    .password=&password,
5185
 
    .filename=filename,
5186
 
    .cancelled_filenames=&cancelled_filenames,
5187
 
    .current_time=&current_time,
5188
 
    .mandos_client_exited=&mandos_client_exited,
5189
 
    .password_is_read=&password_is_read,
5190
 
  };
5191
 
  run_task_with_stderr_to_dev_null(task, queue);
5192
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5193
 
 
5194
 
  __attribute__((cleanup(cleanup_string)))
5195
 
    char *socket_filename = strdup("/nonexistent");
5196
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
5197
 
        .func=connect_question_socket,
5198
 
        .question_filename=tempfilename,
5199
 
        .filename=socket_filename,
5200
 
        .epoll_fd=epoll_fd,
5201
 
        .password=&password,
5202
 
        .current_time=&current_time,
5203
 
        .mandos_client_exited=&mandos_client_exited,
5204
 
        .password_is_read=&password_is_read,
5205
 
      }));
5206
 
  g_assert_true(queue->next_run != 0);
5207
 
 
5208
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
5209
 
}
5210
 
 
5211
 
static
5212
 
void assert_open_and_parse_question_with_notafter(const mono_microsecs
5213
 
                                                  current_time,
5214
 
                                                  const mono_microsecs
5215
 
                                                  notafter,
5216
 
                                                  const mono_microsecs
5217
 
                                                  next_queue_run){
5218
 
  __attribute__((cleanup(cleanup_close)))
5219
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5220
 
  g_assert_cmpint(epoll_fd, >=, 0);
5221
 
  __attribute__((cleanup(string_set_clear)))
5222
 
    string_set cancelled_filenames = {};
5223
 
  buffer password = {};
5224
 
  bool mandos_client_exited = false;
5225
 
  bool password_is_read = false;
5226
 
  __attribute__((cleanup(cleanup_queue)))
5227
 
    task_queue *queue = create_queue();
5228
 
  g_assert_nonnull(queue);
5229
 
  queue->next_run = next_queue_run;
5230
 
 
5231
 
  __attribute__((cleanup(cleanup_string)))
5232
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
5233
 
  g_assert_nonnull(tempfilename);
5234
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
5235
 
  g_assert_cmpint(questionfile, >, 0);
5236
 
  FILE *qf = fdopen(questionfile, "w");
5237
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
5238
 
                          PRIuMAX "\nNotAfter=%" PRIuMAX "\n",
5239
 
                          (uintmax_t)getpid(), notafter), >, 0);
5240
 
  g_assert_cmpint(fclose(qf), ==, 0);
5241
 
 
5242
 
  char *const filename = strdup(tempfilename);
5243
 
  g_assert_nonnull(filename);
5244
 
  task_context task = {
5245
 
    .func=open_and_parse_question,
5246
 
    .question_filename=filename,
5247
 
    .epoll_fd=epoll_fd,
5248
 
    .password=&password,
5249
 
    .filename=filename,
5250
 
    .cancelled_filenames=&cancelled_filenames,
5251
 
    .current_time=&current_time,
5252
 
    .mandos_client_exited=&mandos_client_exited,
5253
 
    .password_is_read=&password_is_read,
5254
 
  };
5255
 
  task.func(task, queue);
5256
 
 
5257
 
  if(queue->length >= 1){
5258
 
    __attribute__((cleanup(cleanup_string)))
5259
 
      char *socket_filename = strdup("/nonexistent");
5260
 
    g_assert_nonnull(find_matching_task(queue, (task_context){
5261
 
          .func=connect_question_socket,
5262
 
          .filename=socket_filename,
5263
 
          .epoll_fd=epoll_fd,
5264
 
          .password=&password,
5265
 
          .current_time=&current_time,
5266
 
          .cancelled_filenames=&cancelled_filenames,
5267
 
          .mandos_client_exited=&mandos_client_exited,
5268
 
          .password_is_read=&password_is_read,
5269
 
        }));
5270
 
    g_assert_true(queue->next_run != 0);
5271
 
  }
5272
 
 
5273
 
  if(notafter == 0){
5274
 
    g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5275
 
  } else if(current_time >= notafter) {
5276
 
    g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5277
 
  } else {
5278
 
    g_assert_nonnull(find_matching_task(queue, (task_context){
5279
 
          .func=cancel_old_question,
5280
 
          .question_filename=tempfilename,
5281
 
          .filename=tempfilename,
5282
 
          .notafter=notafter,
5283
 
          .cancelled_filenames=&cancelled_filenames,
5284
 
          .current_time=&current_time,
5285
 
        }));
5286
 
  }
5287
 
  g_assert_true(queue->next_run == 1);
5288
 
 
5289
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
5290
 
}
5291
 
 
5292
 
static void
5293
 
test_open_and_parse_question_notafter_0(__attribute__((unused))
5294
 
                                        test_fixture *fixture,
5295
 
                                        __attribute__((unused))
5296
 
                                        gconstpointer user_data){
5297
 
  /* current_time, notafter, next_queue_run */
5298
 
  assert_open_and_parse_question_with_notafter(0, 0, 0);
5299
 
}
5300
 
 
5301
 
static void
5302
 
test_open_and_parse_question_notafter_1(__attribute__((unused))
5303
 
                                        test_fixture *fixture,
5304
 
                                        __attribute__((unused))
5305
 
                                        gconstpointer user_data){
5306
 
  /* current_time, notafter, next_queue_run */
5307
 
  assert_open_and_parse_question_with_notafter(0, 1, 0);
5308
 
}
5309
 
 
5310
 
static void
5311
 
test_open_and_parse_question_notafter_1_1(__attribute__((unused))
5312
 
                                          test_fixture *fixture,
5313
 
                                          __attribute__((unused))
5314
 
                                          gconstpointer user_data){
5315
 
  /* current_time, notafter, next_queue_run */
5316
 
  assert_open_and_parse_question_with_notafter(0, 1, 1);
5317
 
}
5318
 
 
5319
 
static void
5320
 
test_open_and_parse_question_notafter_1_2(__attribute__((unused))
5321
 
                                          test_fixture *fixture,
5322
 
                                          __attribute__((unused))
5323
 
                                          gconstpointer user_data){
5324
 
  /* current_time, notafter, next_queue_run */
5325
 
  assert_open_and_parse_question_with_notafter(0, 1, 2);
5326
 
}
5327
 
 
5328
 
static void
5329
 
test_open_and_parse_question_equal_notafter(__attribute__((unused))
5330
 
                                            test_fixture *fixture,
5331
 
                                            __attribute__((unused))
5332
 
                                            gconstpointer user_data){
5333
 
  /* current_time, notafter, next_queue_run */
5334
 
  assert_open_and_parse_question_with_notafter(1, 1, 0);
5335
 
}
5336
 
 
5337
 
static void
5338
 
test_open_and_parse_question_late_notafter(__attribute__((unused))
5339
 
                                           test_fixture *fixture,
5340
 
                                           __attribute__((unused))
5341
 
                                           gconstpointer user_data){
5342
 
  /* current_time, notafter, next_queue_run */
5343
 
  assert_open_and_parse_question_with_notafter(2, 1, 0);
5344
 
}
5345
 
 
5346
 
static void assert_cancel_old_question_param(const mono_microsecs
5347
 
                                             next_queue_run,
5348
 
                                             const mono_microsecs
5349
 
                                             notafter,
5350
 
                                             const mono_microsecs
5351
 
                                             current_time,
5352
 
                                             const mono_microsecs
5353
 
                                             next_set_to){
5354
 
  __attribute__((cleanup(string_set_clear)))
5355
 
    string_set cancelled_filenames = {};
5356
 
  __attribute__((cleanup(cleanup_queue)))
5357
 
    task_queue *queue = create_queue();
5358
 
  g_assert_nonnull(queue);
5359
 
  queue->next_run = next_queue_run;
5360
 
 
5361
 
  char *const question_filename = strdup("/nonexistent");
5362
 
  g_assert_nonnull(question_filename);
5363
 
  task_context task = {
5364
 
    .func=cancel_old_question,
5365
 
    .question_filename=question_filename,
5366
 
    .filename=question_filename,
5367
 
    .notafter=notafter,
5368
 
    .cancelled_filenames=&cancelled_filenames,
5369
 
    .current_time=&current_time,
5370
 
  };
5371
 
  task.func(task, queue);
5372
 
 
5373
 
  if(current_time >= notafter){
5374
 
    g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5375
 
    g_assert_true(string_set_contains(cancelled_filenames,
5376
 
                                      "/nonexistent"));
5377
 
  } else {
5378
 
    g_assert_nonnull(find_matching_task(queue, (task_context){
5379
 
          .func=cancel_old_question,
5380
 
          .question_filename=question_filename,
5381
 
          .filename=question_filename,
5382
 
          .notafter=notafter,
5383
 
          .cancelled_filenames=&cancelled_filenames,
5384
 
          .current_time=&current_time,
5385
 
        }));
5386
 
 
5387
 
    g_assert_false(string_set_contains(cancelled_filenames,
5388
 
                                       question_filename));
5389
 
  }
5390
 
  g_assert_cmpuint((unsigned int)queue->next_run, ==,
5391
 
                   (unsigned int)next_set_to);
5392
 
}
5393
 
 
5394
 
static void test_cancel_old_question_0_1_2(__attribute__((unused))
5395
 
                                           test_fixture *fixture,
5396
 
                                           __attribute__((unused))
5397
 
                                           gconstpointer user_data){
5398
 
  /* next_queue_run unset,
5399
 
     cancellation should happen because time has come,
5400
 
     next_queue_run should be unchanged */
5401
 
  /* next_queue_run, notafter, current_time, next_set_to */
5402
 
  assert_cancel_old_question_param(0, 1, 2, 0);
5403
 
}
5404
 
 
5405
 
static void test_cancel_old_question_0_2_1(__attribute__((unused))
5406
 
                                           test_fixture *fixture,
5407
 
                                           __attribute__((unused))
5408
 
                                           gconstpointer user_data){
5409
 
  /* If next_queue_run is 0, meaning unset, and notafter is 2,
5410
 
     and current_time is not yet notafter or greater,
5411
 
     update value of next_queue_run to value of notafter */
5412
 
  /* next_queue_run, notafter, current_time, next_set_to */
5413
 
  assert_cancel_old_question_param(0, 2, 1, 2);
5414
 
}
5415
 
 
5416
 
static void test_cancel_old_question_1_2_3(__attribute__((unused))
5417
 
                                           test_fixture *fixture,
5418
 
                                           __attribute__((unused))
5419
 
                                           gconstpointer user_data){
5420
 
  /* next_queue_run 1,
5421
 
     cancellation should happen because time has come,
5422
 
     next_queue_run should be unchanged */
5423
 
  /* next_queue_run, notafter, current_time, next_set_to */
5424
 
  assert_cancel_old_question_param(1, 2, 3, 1);
5425
 
}
5426
 
 
5427
 
static void test_cancel_old_question_1_3_2(__attribute__((unused))
5428
 
                                           test_fixture *fixture,
5429
 
                                           __attribute__((unused))
5430
 
                                           gconstpointer user_data){
5431
 
  /* If next_queue_run is set,
5432
 
     and current_time is not yet notafter or greater,
5433
 
     and notafter is larger than next_queue_run
5434
 
     next_queue_run should be unchanged */
5435
 
  /* next_queue_run, notafter, current_time, next_set_to */
5436
 
  assert_cancel_old_question_param(1, 3, 2, 1);
5437
 
}
5438
 
 
5439
 
static void test_cancel_old_question_2_1_3(__attribute__((unused))
5440
 
                                           test_fixture *fixture,
5441
 
                                           __attribute__((unused))
5442
 
                                           gconstpointer user_data){
5443
 
  /* next_queue_run 2,
5444
 
     cancellation should happen because time has come,
5445
 
     next_queue_run should be unchanged */
5446
 
  /* next_queue_run, notafter, current_time, next_set_to */
5447
 
  assert_cancel_old_question_param(2, 1, 3, 2);
5448
 
}
5449
 
 
5450
 
static void test_cancel_old_question_2_3_1(__attribute__((unused))
5451
 
                                           test_fixture *fixture,
5452
 
                                           __attribute__((unused))
5453
 
                                           gconstpointer user_data){
5454
 
  /* If next_queue_run is set,
5455
 
     and current_time is not yet notafter or greater,
5456
 
     and notafter is larger than next_queue_run
5457
 
     next_queue_run should be unchanged */
5458
 
  /* next_queue_run, notafter, current_time, next_set_to */
5459
 
  assert_cancel_old_question_param(2, 3, 1, 2);
5460
 
}
5461
 
 
5462
 
static void test_cancel_old_question_3_1_2(__attribute__((unused))
5463
 
                                           test_fixture *fixture,
5464
 
                                           __attribute__((unused))
5465
 
                                           gconstpointer user_data){
5466
 
  /* next_queue_run 3,
5467
 
     cancellation should happen because time has come,
5468
 
     next_queue_run should be unchanged */
5469
 
  /* next_queue_run, notafter, current_time, next_set_to */
5470
 
  assert_cancel_old_question_param(3, 1, 2, 3);
5471
 
}
5472
 
 
5473
 
static void test_cancel_old_question_3_2_1(__attribute__((unused))
5474
 
                                           test_fixture *fixture,
5475
 
                                           __attribute__((unused))
5476
 
                                           gconstpointer user_data){
5477
 
  /* If next_queue_run is set,
5478
 
     and current_time is not yet notafter or greater,
5479
 
     and notafter is smaller than next_queue_run
5480
 
     update value of next_queue_run to value of notafter */
5481
 
  /* next_queue_run, notafter, current_time, next_set_to */
5482
 
  assert_cancel_old_question_param(3, 2, 1, 2);
5483
 
}
5484
 
 
5485
 
static void
5486
 
test_connect_question_socket_name_too_long(__attribute__((unused))
5487
 
                                           test_fixture *fixture,
5488
 
                                           __attribute__((unused))
5489
 
                                           gconstpointer user_data){
5490
 
  __attribute__((cleanup(cleanup_close)))
5491
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5492
 
  g_assert_cmpint(epoll_fd, >=, 0);
5493
 
  const char question_filename[] = "/nonexistent/question";
5494
 
  __attribute__((cleanup(string_set_clear)))
5495
 
    string_set cancelled_filenames = {};
5496
 
  __attribute__((cleanup(cleanup_queue)))
5497
 
    task_queue *queue = create_queue();
5498
 
  g_assert_nonnull(queue);
5499
 
  __attribute__((cleanup(cleanup_string)))
5500
 
    char *tempdir = make_temporary_directory();
5501
 
  g_assert_nonnull(tempdir);
5502
 
  struct sockaddr_un unix_socket = { .sun_family=AF_LOCAL };
5503
 
  char socket_name[sizeof(unix_socket.sun_path)];
5504
 
  memset(socket_name, 'x', sizeof(socket_name));
5505
 
  socket_name[sizeof(socket_name)-1] = '\0';
5506
 
  char *filename = NULL;
5507
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5508
 
                  >, 0);
5509
 
  g_assert_nonnull(filename);
5510
 
 
5511
 
  task_context task = {
5512
 
    .func=connect_question_socket,
5513
 
    .question_filename=strdup(question_filename),
5514
 
    .epoll_fd=epoll_fd,
5515
 
    .password=(buffer[]){{}},
5516
 
    .filename=filename,
5517
 
    .cancelled_filenames=&cancelled_filenames,
5518
 
    .mandos_client_exited=(bool[]){false},
5519
 
    .password_is_read=(bool[]){false},
5520
 
    .current_time=(mono_microsecs[]){0},
5521
 
  };
5522
 
  g_assert_nonnull(task.question_filename);
5523
 
  run_task_with_stderr_to_dev_null(task, queue);
5524
 
 
5525
 
  g_assert_true(string_set_contains(cancelled_filenames,
5526
 
                                    question_filename));
5527
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5528
 
  g_assert_true(queue->next_run == 0);
5529
 
 
5530
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
5531
 
}
5532
 
 
5533
 
static
5534
 
void test_connect_question_socket_connect_fail(__attribute__((unused))
5535
 
                                               test_fixture *fixture,
5536
 
                                               __attribute__((unused))
5537
 
                                               gconstpointer
5538
 
                                               user_data){
5539
 
  __attribute__((cleanup(cleanup_close)))
5540
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5541
 
  g_assert_cmpint(epoll_fd, >=, 0);
5542
 
  const char question_filename[] = "/nonexistent/question";
5543
 
  __attribute__((cleanup(string_set_clear)))
5544
 
    string_set cancelled_filenames = {};
5545
 
  const mono_microsecs current_time = 3;
5546
 
  __attribute__((cleanup(cleanup_queue)))
5547
 
    task_queue *queue = create_queue();
5548
 
  g_assert_nonnull(queue);
5549
 
  __attribute__((cleanup(cleanup_string)))
5550
 
    char *tempdir = make_temporary_directory();
5551
 
  g_assert_nonnull(tempdir);
5552
 
  char socket_name[] = "nonexistent";
5553
 
  char *filename = NULL;
5554
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5555
 
                  >, 0);
5556
 
  g_assert_nonnull(filename);
5557
 
 
5558
 
  task_context task = {
5559
 
    .func=connect_question_socket,
5560
 
    .question_filename=strdup(question_filename),
5561
 
    .epoll_fd=epoll_fd,
5562
 
    .password=(buffer[]){{}},
5563
 
    .filename=filename,
5564
 
    .cancelled_filenames=&cancelled_filenames,
5565
 
    .mandos_client_exited=(bool[]){false},
5566
 
    .password_is_read=(bool[]){false},
5567
 
    .current_time=&current_time,
5568
 
  };
5569
 
  g_assert_nonnull(task.question_filename);
5570
 
  run_task_with_stderr_to_dev_null(task, queue);
5571
 
 
5572
 
  g_assert_nonnull(find_matching_task(queue, task));
5573
 
 
5574
 
  g_assert_false(string_set_contains(cancelled_filenames,
5575
 
                                     question_filename));
5576
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5577
 
  g_assert_true(queue->next_run == 1000000 + current_time);
5578
 
 
5579
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
5580
 
}
5581
 
 
5582
 
static
5583
 
void test_connect_question_socket_bad_epoll(__attribute__((unused))
5584
 
                                            test_fixture *fixture,
5585
 
                                            __attribute__((unused))
5586
 
                                            gconstpointer user_data){
5587
 
  __attribute__((cleanup(cleanup_close)))
5588
 
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
5589
 
  __attribute__((cleanup(cleanup_string)))
5590
 
    char *const question_filename = strdup("/nonexistent/question");
5591
 
  g_assert_nonnull(question_filename);
5592
 
  __attribute__((cleanup(string_set_clear)))
5593
 
    string_set cancelled_filenames = {};
5594
 
  const mono_microsecs current_time = 5;
5595
 
  __attribute__((cleanup(cleanup_queue)))
5596
 
    task_queue *queue = create_queue();
5597
 
  g_assert_nonnull(queue);
5598
 
  __attribute__((cleanup(cleanup_string)))
5599
 
    char *tempdir = make_temporary_directory();
5600
 
  g_assert_nonnull(tempdir);
5601
 
  __attribute__((cleanup(cleanup_close)))
5602
 
    const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5603
 
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5604
 
  g_assert_cmpint(sock_fd, >=, 0);
5605
 
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5606
 
  const char socket_name[] = "socket_name";
5607
 
  __attribute__((cleanup(cleanup_string)))
5608
 
    char *filename = NULL;
5609
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5610
 
                  >, 0);
5611
 
  g_assert_nonnull(filename);
5612
 
  g_assert_cmpint((int)strlen(filename), <,
5613
 
                  (int)sizeof(sock_name.sun_path));
5614
 
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5615
 
  sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5616
 
  g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5617
 
                            (socklen_t)SUN_LEN(&sock_name)), >=, 0);
5618
 
  task_context task = {
5619
 
    .func=connect_question_socket,
5620
 
    .question_filename=strdup(question_filename),
5621
 
    .epoll_fd=epoll_fd,
5622
 
    .password=(buffer[]){{}},
5623
 
    .filename=strdup(filename),
5624
 
    .cancelled_filenames=&cancelled_filenames,
5625
 
    .mandos_client_exited=(bool[]){false},
5626
 
    .password_is_read=(bool[]){false},
5627
 
    .current_time=&current_time,
5628
 
  };
5629
 
  g_assert_nonnull(task.question_filename);
5630
 
  run_task_with_stderr_to_dev_null(task, queue);
5631
 
 
5632
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5633
 
  const task_context *const added_task
5634
 
    = find_matching_task(queue, task);
5635
 
  g_assert_nonnull(added_task);
5636
 
  g_assert_true(queue->next_run == 1000000 + current_time);
5637
 
 
5638
 
  g_assert_cmpint(unlink(filename), ==, 0);
5639
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
5640
 
}
5641
 
 
5642
 
static
5643
 
void test_connect_question_socket_usable(__attribute__((unused))
5644
 
                                         test_fixture *fixture,
5645
 
                                         __attribute__((unused))
5646
 
                                         gconstpointer user_data){
5647
 
  __attribute__((cleanup(cleanup_close)))
5648
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5649
 
  g_assert_cmpint(epoll_fd, >=, 0);
5650
 
  __attribute__((cleanup(cleanup_string)))
5651
 
    char *const question_filename = strdup("/nonexistent/question");
5652
 
  g_assert_nonnull(question_filename);
5653
 
  __attribute__((cleanup(string_set_clear)))
5654
 
    string_set cancelled_filenames = {};
5655
 
  buffer password = {};
5656
 
  bool mandos_client_exited = false;
5657
 
  bool password_is_read = false;
5658
 
  const mono_microsecs current_time = 0;
5659
 
  __attribute__((cleanup(cleanup_queue)))
5660
 
    task_queue *queue = create_queue();
5661
 
  g_assert_nonnull(queue);
5662
 
  __attribute__((cleanup(cleanup_string)))
5663
 
    char *tempdir = make_temporary_directory();
5664
 
  g_assert_nonnull(tempdir);
5665
 
  __attribute__((cleanup(cleanup_close)))
5666
 
    const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
5667
 
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
5668
 
  g_assert_cmpint(sock_fd, >=, 0);
5669
 
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
5670
 
  const char socket_name[] = "socket_name";
5671
 
  __attribute__((cleanup(cleanup_string)))
5672
 
    char *filename = NULL;
5673
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
5674
 
                  >, 0);
5675
 
  g_assert_nonnull(filename);
5676
 
  g_assert_cmpint((int)strlen(filename), <,
5677
 
                  (int)sizeof(sock_name.sun_path));
5678
 
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
5679
 
  sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
5680
 
  g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
5681
 
                            (socklen_t)SUN_LEN(&sock_name)), >=, 0);
5682
 
  task_context task = {
5683
 
    .func=connect_question_socket,
5684
 
    .question_filename=strdup(question_filename),
5685
 
    .epoll_fd=epoll_fd,
5686
 
    .password=&password,
5687
 
    .filename=strdup(filename),
5688
 
    .cancelled_filenames=&cancelled_filenames,
5689
 
    .mandos_client_exited=&mandos_client_exited,
5690
 
    .password_is_read=&password_is_read,
5691
 
    .current_time=&current_time,
5692
 
  };
5693
 
  g_assert_nonnull(task.question_filename);
5694
 
  task.func(task, queue);
5695
 
 
5696
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5697
 
  const task_context *const added_task
5698
 
    = find_matching_task(queue, (task_context){
5699
 
        .func=send_password_to_socket,
5700
 
        .question_filename=question_filename,
5701
 
        .filename=filename,
5702
 
        .epoll_fd=epoll_fd,
5703
 
        .password=&password,
5704
 
        .cancelled_filenames=&cancelled_filenames,
5705
 
        .mandos_client_exited=&mandos_client_exited,
5706
 
        .password_is_read=&password_is_read,
5707
 
        .current_time=&current_time,
5708
 
      });
5709
 
  g_assert_nonnull(added_task);
5710
 
  g_assert_cmpint(added_task->fd, >, 0);
5711
 
 
5712
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5713
 
                                   EPOLLOUT));
5714
 
 
5715
 
  const int fd = added_task->fd;
5716
 
  g_assert_cmpint(fd, >, 0);
5717
 
  g_assert_true(fd_has_cloexec_and_nonblock(fd));
5718
 
 
5719
 
  /* write to fd */
5720
 
  char write_data[PIPE_BUF];
5721
 
  {
5722
 
    /* Construct test password buffer */
5723
 
    /* Start with + since that is what the real procotol uses */
5724
 
    write_data[0] = '+';
5725
 
    /* Set a special character at string end just to mark the end */
5726
 
    write_data[sizeof(write_data)-2] = 'y';
5727
 
    /* Set NUL at buffer end, as suggested by the protocol */
5728
 
    write_data[sizeof(write_data)-1] = '\0';
5729
 
    /* Fill rest of password with 'x' */
5730
 
    memset(write_data+1, 'x', sizeof(write_data)-3);
5731
 
    g_assert_cmpint((int)send(fd, write_data, sizeof(write_data),
5732
 
                              MSG_NOSIGNAL), ==, sizeof(write_data));
5733
 
  }
5734
 
 
5735
 
  /* read from sock_fd */
5736
 
  char read_data[sizeof(write_data)];
5737
 
  g_assert_cmpint((int)read(sock_fd, read_data, sizeof(read_data)),
5738
 
                  ==, sizeof(read_data));
5739
 
 
5740
 
  g_assert_true(memcmp(write_data, read_data, sizeof(write_data))
5741
 
                == 0);
5742
 
 
5743
 
  /* writing to sock_fd should fail */
5744
 
  g_assert_cmpint(send(sock_fd, write_data, sizeof(write_data),
5745
 
                       MSG_NOSIGNAL), <, 0);
5746
 
 
5747
 
  /* reading from fd should fail */
5748
 
  g_assert_cmpint((int)recv(fd, read_data, sizeof(read_data),
5749
 
                            MSG_NOSIGNAL), <, 0);
5750
 
 
5751
 
  g_assert_cmpint(unlink(filename), ==, 0);
5752
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
5753
 
}
5754
 
 
5755
 
static void
5756
 
test_send_password_to_socket_client_not_exited(__attribute__((unused))
5757
 
                                               test_fixture *fixture,
5758
 
                                               __attribute__((unused))
5759
 
                                               gconstpointer
5760
 
                                               user_data){
5761
 
  __attribute__((cleanup(cleanup_close)))
5762
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5763
 
  g_assert_cmpint(epoll_fd, >=, 0);
5764
 
  __attribute__((cleanup(cleanup_string)))
5765
 
    char *const question_filename = strdup("/nonexistent/question");
5766
 
  g_assert_nonnull(question_filename);
5767
 
  __attribute__((cleanup(cleanup_string)))
5768
 
    char *const filename = strdup("/nonexistent/socket");
5769
 
  g_assert_nonnull(filename);
5770
 
  __attribute__((cleanup(string_set_clear)))
5771
 
    string_set cancelled_filenames = {};
5772
 
  buffer password = {};
5773
 
  bool password_is_read = true;
5774
 
  __attribute__((cleanup(cleanup_queue)))
5775
 
    task_queue *queue = create_queue();
5776
 
  g_assert_nonnull(queue);
5777
 
  int socketfds[2];
5778
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5779
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5780
 
                             socketfds), ==, 0);
5781
 
  __attribute__((cleanup(cleanup_close)))
5782
 
    const int read_socket = socketfds[0];
5783
 
  const int write_socket = socketfds[1];
5784
 
  task_context task = {
5785
 
    .func=send_password_to_socket,
5786
 
    .question_filename=strdup(question_filename),
5787
 
    .filename=strdup(filename),
5788
 
    .epoll_fd=epoll_fd,
5789
 
    .fd=write_socket,
5790
 
    .password=&password,
5791
 
    .cancelled_filenames=&cancelled_filenames,
5792
 
    .mandos_client_exited=(bool[]){false},
5793
 
    .password_is_read=&password_is_read,
5794
 
    .current_time=(mono_microsecs[]){0},
5795
 
  };
5796
 
  g_assert_nonnull(task.question_filename);
5797
 
 
5798
 
  task.func(task, queue);
5799
 
 
5800
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5801
 
 
5802
 
  const task_context *const added_task
5803
 
    = find_matching_task(queue, task);
5804
 
  g_assert_nonnull(added_task);
5805
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
5806
 
  g_assert_true(password_is_read);
5807
 
 
5808
 
  g_assert_cmpint(added_task->fd, >, 0);
5809
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5810
 
                                   EPOLLOUT));
5811
 
}
5812
 
 
5813
 
static void
5814
 
test_send_password_to_socket_password_not_read(__attribute__((unused))
5815
 
                                               test_fixture *fixture,
5816
 
                                               __attribute__((unused))
5817
 
                                               gconstpointer
5818
 
                                               user_data){
5819
 
  __attribute__((cleanup(cleanup_close)))
5820
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5821
 
  g_assert_cmpint(epoll_fd, >=, 0);
5822
 
  __attribute__((cleanup(cleanup_string)))
5823
 
    char *const question_filename = strdup("/nonexistent/question");
5824
 
  g_assert_nonnull(question_filename);
5825
 
  __attribute__((cleanup(cleanup_string)))
5826
 
    char *const filename = strdup("/nonexistent/socket");
5827
 
  __attribute__((cleanup(string_set_clear)))
5828
 
    string_set cancelled_filenames = {};
5829
 
  buffer password = {};
5830
 
  __attribute__((cleanup(cleanup_queue)))
5831
 
    task_queue *queue = create_queue();
5832
 
  g_assert_nonnull(queue);
5833
 
  int socketfds[2];
5834
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5835
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5836
 
                             socketfds), ==, 0);
5837
 
  __attribute__((cleanup(cleanup_close)))
5838
 
    const int read_socket = socketfds[0];
5839
 
  const int write_socket = socketfds[1];
5840
 
  task_context task = {
5841
 
    .func=send_password_to_socket,
5842
 
    .question_filename=strdup(question_filename),
5843
 
    .filename=strdup(filename),
5844
 
    .epoll_fd=epoll_fd,
5845
 
    .fd=write_socket,
5846
 
    .password=&password,
5847
 
    .cancelled_filenames=&cancelled_filenames,
5848
 
    .mandos_client_exited=(bool[]){false},
5849
 
    .password_is_read=(bool[]){false},
5850
 
    .current_time=(mono_microsecs[]){0},
5851
 
  };
5852
 
  g_assert_nonnull(task.question_filename);
5853
 
 
5854
 
  task.func(task, queue);
5855
 
 
5856
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5857
 
 
5858
 
  const task_context *const added_task = find_matching_task(queue,
5859
 
                                                            task);
5860
 
  g_assert_nonnull(added_task);
5861
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
5862
 
  g_assert_true(queue->next_run == 0);
5863
 
 
5864
 
  g_assert_cmpint(added_task->fd, >, 0);
5865
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5866
 
                                   EPOLLOUT));
5867
 
}
5868
 
 
5869
 
static
5870
 
void test_send_password_to_socket_EMSGSIZE(__attribute__((unused))
5871
 
                                           test_fixture *fixture,
5872
 
                                           __attribute__((unused))
5873
 
                                           gconstpointer user_data){
5874
 
  __attribute__((cleanup(cleanup_close)))
5875
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5876
 
  g_assert_cmpint(epoll_fd, >=, 0);
5877
 
  const char question_filename[] = "/nonexistent/question";
5878
 
  char *const filename = strdup("/nonexistent/socket");
5879
 
  __attribute__((cleanup(string_set_clear)))
5880
 
    string_set cancelled_filenames = {};
5881
 
  const size_t oversized = 1024*1024; /* Limit seems to be 212960 */
5882
 
  __attribute__((cleanup(cleanup_buffer)))
5883
 
    buffer password = {
5884
 
    .data=malloc(oversized),
5885
 
    .length=oversized,
5886
 
    .allocated=oversized,
5887
 
  };
5888
 
  g_assert_nonnull(password.data);
5889
 
  if(mlock(password.data, password.allocated) != 0){
5890
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
5891
 
  }
5892
 
  /* Construct test password buffer */
5893
 
  /* Start with + since that is what the real procotol uses */
5894
 
  password.data[0] = '+';
5895
 
  /* Set a special character at string end just to mark the end */
5896
 
  password.data[oversized-3] = 'y';
5897
 
  /* Set NUL at buffer end, as suggested by the protocol */
5898
 
  password.data[oversized-2] = '\0';
5899
 
  /* Fill rest of password with 'x' */
5900
 
  memset(password.data+1, 'x', oversized-3);
5901
 
 
5902
 
  __attribute__((cleanup(cleanup_queue)))
5903
 
    task_queue *queue = create_queue();
5904
 
  g_assert_nonnull(queue);
5905
 
  int socketfds[2];
5906
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5907
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5908
 
                             socketfds), ==, 0);
5909
 
  __attribute__((cleanup(cleanup_close)))
5910
 
    const int read_socket = socketfds[0];
5911
 
  __attribute__((cleanup(cleanup_close)))
5912
 
    const int write_socket = socketfds[1];
5913
 
  task_context task = {
5914
 
    .func=send_password_to_socket,
5915
 
    .question_filename=strdup(question_filename),
5916
 
    .filename=filename,
5917
 
    .epoll_fd=epoll_fd,
5918
 
    .fd=write_socket,
5919
 
    .password=&password,
5920
 
    .cancelled_filenames=&cancelled_filenames,
5921
 
    .mandos_client_exited=(bool[]){true},
5922
 
    .password_is_read=(bool[]){true},
5923
 
    .current_time=(mono_microsecs[]){0},
5924
 
  };
5925
 
  g_assert_nonnull(task.question_filename);
5926
 
 
5927
 
  run_task_with_stderr_to_dev_null(task, queue);
5928
 
 
5929
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
5930
 
  g_assert_true(string_set_contains(cancelled_filenames,
5931
 
                                    question_filename));
5932
 
}
5933
 
 
5934
 
static void test_send_password_to_socket_retry(__attribute__((unused))
5935
 
                                               test_fixture *fixture,
5936
 
                                               __attribute__((unused))
5937
 
                                               gconstpointer
5938
 
                                               user_data){
5939
 
  __attribute__((cleanup(cleanup_close)))
5940
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5941
 
  g_assert_cmpint(epoll_fd, >=, 0);
5942
 
  __attribute__((cleanup(cleanup_string)))
5943
 
    char *const question_filename = strdup("/nonexistent/question");
5944
 
  g_assert_nonnull(question_filename);
5945
 
  __attribute__((cleanup(cleanup_string)))
5946
 
    char *const filename = strdup("/nonexistent/socket");
5947
 
  g_assert_nonnull(filename);
5948
 
  __attribute__((cleanup(string_set_clear)))
5949
 
    string_set cancelled_filenames = {};
5950
 
  __attribute__((cleanup(cleanup_buffer)))
5951
 
    buffer password = {};
5952
 
 
5953
 
  __attribute__((cleanup(cleanup_queue)))
5954
 
    task_queue *queue = create_queue();
5955
 
  g_assert_nonnull(queue);
5956
 
  int socketfds[2];
5957
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5958
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5959
 
                             socketfds), ==, 0);
5960
 
  __attribute__((cleanup(cleanup_close)))
5961
 
    const int read_socket = socketfds[0];
5962
 
  const int write_socket = socketfds[1];
5963
 
  /* Close the server side socket to force ECONNRESET on client */
5964
 
  g_assert_cmpint(close(read_socket), ==, 0);
5965
 
  task_context task = {
5966
 
    .func=send_password_to_socket,
5967
 
    .question_filename=strdup(question_filename),
5968
 
    .filename=strdup(filename),
5969
 
    .epoll_fd=epoll_fd,
5970
 
    .fd=write_socket,
5971
 
    .password=&password,
5972
 
    .cancelled_filenames=&cancelled_filenames,
5973
 
    .mandos_client_exited=(bool[]){true},
5974
 
    .password_is_read=(bool[]){true},
5975
 
    .current_time=(mono_microsecs[]){0},
5976
 
  };
5977
 
  g_assert_nonnull(task.question_filename);
5978
 
 
5979
 
  task.func(task, queue);
5980
 
 
5981
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
5982
 
 
5983
 
  const task_context *const added_task = find_matching_task(queue,
5984
 
                                                            task);
5985
 
  g_assert_nonnull(added_task);
5986
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
5987
 
 
5988
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
5989
 
                                   EPOLLOUT));
5990
 
}
5991
 
 
5992
 
static
5993
 
void test_send_password_to_socket_bad_epoll(__attribute__((unused))
5994
 
                                            test_fixture *fixture,
5995
 
                                            __attribute__((unused))
5996
 
                                            gconstpointer user_data){
5997
 
  __attribute__((cleanup(cleanup_close)))
5998
 
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
5999
 
  __attribute__((cleanup(cleanup_string)))
6000
 
    char *const question_filename = strdup("/nonexistent/question");
6001
 
  g_assert_nonnull(question_filename);
6002
 
  __attribute__((cleanup(cleanup_string)))
6003
 
    char *const filename = strdup("/nonexistent/socket");
6004
 
  g_assert_nonnull(filename);
6005
 
  __attribute__((cleanup(string_set_clear)))
6006
 
    string_set cancelled_filenames = {};
6007
 
  __attribute__((cleanup(cleanup_buffer)))
6008
 
    buffer password = {};
6009
 
 
6010
 
  const mono_microsecs current_time = 11;
6011
 
  __attribute__((cleanup(cleanup_queue)))
6012
 
    task_queue *queue = create_queue();
6013
 
  g_assert_nonnull(queue);
6014
 
  int socketfds[2];
6015
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6016
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6017
 
                             socketfds), ==, 0);
6018
 
  __attribute__((cleanup(cleanup_close)))
6019
 
    const int read_socket = socketfds[0];
6020
 
  const int write_socket = socketfds[1];
6021
 
  /* Close the server side socket to force ECONNRESET on client */
6022
 
  g_assert_cmpint(close(read_socket), ==, 0);
6023
 
  task_context task = {
6024
 
    .func=send_password_to_socket,
6025
 
    .question_filename=strdup(question_filename),
6026
 
    .filename=strdup(filename),
6027
 
    .epoll_fd=epoll_fd,
6028
 
    .fd=write_socket,
6029
 
    .password=&password,
6030
 
    .cancelled_filenames=&cancelled_filenames,
6031
 
    .mandos_client_exited=(bool[]){true},
6032
 
    .password_is_read=(bool[]){true},
6033
 
    .current_time=&current_time,
6034
 
  };
6035
 
  g_assert_nonnull(task.question_filename);
6036
 
 
6037
 
  run_task_with_stderr_to_dev_null(task, queue);
6038
 
 
6039
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6040
 
 
6041
 
  const task_context *const added_task = find_matching_task(queue,
6042
 
                                                            task);
6043
 
  g_assert_nonnull(added_task);
6044
 
  g_assert_true(queue->next_run == current_time + 1000000);
6045
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
6046
 
}
6047
 
 
6048
 
static void assert_send_password_to_socket_password(buffer password){
6049
 
  __attribute__((cleanup(cleanup_close)))
6050
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6051
 
  g_assert_cmpint(epoll_fd, >=, 0);
6052
 
  char *const question_filename = strdup("/nonexistent/question");
6053
 
  g_assert_nonnull(question_filename);
6054
 
  char *const filename = strdup("/nonexistent/socket");
6055
 
  g_assert_nonnull(filename);
6056
 
  __attribute__((cleanup(string_set_clear)))
6057
 
    string_set cancelled_filenames = {};
6058
 
 
6059
 
  __attribute__((cleanup(cleanup_queue)))
6060
 
    task_queue *queue = create_queue();
6061
 
  g_assert_nonnull(queue);
6062
 
  int socketfds[2];
6063
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6064
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6065
 
                             socketfds), ==, 0);
6066
 
  __attribute__((cleanup(cleanup_close)))
6067
 
    const int read_socket = socketfds[0];
6068
 
  const int write_socket = socketfds[1];
6069
 
  task_context task = {
6070
 
    .func=send_password_to_socket,
6071
 
    .question_filename=question_filename,
6072
 
    .filename=filename,
6073
 
    .epoll_fd=epoll_fd,
6074
 
    .fd=write_socket,
6075
 
    .password=&password,
6076
 
    .cancelled_filenames=&cancelled_filenames,
6077
 
    .mandos_client_exited=(bool[]){true},
6078
 
    .password_is_read=(bool[]){true},
6079
 
    .current_time=(mono_microsecs[]){0},
6080
 
  };
6081
 
 
6082
 
  char *expected_written_data = malloc(password.length + 2);
6083
 
  g_assert_nonnull(expected_written_data);
6084
 
  expected_written_data[0] = '+';
6085
 
  expected_written_data[password.length + 1] = '\0';
6086
 
  if(password.length > 0){
6087
 
    g_assert_nonnull(password.data);
6088
 
    memcpy(expected_written_data + 1, password.data, password.length);
6089
 
  }
6090
 
 
6091
 
  task.func(task, queue);
6092
 
 
6093
 
  char buf[PIPE_BUF];
6094
 
  g_assert_cmpint((int)read(read_socket, buf, PIPE_BUF), ==,
6095
 
                  (int)(password.length + 2));
6096
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6097
 
 
6098
 
  g_assert_true(memcmp(expected_written_data, buf,
6099
 
                       password.length + 2) == 0);
6100
 
 
6101
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, write_socket));
6102
 
 
6103
 
  free(expected_written_data);
6104
 
}
6105
 
 
6106
 
static void
6107
 
test_send_password_to_socket_null_password(__attribute__((unused))
6108
 
                                           test_fixture *fixture,
6109
 
                                           __attribute__((unused))
6110
 
                                           gconstpointer user_data){
6111
 
  __attribute__((cleanup(cleanup_buffer)))
6112
 
    buffer password = {};
6113
 
  assert_send_password_to_socket_password(password);
6114
 
}
6115
 
 
6116
 
static void
6117
 
test_send_password_to_socket_empty_password(__attribute__((unused))
6118
 
                                            test_fixture *fixture,
6119
 
                                            __attribute__((unused))
6120
 
                                            gconstpointer user_data){
6121
 
  __attribute__((cleanup(cleanup_buffer)))
6122
 
    buffer password = {
6123
 
    .data=malloc(1),           /* because malloc(0) may return NULL */
6124
 
    .length=0,
6125
 
    .allocated=0,               /* deliberate lie */
6126
 
  };
6127
 
  g_assert_nonnull(password.data);
6128
 
  assert_send_password_to_socket_password(password);
6129
 
}
6130
 
 
6131
 
static void
6132
 
test_send_password_to_socket_empty_str_pass(__attribute__((unused))
6133
 
                                            test_fixture *fixture,
6134
 
                                            __attribute__((unused))
6135
 
                                            gconstpointer user_data){
6136
 
  __attribute__((cleanup(cleanup_buffer)))
6137
 
    buffer password = {
6138
 
    .data=strdup(""),
6139
 
    .length=0,
6140
 
    .allocated=1,
6141
 
  };
6142
 
  if(mlock(password.data, password.allocated) != 0){
6143
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
6144
 
  }
6145
 
  assert_send_password_to_socket_password(password);
6146
 
}
6147
 
 
6148
 
static void
6149
 
test_send_password_to_socket_text_password(__attribute__((unused))
6150
 
                                           test_fixture *fixture,
6151
 
                                           __attribute__((unused))
6152
 
                                           gconstpointer user_data){
6153
 
  const char dummy_test_password[] = "dummy test password";
6154
 
  __attribute__((cleanup(cleanup_buffer)))
6155
 
    buffer password = {
6156
 
    .data = strdup(dummy_test_password),
6157
 
    .length = strlen(dummy_test_password),
6158
 
    .allocated = sizeof(dummy_test_password),
6159
 
  };
6160
 
  if(mlock(password.data, password.allocated) != 0){
6161
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
6162
 
  }
6163
 
  assert_send_password_to_socket_password(password);
6164
 
}
6165
 
 
6166
 
static void
6167
 
test_send_password_to_socket_binary_password(__attribute__((unused))
6168
 
                                             test_fixture *fixture,
6169
 
                                             __attribute__((unused))
6170
 
                                             gconstpointer user_data){
6171
 
  __attribute__((cleanup(cleanup_buffer)))
6172
 
    buffer password = {
6173
 
    .data=malloc(255),
6174
 
    .length=255,
6175
 
    .allocated=255,
6176
 
  };
6177
 
  g_assert_nonnull(password.data);
6178
 
  if(mlock(password.data, password.allocated) != 0){
6179
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
6180
 
  }
6181
 
  char c = 1;                   /* Start at 1, avoiding NUL */
6182
 
  for(int i=0; i < 255; i++){
6183
 
    password.data[i] = c++;
6184
 
  }
6185
 
  assert_send_password_to_socket_password(password);
6186
 
}
6187
 
 
6188
 
static void
6189
 
test_send_password_to_socket_nuls_in_password(__attribute__((unused))
6190
 
                                              test_fixture *fixture,
6191
 
                                              __attribute__((unused))
6192
 
                                              gconstpointer
6193
 
                                              user_data){
6194
 
  char test_password[] = {'\0', 'a', '\0', 'b', '\0', 'c', '\0'};
6195
 
  __attribute__((cleanup(cleanup_buffer)))
6196
 
    buffer password = {
6197
 
    .data=malloc(sizeof(test_password)),
6198
 
    .length=sizeof(test_password),
6199
 
    .allocated=sizeof(test_password),
6200
 
  };
6201
 
  g_assert_nonnull(password.data);
6202
 
  if(mlock(password.data, password.allocated) !=0){
6203
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
6204
 
  }
6205
 
  memcpy(password.data, test_password, password.allocated);
6206
 
  assert_send_password_to_socket_password(password);
6207
 
}
6208
 
 
6209
 
static bool assert_add_existing_questions_to_devnull(task_queue
6210
 
                                                     *const,
6211
 
                                                     const int,
6212
 
                                                     buffer *const,
6213
 
                                                     string_set *,
6214
 
                                                     const
6215
 
                                                     mono_microsecs
6216
 
                                                     *const,
6217
 
                                                     bool *const,
6218
 
                                                     bool *const,
6219
 
                                                     const char
6220
 
                                                     *const);
6221
 
 
6222
 
static void test_add_existing_questions_ENOENT(__attribute__((unused))
6223
 
                                               test_fixture *fixture,
6224
 
                                               __attribute__((unused))
6225
 
                                               gconstpointer
6226
 
                                               user_data){
6227
 
  __attribute__((cleanup(cleanup_queue)))
6228
 
    task_queue *queue = create_queue();
6229
 
  g_assert_nonnull(queue);
6230
 
  __attribute__((cleanup(cleanup_close)))
6231
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6232
 
  g_assert_cmpint(epoll_fd, >=, 0);
6233
 
  __attribute__((cleanup(string_set_clear)))
6234
 
    string_set cancelled_filenames = {};
6235
 
 
6236
 
  g_assert_false(assert_add_existing_questions_to_devnull
6237
 
                 (queue,
6238
 
                  epoll_fd,
6239
 
                  (buffer[]){{}}, /* password */
6240
 
                  &cancelled_filenames,
6241
 
                  (mono_microsecs[]){0}, /* current_time */
6242
 
                  (bool[]){false},       /* mandos_client_exited */
6243
 
                  (bool[]){false},       /* password_is_read */
6244
 
                  "/nonexistent"));      /* dirname */
6245
 
 
6246
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6247
 
}
6248
 
 
6249
 
static
6250
 
bool assert_add_existing_questions_to_devnull(task_queue
6251
 
                                              *const queue,
6252
 
                                              const int
6253
 
                                              epoll_fd,
6254
 
                                              buffer *const
6255
 
                                              password,
6256
 
                                              string_set
6257
 
                                              *cancelled_filenames,
6258
 
                                              const mono_microsecs
6259
 
                                              *const current_time,
6260
 
                                              bool *const
6261
 
                                              mandos_client_exited,
6262
 
                                              bool *const
6263
 
                                              password_is_read,
6264
 
                                              const char *const
6265
 
                                              dirname){
6266
 
  __attribute__((cleanup(cleanup_close)))
6267
 
    const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
6268
 
  g_assert_cmpint(devnull_fd, >=, 0);
6269
 
  __attribute__((cleanup(cleanup_close)))
6270
 
    const int real_stderr_fd = dup(STDERR_FILENO);
6271
 
  g_assert_cmpint(real_stderr_fd, >=, 0);
6272
 
  dup2(devnull_fd, STDERR_FILENO);
6273
 
  const bool ret = add_existing_questions(queue, epoll_fd, password,
6274
 
                                          cancelled_filenames,
6275
 
                                          current_time,
6276
 
                                          mandos_client_exited,
6277
 
                                          password_is_read, dirname);
6278
 
  dup2(real_stderr_fd, STDERR_FILENO);
6279
 
  return ret;
6280
 
}
6281
 
 
6282
 
static
6283
 
void test_add_existing_questions_no_questions(__attribute__((unused))
6284
 
                                              test_fixture *fixture,
6285
 
                                              __attribute__((unused))
6286
 
                                              gconstpointer
6287
 
                                              user_data){
6288
 
  __attribute__((cleanup(cleanup_queue)))
6289
 
    task_queue *queue = create_queue();
6290
 
  g_assert_nonnull(queue);
6291
 
  __attribute__((cleanup(cleanup_close)))
6292
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6293
 
  g_assert_cmpint(epoll_fd, >=, 0);
6294
 
  __attribute__((cleanup(string_set_clear)))
6295
 
    string_set cancelled_filenames = {};
6296
 
  __attribute__((cleanup(cleanup_string)))
6297
 
    char *tempdir = make_temporary_directory();
6298
 
  g_assert_nonnull(tempdir);
6299
 
 
6300
 
  g_assert_false(assert_add_existing_questions_to_devnull
6301
 
                 (queue,
6302
 
                  epoll_fd,
6303
 
                  (buffer[]){{}}, /* password */
6304
 
                  &cancelled_filenames,
6305
 
                  (mono_microsecs[]){0}, /* current_time */
6306
 
                  (bool[]){false},       /* mandos_client_exited */
6307
 
                  (bool[]){false},       /* password_is_read */
6308
 
                  tempdir));
6309
 
 
6310
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6311
 
 
6312
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
6313
 
}
6314
 
 
6315
 
static char *make_question_file_in_directory(const char *const);
6316
 
 
6317
 
static
6318
 
void test_add_existing_questions_one_question(__attribute__((unused))
6319
 
                                              test_fixture *fixture,
6320
 
                                              __attribute__((unused))
6321
 
                                              gconstpointer
6322
 
                                              user_data){
6323
 
  __attribute__((cleanup(cleanup_queue)))
6324
 
    task_queue *queue = create_queue();
6325
 
  g_assert_nonnull(queue);
6326
 
  __attribute__((cleanup(cleanup_close)))
6327
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6328
 
  g_assert_cmpint(epoll_fd, >=, 0);
6329
 
  __attribute__((cleanup(cleanup_buffer)))
6330
 
    buffer password = {};
6331
 
  __attribute__((cleanup(string_set_clear)))
6332
 
    string_set cancelled_filenames = {};
6333
 
  const mono_microsecs current_time = 0;
6334
 
  bool mandos_client_exited = false;
6335
 
  bool password_is_read = false;
6336
 
  __attribute__((cleanup(cleanup_string)))
6337
 
    char *tempdir = make_temporary_directory();
6338
 
  g_assert_nonnull(tempdir);
6339
 
  __attribute__((cleanup(cleanup_string)))
6340
 
    char *question_filename
6341
 
    = make_question_file_in_directory(tempdir);
6342
 
  g_assert_nonnull(question_filename);
6343
 
 
6344
 
  g_assert_true(assert_add_existing_questions_to_devnull
6345
 
                (queue,
6346
 
                 epoll_fd,
6347
 
                 &password,
6348
 
                 &cancelled_filenames,
6349
 
                 &current_time,
6350
 
                 &mandos_client_exited,
6351
 
                 &password_is_read,
6352
 
                 tempdir));
6353
 
 
6354
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6355
 
 
6356
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
6357
 
        .func=open_and_parse_question,
6358
 
        .epoll_fd=epoll_fd,
6359
 
        .filename=question_filename,
6360
 
        .question_filename=question_filename,
6361
 
        .password=&password,
6362
 
        .cancelled_filenames=&cancelled_filenames,
6363
 
        .current_time=&current_time,
6364
 
        .mandos_client_exited=&mandos_client_exited,
6365
 
        .password_is_read=&password_is_read,
6366
 
      }));
6367
 
 
6368
 
  g_assert_true(queue->next_run == 1);
6369
 
 
6370
 
  g_assert_cmpint(unlink(question_filename), ==, 0);
6371
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
6372
 
}
6373
 
 
6374
 
static char *make_question_file_in_directory(const char
6375
 
                                             *const dir){
6376
 
  return make_temporary_prefixed_file_in_directory("ask.", dir);
6377
 
}
6378
 
 
6379
 
static
6380
 
void test_add_existing_questions_two_questions(__attribute__((unused))
6381
 
                                               test_fixture *fixture,
6382
 
                                               __attribute__((unused))
6383
 
                                               gconstpointer
6384
 
                                               user_data){
6385
 
  __attribute__((cleanup(cleanup_queue)))
6386
 
    task_queue *queue = create_queue();
6387
 
  g_assert_nonnull(queue);
6388
 
  __attribute__((cleanup(cleanup_close)))
6389
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6390
 
  g_assert_cmpint(epoll_fd, >=, 0);
6391
 
  __attribute__((cleanup(cleanup_buffer)))
6392
 
    buffer password = {};
6393
 
  __attribute__((cleanup(string_set_clear)))
6394
 
    string_set cancelled_filenames = {};
6395
 
  const mono_microsecs current_time = 0;
6396
 
  bool mandos_client_exited = false;
6397
 
  bool password_is_read = false;
6398
 
  __attribute__((cleanup(cleanup_string)))
6399
 
    char *tempdir = make_temporary_directory();
6400
 
  g_assert_nonnull(tempdir);
6401
 
  __attribute__((cleanup(cleanup_string)))
6402
 
    char *question_filename1
6403
 
    = make_question_file_in_directory(tempdir);
6404
 
  g_assert_nonnull(question_filename1);
6405
 
  __attribute__((cleanup(cleanup_string)))
6406
 
    char *question_filename2
6407
 
    = make_question_file_in_directory(tempdir);
6408
 
  g_assert_nonnull(question_filename2);
6409
 
 
6410
 
  g_assert_true(assert_add_existing_questions_to_devnull
6411
 
                (queue,
6412
 
                 epoll_fd,
6413
 
                 &password,
6414
 
                 &cancelled_filenames,
6415
 
                 &current_time,
6416
 
                 &mandos_client_exited,
6417
 
                 &password_is_read,
6418
 
                 tempdir));
6419
 
 
6420
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 2);
6421
 
 
6422
 
  g_assert_true(queue->next_run == 1);
6423
 
 
6424
 
  __attribute__((cleanup(string_set_clear)))
6425
 
    string_set seen_questions = {};
6426
 
 
6427
 
  bool queue_contains_question_opener(char *const question_filename){
6428
 
    return(find_matching_task(queue, (task_context){
6429
 
          .func=open_and_parse_question,
6430
 
          .epoll_fd=epoll_fd,
6431
 
          .question_filename=question_filename,
6432
 
          .password=&password,
6433
 
          .cancelled_filenames=&cancelled_filenames,
6434
 
          .current_time=&current_time,
6435
 
          .mandos_client_exited=&mandos_client_exited,
6436
 
          .password_is_read=&password_is_read,
6437
 
        }) != NULL);
6438
 
  }
6439
 
 
6440
 
  g_assert_true(queue_contains_question_opener(question_filename1));
6441
 
  g_assert_true(queue_contains_question_opener(question_filename2));
6442
 
 
6443
 
  g_assert_true(queue->next_run == 1);
6444
 
 
6445
 
  g_assert_cmpint(unlink(question_filename1), ==, 0);
6446
 
  g_assert_cmpint(unlink(question_filename2), ==, 0);
6447
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
6448
 
}
6449
 
 
6450
 
static void
6451
 
test_add_existing_questions_non_questions(__attribute__((unused))
6452
 
                                          test_fixture *fixture,
6453
 
                                          __attribute__((unused))
6454
 
                                          gconstpointer user_data){
6455
 
  __attribute__((cleanup(cleanup_queue)))
6456
 
    task_queue *queue = create_queue();
6457
 
  g_assert_nonnull(queue);
6458
 
  __attribute__((cleanup(cleanup_close)))
6459
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6460
 
  g_assert_cmpint(epoll_fd, >=, 0);
6461
 
  __attribute__((cleanup(string_set_clear)))
6462
 
    string_set cancelled_filenames = {};
6463
 
  __attribute__((cleanup(cleanup_string)))
6464
 
    char *tempdir = make_temporary_directory();
6465
 
  g_assert_nonnull(tempdir);
6466
 
  __attribute__((cleanup(cleanup_string)))
6467
 
    char *question_filename1
6468
 
    = make_temporary_file_in_directory(tempdir);
6469
 
  g_assert_nonnull(question_filename1);
6470
 
  __attribute__((cleanup(cleanup_string)))
6471
 
    char *question_filename2
6472
 
    = make_temporary_file_in_directory(tempdir);
6473
 
  g_assert_nonnull(question_filename2);
6474
 
 
6475
 
  g_assert_false(assert_add_existing_questions_to_devnull
6476
 
                 (queue,
6477
 
                  epoll_fd,
6478
 
                  (buffer[]){{}}, /* password */
6479
 
                  &cancelled_filenames,
6480
 
                  (mono_microsecs[]){0}, /* current_time */
6481
 
                  (bool[]){false},       /* mandos_client_exited */
6482
 
                  (bool[]){false},       /* password_is_read */
6483
 
                  tempdir));
6484
 
 
6485
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6486
 
 
6487
 
  g_assert_cmpint(unlink(question_filename1), ==, 0);
6488
 
  g_assert_cmpint(unlink(question_filename2), ==, 0);
6489
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
6490
 
}
6491
 
 
6492
 
static void
6493
 
test_add_existing_questions_both_types(__attribute__((unused))
6494
 
                                       test_fixture *fixture,
6495
 
                                       __attribute__((unused))
6496
 
                                       gconstpointer user_data){
6497
 
  __attribute__((cleanup(cleanup_queue)))
6498
 
    task_queue *queue = create_queue();
6499
 
  g_assert_nonnull(queue);
6500
 
  __attribute__((cleanup(cleanup_close)))
6501
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6502
 
  g_assert_cmpint(epoll_fd, >=, 0);
6503
 
  __attribute__((cleanup(cleanup_buffer)))
6504
 
    buffer password = {};
6505
 
  __attribute__((cleanup(string_set_clear)))
6506
 
    string_set cancelled_filenames = {};
6507
 
  const mono_microsecs current_time = 0;
6508
 
  bool mandos_client_exited = false;
6509
 
  bool password_is_read = false;
6510
 
  __attribute__((cleanup(cleanup_string)))
6511
 
    char *tempdir = make_temporary_directory();
6512
 
  g_assert_nonnull(tempdir);
6513
 
  __attribute__((cleanup(cleanup_string)))
6514
 
    char *tempfilename1 = make_temporary_file_in_directory(tempdir);
6515
 
  g_assert_nonnull(tempfilename1);
6516
 
  __attribute__((cleanup(cleanup_string)))
6517
 
    char *tempfilename2 = make_temporary_file_in_directory(tempdir);
6518
 
  g_assert_nonnull(tempfilename2);
6519
 
  __attribute__((cleanup(cleanup_string)))
6520
 
    char *question_filename
6521
 
    = make_question_file_in_directory(tempdir);
6522
 
  g_assert_nonnull(question_filename);
6523
 
 
6524
 
  g_assert_true(assert_add_existing_questions_to_devnull
6525
 
                (queue,
6526
 
                 epoll_fd,
6527
 
                 &password,
6528
 
                 &cancelled_filenames,
6529
 
                 &current_time,
6530
 
                 &mandos_client_exited,
6531
 
                 &password_is_read,
6532
 
                 tempdir));
6533
 
 
6534
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
6535
 
 
6536
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
6537
 
        .func=open_and_parse_question,
6538
 
        .epoll_fd=epoll_fd,
6539
 
        .filename=question_filename,
6540
 
        .question_filename=question_filename,
6541
 
        .password=&password,
6542
 
        .cancelled_filenames=&cancelled_filenames,
6543
 
        .current_time=&current_time,
6544
 
        .mandos_client_exited=&mandos_client_exited,
6545
 
        .password_is_read=&password_is_read,
6546
 
      }));
6547
 
 
6548
 
  g_assert_true(queue->next_run == 1);
6549
 
 
6550
 
  g_assert_cmpint(unlink(tempfilename1), ==, 0);
6551
 
  g_assert_cmpint(unlink(tempfilename2), ==, 0);
6552
 
  g_assert_cmpint(unlink(question_filename), ==, 0);
6553
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
6554
 
}
6555
 
 
6556
 
static void test_wait_for_event_timeout(__attribute__((unused))
6557
 
                                        test_fixture *fixture,
6558
 
                                        __attribute__((unused))
6559
 
                                        gconstpointer user_data){
6560
 
  __attribute__((cleanup(cleanup_close)))
6561
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6562
 
  g_assert_cmpint(epoll_fd, >=, 0);
6563
 
 
6564
 
  g_assert_true(wait_for_event(epoll_fd, 1, 0));
6565
 
}
6566
 
 
6567
 
static void test_wait_for_event_event(__attribute__((unused))
6568
 
                                      test_fixture *fixture,
6569
 
                                      __attribute__((unused))
6570
 
                                      gconstpointer user_data){
6571
 
  __attribute__((cleanup(cleanup_close)))
6572
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6573
 
  g_assert_cmpint(epoll_fd, >=, 0);
6574
 
  int pipefds[2];
6575
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6576
 
  __attribute__((cleanup(cleanup_close)))
6577
 
    const int read_pipe = pipefds[0];
6578
 
  __attribute__((cleanup(cleanup_close)))
6579
 
    const int write_pipe = pipefds[1];
6580
 
  g_assert_cmpint(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, read_pipe,
6581
 
                            &(struct epoll_event)
6582
 
                            { .events=EPOLLIN | EPOLLRDHUP }), ==, 0);
6583
 
  g_assert_cmpint((int)write(write_pipe, "x", 1), ==, 1);
6584
 
 
6585
 
  g_assert_true(wait_for_event(epoll_fd, 0, 0));
6586
 
}
6587
 
 
6588
 
static void test_wait_for_event_sigchld(test_fixture *fixture,
6589
 
                                        __attribute__((unused))
6590
 
                                        gconstpointer user_data){
6591
 
  const pid_t pid = fork();
6592
 
  if(pid == 0){         /* Child */
6593
 
    if(not restore_signal_handler(&fixture->orig_sigaction)){
6594
 
      _exit(EXIT_FAILURE);
6595
 
    }
6596
 
    if(not restore_sigmask(&fixture->orig_sigmask)){
6597
 
      _exit(EXIT_FAILURE);
6598
 
    }
6599
 
    exit(EXIT_SUCCESS);
6600
 
  }
6601
 
  g_assert_true(pid != -1);
6602
 
  __attribute__((cleanup(cleanup_close)))
6603
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6604
 
  g_assert_cmpint(epoll_fd, >=, 0);
6605
 
 
6606
 
  g_assert_true(wait_for_event(epoll_fd, 0, 0));
6607
 
 
6608
 
  int status;
6609
 
  g_assert_true(waitpid(pid, &status, 0) == pid);
6610
 
  g_assert_true(WIFEXITED(status));
6611
 
  g_assert_cmpint(WEXITSTATUS(status), ==, EXIT_SUCCESS);
6612
 
}
6613
 
 
6614
 
static void test_run_queue_zeroes_next_run(__attribute__((unused))
6615
 
                                           test_fixture *fixture,
6616
 
                                           __attribute__((unused))
6617
 
                                           gconstpointer user_data){
6618
 
  __attribute__((cleanup(cleanup_queue)))
6619
 
    task_queue *queue = create_queue();
6620
 
  g_assert_nonnull(queue);
6621
 
  queue->next_run = 1;
6622
 
  __attribute__((cleanup(cleanup_close)))
6623
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6624
 
  __attribute__((cleanup(string_set_clear)))
6625
 
    string_set cancelled_filenames = {};
6626
 
  bool quit_now = false;
6627
 
 
6628
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6629
 
  g_assert_false(quit_now);
6630
 
  g_assert_cmpuint((unsigned int)queue->next_run, ==, 0);
6631
 
}
6632
 
 
6633
 
static
6634
 
void test_run_queue_clears_cancelled_filenames(__attribute__((unused))
6635
 
                                               test_fixture *fixture,
6636
 
                                               __attribute__((unused))
6637
 
                                               gconstpointer
6638
 
                                               user_data){
6639
 
  __attribute__((cleanup(cleanup_queue)))
6640
 
    task_queue *queue = create_queue();
6641
 
  g_assert_nonnull(queue);
6642
 
  __attribute__((cleanup(string_set_clear)))
6643
 
    string_set cancelled_filenames = {};
6644
 
  bool quit_now = false;
6645
 
  const char question_filename[] = "/nonexistent/question_filename";
6646
 
  g_assert_true(string_set_add(&cancelled_filenames,
6647
 
                               question_filename));
6648
 
 
6649
 
  g_assert_true(add_to_queue(queue,
6650
 
                             (task_context){ .func=dummy_func }));
6651
 
 
6652
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6653
 
  g_assert_false(quit_now);
6654
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6655
 
  g_assert_false(string_set_contains(cancelled_filenames,
6656
 
                                     question_filename));
6657
 
}
6658
 
 
6659
 
static
6660
 
void test_run_queue_skips_cancelled_filenames(__attribute__((unused))
6661
 
                                              test_fixture *fixture,
6662
 
                                              __attribute__((unused))
6663
 
                                              gconstpointer
6664
 
                                              user_data){
6665
 
  __attribute__((cleanup(cleanup_queue)))
6666
 
    task_queue *queue = create_queue();
6667
 
  g_assert_nonnull(queue);
6668
 
  __attribute__((cleanup(string_set_clear)))
6669
 
    string_set cancelled_filenames = {};
6670
 
  bool quit_now = false;
6671
 
  int pipefds[2];
6672
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6673
 
  __attribute__((cleanup(cleanup_close)))
6674
 
    const int read_pipe = pipefds[0];
6675
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
6676
 
  const char question_filename[] = "/nonexistent/question_filename";
6677
 
  g_assert_true(string_set_add(&cancelled_filenames,
6678
 
                               question_filename));
6679
 
  __attribute__((nonnull))
6680
 
    void quit_func(const task_context task,
6681
 
                   __attribute__((unused)) task_queue *const q){
6682
 
    g_assert_nonnull(task.quit_now);
6683
 
    *task.quit_now = true;
6684
 
  }
6685
 
  task_context task = {
6686
 
    .func=quit_func,
6687
 
    .question_filename=strdup(question_filename),
6688
 
    .quit_now=&quit_now,
6689
 
    .fd=read_pipe,
6690
 
  };
6691
 
  g_assert_nonnull(task.question_filename);
6692
 
 
6693
 
  g_assert_true(add_to_queue(queue, task));
6694
 
 
6695
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6696
 
  g_assert_false(quit_now);
6697
 
 
6698
 
  /* read_pipe should be closed already */
6699
 
  errno = 0;
6700
 
  bool read_pipe_closed = (close(read_pipe) == -1);
6701
 
  read_pipe_closed &= (errno == EBADF);
6702
 
  g_assert_true(read_pipe_closed);
6703
 
}
6704
 
 
6705
 
static void test_run_queue_one_task(__attribute__((unused))
6706
 
                                    test_fixture *fixture,
6707
 
                                    __attribute__((unused))
6708
 
                                    gconstpointer user_data){
6709
 
  __attribute__((cleanup(cleanup_queue)))
6710
 
    task_queue *queue = create_queue();
6711
 
  g_assert_nonnull(queue);
6712
 
  __attribute__((cleanup(string_set_clear)))
6713
 
    string_set cancelled_filenames = {};
6714
 
  bool quit_now = false;
6715
 
 
6716
 
  __attribute__((nonnull))
6717
 
    void next_run_func(__attribute__((unused))
6718
 
                       const task_context task,
6719
 
                       task_queue *const q){
6720
 
    q->next_run = 1;
6721
 
  }
6722
 
 
6723
 
  task_context task = {
6724
 
    .func=next_run_func,
6725
 
  };
6726
 
  g_assert_true(add_to_queue(queue, task));
6727
 
 
6728
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6729
 
  g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6730
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6731
 
}
6732
 
 
6733
 
static void test_run_queue_two_tasks(__attribute__((unused))
6734
 
                                     test_fixture *fixture,
6735
 
                                     __attribute__((unused))
6736
 
                                     gconstpointer user_data){
6737
 
  __attribute__((cleanup(cleanup_queue)))
6738
 
    task_queue *queue = create_queue();
6739
 
  g_assert_nonnull(queue);
6740
 
  queue->next_run = 1;
6741
 
  __attribute__((cleanup(string_set_clear)))
6742
 
    string_set cancelled_filenames = {};
6743
 
  bool quit_now = false;
6744
 
  bool mandos_client_exited = false;
6745
 
 
6746
 
  __attribute__((nonnull))
6747
 
    void next_run_func(__attribute__((unused))
6748
 
                       const task_context task,
6749
 
                       task_queue *const q){
6750
 
    q->next_run = 1;
6751
 
  }
6752
 
 
6753
 
  __attribute__((nonnull))
6754
 
    void exited_func(const task_context task,
6755
 
                     __attribute__((unused)) task_queue *const q){
6756
 
    *task.mandos_client_exited = true;
6757
 
  }
6758
 
 
6759
 
  task_context task1 = {
6760
 
    .func=next_run_func,
6761
 
  };
6762
 
  g_assert_true(add_to_queue(queue, task1));
6763
 
 
6764
 
  task_context task2 = {
6765
 
    .func=exited_func,
6766
 
    .mandos_client_exited=&mandos_client_exited,
6767
 
  };
6768
 
  g_assert_true(add_to_queue(queue, task2));
6769
 
 
6770
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
6771
 
  g_assert_false(quit_now);
6772
 
  g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
6773
 
  g_assert_true(mandos_client_exited);
6774
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6775
 
}
6776
 
 
6777
 
static void test_run_queue_two_tasks_quit(__attribute__((unused))
6778
 
                                          test_fixture *fixture,
6779
 
                                          __attribute__((unused))
6780
 
                                          gconstpointer user_data){
6781
 
  __attribute__((cleanup(cleanup_queue)))
6782
 
    task_queue *queue = create_queue();
6783
 
  g_assert_nonnull(queue);
6784
 
  __attribute__((cleanup(string_set_clear)))
6785
 
    string_set cancelled_filenames = {};
6786
 
  bool quit_now = false;
6787
 
  bool mandos_client_exited = false;
6788
 
  bool password_is_read = false;
6789
 
 
6790
 
  __attribute__((nonnull))
6791
 
    void set_exited_func(const task_context task,
6792
 
                         __attribute__((unused)) task_queue *const q){
6793
 
    *task.mandos_client_exited = true;
6794
 
    *task.quit_now = true;
6795
 
  }
6796
 
  task_context task1 = {
6797
 
    .func=set_exited_func,
6798
 
    .quit_now=&quit_now,
6799
 
    .mandos_client_exited=&mandos_client_exited,
6800
 
  };
6801
 
  g_assert_true(add_to_queue(queue, task1));
6802
 
 
6803
 
  __attribute__((nonnull))
6804
 
    void set_read_func(const task_context task,
6805
 
                       __attribute__((unused)) task_queue *const q){
6806
 
    *task.quit_now = true;
6807
 
    *task.password_is_read = true;
6808
 
  }
6809
 
  task_context task2 = {
6810
 
    .func=set_read_func,
6811
 
    .quit_now=&quit_now,
6812
 
    .password_is_read=&password_is_read,
6813
 
  };
6814
 
  g_assert_true(add_to_queue(queue, task2));
6815
 
 
6816
 
  g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6817
 
  g_assert_true(quit_now);
6818
 
  g_assert_true(mandos_client_exited xor password_is_read);
6819
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6820
 
}
6821
 
 
6822
 
static void test_run_queue_two_tasks_cleanup(__attribute__((unused))
6823
 
                                             test_fixture *fixture,
6824
 
                                             __attribute__((unused))
6825
 
                                             gconstpointer user_data){
6826
 
  __attribute__((cleanup(cleanup_queue)))
6827
 
    task_queue *queue = create_queue();
6828
 
  g_assert_nonnull(queue);
6829
 
  __attribute__((cleanup(string_set_clear)))
6830
 
    string_set cancelled_filenames = {};
6831
 
  int pipefds[2];
6832
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
6833
 
  __attribute__((cleanup(cleanup_close)))
6834
 
    const int read_pipe = pipefds[0];
6835
 
  __attribute__((cleanup(cleanup_close)))
6836
 
    const int write_pipe = pipefds[1];
6837
 
  bool quit_now = false;
6838
 
 
6839
 
  __attribute__((nonnull))
6840
 
    void read_func(const task_context task,
6841
 
                   __attribute__((unused)) task_queue *const q){
6842
 
    *task.quit_now = true;
6843
 
  }
6844
 
  task_context task1 = {
6845
 
    .func=read_func,
6846
 
    .quit_now=&quit_now,
6847
 
    .fd=read_pipe,
6848
 
  };
6849
 
  g_assert_true(add_to_queue(queue, task1));
6850
 
 
6851
 
  __attribute__((nonnull))
6852
 
    void write_func(const task_context task,
6853
 
                    __attribute__((unused)) task_queue *const q){
6854
 
    *task.quit_now = true;
6855
 
  }
6856
 
  task_context task2 = {
6857
 
    .func=write_func,
6858
 
    .quit_now=&quit_now,
6859
 
    .fd=write_pipe,
6860
 
  };
6861
 
  g_assert_true(add_to_queue(queue, task2));
6862
 
 
6863
 
  g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
6864
 
  g_assert_true(quit_now);
6865
 
 
6866
 
  /* Either read_pipe or write_pipe should be closed already */
6867
 
  errno = 0;
6868
 
  bool close_read_pipe = (close(read_pipe) == -1);
6869
 
  close_read_pipe &= (errno == EBADF);
6870
 
  errno = 0;
6871
 
  bool close_write_pipe = (close(write_pipe) == -1);
6872
 
  close_write_pipe &= (errno == EBADF);
6873
 
  g_assert_true(close_read_pipe xor close_write_pipe);
6874
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
6875
 
}
6876
 
 
6877
 
static void test_setup_signal_handler(__attribute__((unused))
6878
 
                                      test_fixture *fixture,
6879
 
                                      __attribute__((unused))
6880
 
                                      gconstpointer user_data){
6881
 
  /* Save current SIGCHLD action, whatever it is */
6882
 
  struct sigaction expected_sigchld_action;
6883
 
  g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
6884
 
                  ==, 0);
6885
 
 
6886
 
  /* Act; i.e. run the setup_signal_handler() function */
6887
 
  struct sigaction actual_old_sigchld_action;
6888
 
  g_assert_true(setup_signal_handler(&actual_old_sigchld_action));
6889
 
 
6890
 
  /* Check that the function correctly set "actual_old_sigchld_action"
6891
 
     to the same values as the previously saved
6892
 
     "expected_sigchld_action" */
6893
 
  /* Check member sa_handler */
6894
 
  g_assert_true(actual_old_sigchld_action.sa_handler
6895
 
                == expected_sigchld_action.sa_handler);
6896
 
  /* Check member sa_mask */
6897
 
  for(int signum = 1; signum < NSIG; signum++){
6898
 
    const int expected_old_block_state
6899
 
      = sigismember(&expected_sigchld_action.sa_mask, signum);
6900
 
    g_assert_cmpint(expected_old_block_state, >=, 0);
6901
 
    const int actual_old_block_state
6902
 
      = sigismember(&actual_old_sigchld_action.sa_mask, signum);
6903
 
    g_assert_cmpint(actual_old_block_state, >=, 0);
6904
 
    g_assert_cmpint(actual_old_block_state,
6905
 
                    ==, expected_old_block_state);
6906
 
  }
6907
 
  /* Check member sa_flags */
6908
 
  g_assert_true((actual_old_sigchld_action.sa_flags
6909
 
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
6910
 
                == (expected_sigchld_action.sa_flags
6911
 
                    & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
6912
 
 
6913
 
  /* Retrieve the current signal handler for SIGCHLD as set by
6914
 
     setup_signal_handler() */
6915
 
  struct sigaction actual_new_sigchld_action;
6916
 
  g_assert_cmpint(sigaction(SIGCHLD, NULL,
6917
 
                            &actual_new_sigchld_action), ==, 0);
6918
 
  /* Check that the signal handler (member sa_handler) is correctly
6919
 
     set to the "handle_sigchld" function */
6920
 
  g_assert_true(actual_new_sigchld_action.sa_handler != SIG_DFL);
6921
 
  g_assert_true(actual_new_sigchld_action.sa_handler != SIG_IGN);
6922
 
  g_assert_true(actual_new_sigchld_action.sa_handler
6923
 
                == handle_sigchld);
6924
 
  /* Check (in member sa_mask) that at least a handful of signals are
6925
 
     actually blocked during the signal handler */
6926
 
  for(int signum = 1; signum < NSIG; signum++){
6927
 
    int actual_new_block_state;
6928
 
    switch(signum){
6929
 
    case SIGTERM:
6930
 
    case SIGINT:
6931
 
    case SIGQUIT:
6932
 
    case SIGHUP:
6933
 
      actual_new_block_state
6934
 
        = sigismember(&actual_new_sigchld_action.sa_mask, signum);
6935
 
      g_assert_cmpint(actual_new_block_state, ==, 1);
6936
 
      continue;
6937
 
    case SIGKILL:               /* non-blockable */
6938
 
    case SIGSTOP:               /* non-blockable */
6939
 
    case SIGCHLD:               /* always blocked */
6940
 
    default:
6941
 
      continue;
6942
 
    }
6943
 
  }
6944
 
  /* Check member sa_flags */
6945
 
  g_assert_true((actual_new_sigchld_action.sa_flags
6946
 
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
6947
 
                == (SA_NOCLDSTOP | SA_RESTART));
6948
 
 
6949
 
  /* Restore signal handler */
6950
 
  g_assert_cmpint(sigaction(SIGCHLD, &expected_sigchld_action, NULL),
6951
 
                  ==, 0);
6952
 
}
6953
 
 
6954
 
static void test_restore_signal_handler(__attribute__((unused))
6955
 
                                        test_fixture *fixture,
6956
 
                                        __attribute__((unused))
6957
 
                                        gconstpointer user_data){
6958
 
  /* Save current SIGCHLD action, whatever it is */
6959
 
  struct sigaction expected_sigchld_action;
6960
 
  g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
6961
 
                  ==, 0);
6962
 
  /* Since we haven't established a signal handler yet, there should
6963
 
     not be one established.  But another test may have relied on
6964
 
     restore_signal_handler() to restore the signal handler, and if
6965
 
     restore_signal_handler() is buggy (which we should be prepared
6966
 
     for in this test) the signal handler may not have been restored
6967
 
     properly; check for this: */
6968
 
  g_assert_true(expected_sigchld_action.sa_handler != handle_sigchld);
6969
 
 
6970
 
  /* Establish a signal handler */
6971
 
  struct sigaction sigchld_action = {
6972
 
    .sa_handler=handle_sigchld,
6973
 
    .sa_flags=SA_RESTART | SA_NOCLDSTOP,
6974
 
  };
6975
 
  g_assert_cmpint(sigfillset(&sigchld_action.sa_mask), ==, 0);
6976
 
  g_assert_cmpint(sigaction(SIGCHLD, &sigchld_action, NULL), ==, 0);
6977
 
 
6978
 
  /* Act; i.e. run the restore_signal_handler() function */
6979
 
  g_assert_true(restore_signal_handler(&expected_sigchld_action));
6980
 
 
6981
 
  /* Retrieve the restored signal handler data */
6982
 
  struct sigaction actual_restored_sigchld_action;
6983
 
  g_assert_cmpint(sigaction(SIGCHLD, NULL,
6984
 
                            &actual_restored_sigchld_action), ==, 0);
6985
 
 
6986
 
  /* Check that the function correctly restored the signal action, as
6987
 
     saved in "actual_restored_sigchld_action", to the same values as
6988
 
     the previously saved "expected_sigchld_action" */
6989
 
  /* Check member sa_handler */
6990
 
  g_assert_true(actual_restored_sigchld_action.sa_handler
6991
 
                == expected_sigchld_action.sa_handler);
6992
 
  /* Check member sa_mask */
6993
 
  for(int signum = 1; signum < NSIG; signum++){
6994
 
    const int expected_old_block_state
6995
 
      = sigismember(&expected_sigchld_action.sa_mask, signum);
6996
 
    g_assert_cmpint(expected_old_block_state, >=, 0);
6997
 
    const int actual_restored_block_state
6998
 
      = sigismember(&actual_restored_sigchld_action.sa_mask, signum);
6999
 
    g_assert_cmpint(actual_restored_block_state, >=, 0);
7000
 
    g_assert_cmpint(actual_restored_block_state,
7001
 
                    ==, expected_old_block_state);
7002
 
  }
7003
 
  /* Check member sa_flags */
7004
 
  g_assert_true((actual_restored_sigchld_action.sa_flags
7005
 
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
7006
 
                == (expected_sigchld_action.sa_flags
7007
 
                    & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
7008
 
}
7009
 
 
7010
 
static void test_block_sigchld(__attribute__((unused))
7011
 
                               test_fixture *fixture,
7012
 
                               __attribute__((unused))
7013
 
                               gconstpointer user_data){
7014
 
  /* Save original signal mask */
7015
 
  sigset_t expected_sigmask;
7016
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &expected_sigmask),
7017
 
                  ==, 0);
7018
 
 
7019
 
  /* Make sure SIGCHLD is unblocked for this test */
7020
 
  sigset_t sigchld_sigmask;
7021
 
  g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
7022
 
  g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
7023
 
  g_assert_cmpint(pthread_sigmask(SIG_UNBLOCK, &sigchld_sigmask,
7024
 
                                  NULL), ==, 0);
7025
 
 
7026
 
  /* Act; i.e. run the block_sigchld() function */
7027
 
  sigset_t actual_old_sigmask;
7028
 
  g_assert_true(block_sigchld(&actual_old_sigmask));
7029
 
 
7030
 
  /* Check the actual_old_sigmask; it should be the same as the
7031
 
     previously saved signal mask "expected_sigmask". */
7032
 
  for(int signum = 1; signum < NSIG; signum++){
7033
 
    const int expected_old_block_state
7034
 
      = sigismember(&expected_sigmask, signum);
7035
 
    g_assert_cmpint(expected_old_block_state, >=, 0);
7036
 
    const int actual_old_block_state
7037
 
      = sigismember(&actual_old_sigmask, signum);
7038
 
    g_assert_cmpint(actual_old_block_state, >=, 0);
7039
 
    g_assert_cmpint(actual_old_block_state,
7040
 
                    ==, expected_old_block_state);
7041
 
  }
7042
 
 
7043
 
  /* Retrieve the newly set signal mask */
7044
 
  sigset_t actual_sigmask;
7045
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &actual_sigmask), ==, 0);
7046
 
 
7047
 
  /* SIGCHLD should be blocked */
7048
 
  g_assert_cmpint(sigismember(&actual_sigmask, SIGCHLD), ==, 1);
7049
 
 
7050
 
  /* Restore signal mask */
7051
 
  g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &expected_sigmask,
7052
 
                                  NULL), ==, 0);
7053
 
}
7054
 
 
7055
 
static void test_restore_sigmask(__attribute__((unused))
7056
 
                                 test_fixture *fixture,
7057
 
                                 __attribute__((unused))
7058
 
                                 gconstpointer user_data){
7059
 
  /* Save original signal mask */
7060
 
  sigset_t orig_sigmask;
7061
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &orig_sigmask), ==, 0);
7062
 
 
7063
 
  /* Make sure SIGCHLD is blocked for this test */
7064
 
  sigset_t sigchld_sigmask;
7065
 
  g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
7066
 
  g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
7067
 
  g_assert_cmpint(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask,
7068
 
                                  NULL), ==, 0);
7069
 
 
7070
 
  /* Act; i.e. run the restore_sigmask() function */
7071
 
  g_assert_true(restore_sigmask(&orig_sigmask));
7072
 
 
7073
 
  /* Retrieve the newly restored signal mask */
7074
 
  sigset_t restored_sigmask;
7075
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &restored_sigmask),
7076
 
                  ==, 0);
7077
 
 
7078
 
  /* Check the restored_sigmask; it should be the same as the
7079
 
     previously saved signal mask "orig_sigmask". */
7080
 
  for(int signum = 1; signum < NSIG; signum++){
7081
 
    const int orig_block_state = sigismember(&orig_sigmask, signum);
7082
 
    g_assert_cmpint(orig_block_state, >=, 0);
7083
 
    const int restored_block_state = sigismember(&restored_sigmask,
7084
 
                                                 signum);
7085
 
    g_assert_cmpint(restored_block_state, >=, 0);
7086
 
    g_assert_cmpint(restored_block_state, ==, orig_block_state);
7087
 
  }
7088
 
 
7089
 
  /* Restore signal mask */
7090
 
  g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &orig_sigmask,
7091
 
                                  NULL), ==, 0);
7092
 
}
7093
 
 
7094
 
static void test_parse_arguments_noargs(__attribute__((unused))
7095
 
                                        test_fixture *fixture,
7096
 
                                        __attribute__((unused))
7097
 
                                        gconstpointer user_data){
7098
 
  char *argv[] = {
7099
 
    strdup("prgname"),
7100
 
    NULL };
7101
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7102
 
 
7103
 
  char *agent_directory = NULL;
7104
 
  char *helper_directory = NULL;
7105
 
  uid_t user = 0;
7106
 
  gid_t group = 0;
7107
 
  char *mandos_argz = NULL;
7108
 
  size_t mandos_argz_length = 0;
7109
 
 
7110
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7111
 
                                &helper_directory, &user, &group,
7112
 
                                &mandos_argz, &mandos_argz_length));
7113
 
  g_assert_null(agent_directory);
7114
 
  g_assert_null(helper_directory);
7115
 
  g_assert_true(user == 0);
7116
 
  g_assert_true(group == 0);
7117
 
  g_assert_null(mandos_argz);
7118
 
  g_assert_true(mandos_argz_length == 0);
7119
 
 
7120
 
  for(char **arg = argv; *arg != NULL; arg++){
7121
 
    free(*arg);
7122
 
  }
7123
 
}
7124
 
 
7125
 
__attribute__((nonnull))
7126
 
static bool parse_arguments_devnull(int argc, char *argv[],
7127
 
                                    const bool exit_failure,
7128
 
                                    char **agent_directory,
7129
 
                                    char **helper_directory,
7130
 
                                    uid_t *const user,
7131
 
                                    gid_t *const group,
7132
 
                                    char **mandos_argz,
7133
 
                                    size_t *mandos_argz_length){
7134
 
 
7135
 
  FILE *real_stderr = stderr;
7136
 
  FILE *devnull = fopen("/dev/null", "we");
7137
 
  g_assert_nonnull(devnull);
7138
 
  stderr = devnull;
7139
 
 
7140
 
  const bool ret = parse_arguments(argc, argv, exit_failure,
7141
 
                                   agent_directory,
7142
 
                                   helper_directory, user, group,
7143
 
                                   mandos_argz, mandos_argz_length);
7144
 
  const error_t saved_errno = errno;
7145
 
 
7146
 
  stderr = real_stderr;
7147
 
  g_assert_cmpint(fclose(devnull), ==, 0);
7148
 
 
7149
 
  errno = saved_errno;
7150
 
 
7151
 
  return ret;
7152
 
}
7153
 
 
7154
 
static void test_parse_arguments_invalid(__attribute__((unused))
7155
 
                                         test_fixture *fixture,
7156
 
                                         __attribute__((unused))
7157
 
                                         gconstpointer user_data){
7158
 
  char *argv[] = {
7159
 
    strdup("prgname"),
7160
 
    strdup("--invalid"),
7161
 
    NULL };
7162
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7163
 
 
7164
 
  char *agent_directory = NULL;
7165
 
  char *helper_directory = NULL;
7166
 
  uid_t user = 0;
7167
 
  gid_t group = 0;
7168
 
  char *mandos_argz = NULL;
7169
 
  size_t mandos_argz_length = 0;
7170
 
 
7171
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
7172
 
                                         &agent_directory,
7173
 
                                         &helper_directory, &user,
7174
 
                                         &group, &mandos_argz,
7175
 
                                         &mandos_argz_length));
7176
 
 
7177
 
  g_assert_true(errno == EINVAL);
7178
 
  g_assert_null(agent_directory);
7179
 
  g_assert_null(helper_directory);
7180
 
  g_assert_null(mandos_argz);
7181
 
  g_assert_true(mandos_argz_length == 0);
7182
 
 
7183
 
  for(char **arg = argv; *arg != NULL; arg++){
7184
 
    free(*arg);
7185
 
  }
7186
 
}
7187
 
 
7188
 
static void test_parse_arguments_long_dir(__attribute__((unused))
7189
 
                                          test_fixture *fixture,
7190
 
                                          __attribute__((unused))
7191
 
                                          gconstpointer user_data){
7192
 
  char *argv[] = {
7193
 
    strdup("prgname"),
7194
 
    strdup("--agent-directory"),
7195
 
    strdup("/tmp"),
7196
 
    NULL };
7197
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7198
 
 
7199
 
  __attribute__((cleanup(cleanup_string)))
7200
 
    char *agent_directory = NULL;
7201
 
  char *helper_directory = NULL;
7202
 
  uid_t user = 0;
7203
 
  gid_t group = 0;
7204
 
  __attribute__((cleanup(cleanup_string)))
7205
 
    char *mandos_argz = NULL;
7206
 
  size_t mandos_argz_length = 0;
7207
 
 
7208
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7209
 
                                &helper_directory, &user, &group,
7210
 
                                &mandos_argz, &mandos_argz_length));
7211
 
 
7212
 
  g_assert_cmpstr(agent_directory, ==, "/tmp");
7213
 
  g_assert_null(helper_directory);
7214
 
  g_assert_true(user == 0);
7215
 
  g_assert_true(group == 0);
7216
 
  g_assert_null(mandos_argz);
7217
 
  g_assert_true(mandos_argz_length == 0);
7218
 
 
7219
 
  for(char **arg = argv; *arg != NULL; arg++){
7220
 
    free(*arg);
7221
 
  }
7222
 
}
7223
 
 
7224
 
static void test_parse_arguments_short_dir(__attribute__((unused))
7225
 
                                           test_fixture *fixture,
7226
 
                                           __attribute__((unused))
7227
 
                                           gconstpointer user_data){
7228
 
  char *argv[] = {
7229
 
    strdup("prgname"),
7230
 
    strdup("-d"),
7231
 
    strdup("/tmp"),
7232
 
    NULL };
7233
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7234
 
 
7235
 
  __attribute__((cleanup(cleanup_string)))
7236
 
    char *agent_directory = NULL;
7237
 
  char *helper_directory = NULL;
7238
 
  uid_t user = 0;
7239
 
  gid_t group = 0;
7240
 
  __attribute__((cleanup(cleanup_string)))
7241
 
    char *mandos_argz = NULL;
7242
 
  size_t mandos_argz_length = 0;
7243
 
 
7244
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7245
 
                                &helper_directory, &user, &group,
7246
 
                                &mandos_argz, &mandos_argz_length));
7247
 
 
7248
 
  g_assert_cmpstr(agent_directory, ==, "/tmp");
7249
 
  g_assert_null(helper_directory);
7250
 
  g_assert_true(user == 0);
7251
 
  g_assert_true(group == 0);
7252
 
  g_assert_null(mandos_argz);
7253
 
  g_assert_true(mandos_argz_length == 0);
7254
 
 
7255
 
  for(char **arg = argv; *arg != NULL; arg++){
7256
 
    free(*arg);
7257
 
  }
7258
 
}
7259
 
 
7260
 
static
7261
 
void test_parse_arguments_helper_directory(__attribute__((unused))
7262
 
                                           test_fixture *fixture,
7263
 
                                           __attribute__((unused))
7264
 
                                           gconstpointer user_data){
7265
 
  char *argv[] = {
7266
 
    strdup("prgname"),
7267
 
    strdup("--helper-directory"),
7268
 
    strdup("/tmp"),
7269
 
    NULL };
7270
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7271
 
 
7272
 
  char *agent_directory = NULL;
7273
 
  __attribute__((cleanup(cleanup_string)))
7274
 
    char *helper_directory = NULL;
7275
 
  uid_t user = 0;
7276
 
  gid_t group = 0;
7277
 
  __attribute__((cleanup(cleanup_string)))
7278
 
    char *mandos_argz = NULL;
7279
 
  size_t mandos_argz_length = 0;
7280
 
 
7281
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7282
 
                                &helper_directory, &user, &group,
7283
 
                                &mandos_argz, &mandos_argz_length));
7284
 
 
7285
 
  g_assert_cmpstr(helper_directory, ==, "/tmp");
7286
 
  g_assert_null(agent_directory);
7287
 
  g_assert_true(user == 0);
7288
 
  g_assert_true(group == 0);
7289
 
  g_assert_null(mandos_argz);
7290
 
  g_assert_true(mandos_argz_length == 0);
7291
 
 
7292
 
  for(char **arg = argv; *arg != NULL; arg++){
7293
 
    free(*arg);
7294
 
  }
7295
 
}
7296
 
 
7297
 
static
7298
 
void test_parse_arguments_plugin_helper_dir(__attribute__((unused))
7299
 
                                            test_fixture *fixture,
7300
 
                                            __attribute__((unused))
7301
 
                                            gconstpointer user_data){
7302
 
  char *argv[] = {
7303
 
    strdup("prgname"),
7304
 
    strdup("--plugin-helper-dir"),
7305
 
    strdup("/tmp"),
7306
 
    NULL };
7307
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7308
 
 
7309
 
  char *agent_directory = NULL;
7310
 
  __attribute__((cleanup(cleanup_string)))
7311
 
    char *helper_directory = NULL;
7312
 
  uid_t user = 0;
7313
 
  gid_t group = 0;
7314
 
  __attribute__((cleanup(cleanup_string)))
7315
 
    char *mandos_argz = NULL;
7316
 
  size_t mandos_argz_length = 0;
7317
 
 
7318
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7319
 
                                &helper_directory, &user, &group,
7320
 
                                &mandos_argz, &mandos_argz_length));
7321
 
 
7322
 
  g_assert_cmpstr(helper_directory, ==, "/tmp");
7323
 
  g_assert_null(agent_directory);
7324
 
  g_assert_true(user == 0);
7325
 
  g_assert_true(group == 0);
7326
 
  g_assert_null(mandos_argz);
7327
 
  g_assert_true(mandos_argz_length == 0);
7328
 
 
7329
 
  for(char **arg = argv; *arg != NULL; arg++){
7330
 
    free(*arg);
7331
 
  }
7332
 
}
7333
 
 
7334
 
static void test_parse_arguments_user(__attribute__((unused))
7335
 
                                      test_fixture *fixture,
7336
 
                                      __attribute__((unused))
7337
 
                                      gconstpointer user_data){
7338
 
  char *argv[] = {
7339
 
    strdup("prgname"),
7340
 
    strdup("--user"),
7341
 
    strdup("1000"),
7342
 
    NULL };
7343
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7344
 
 
7345
 
  char *agent_directory = NULL;
7346
 
  __attribute__((cleanup(cleanup_string)))
7347
 
    char *helper_directory = NULL;
7348
 
  uid_t user = 0;
7349
 
  gid_t group = 0;
7350
 
  __attribute__((cleanup(cleanup_string)))
7351
 
    char *mandos_argz = NULL;
7352
 
  size_t mandos_argz_length = 0;
7353
 
 
7354
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7355
 
                                &helper_directory, &user, &group,
7356
 
                                &mandos_argz, &mandos_argz_length));
7357
 
 
7358
 
  g_assert_null(helper_directory);
7359
 
  g_assert_null(agent_directory);
7360
 
  g_assert_cmpuint((unsigned int)user, ==, 1000);
7361
 
  g_assert_true(group == 0);
7362
 
  g_assert_null(mandos_argz);
7363
 
  g_assert_true(mandos_argz_length == 0);
7364
 
 
7365
 
  for(char **arg = argv; *arg != NULL; arg++){
7366
 
    free(*arg);
7367
 
  }
7368
 
}
7369
 
 
7370
 
static void test_parse_arguments_user_invalid(__attribute__((unused))
7371
 
                                              test_fixture *fixture,
7372
 
                                              __attribute__((unused))
7373
 
                                              gconstpointer
7374
 
                                              user_data){
7375
 
  char *argv[] = {
7376
 
    strdup("prgname"),
7377
 
    strdup("--user"),
7378
 
    strdup("invalid"),
7379
 
    NULL };
7380
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7381
 
 
7382
 
  char *agent_directory = NULL;
7383
 
  __attribute__((cleanup(cleanup_string)))
7384
 
    char *helper_directory = NULL;
7385
 
  uid_t user = 0;
7386
 
  gid_t group = 0;
7387
 
  __attribute__((cleanup(cleanup_string)))
7388
 
    char *mandos_argz = NULL;
7389
 
  size_t mandos_argz_length = 0;
7390
 
 
7391
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
7392
 
                                         &agent_directory,
7393
 
                                         &helper_directory, &user,
7394
 
                                         &group, &mandos_argz,
7395
 
                                         &mandos_argz_length));
7396
 
 
7397
 
  g_assert_null(helper_directory);
7398
 
  g_assert_null(agent_directory);
7399
 
  g_assert_cmpuint((unsigned int)user, ==, 0);
7400
 
  g_assert_true(group == 0);
7401
 
  g_assert_null(mandos_argz);
7402
 
  g_assert_true(mandos_argz_length == 0);
7403
 
 
7404
 
  for(char **arg = argv; *arg != NULL; arg++){
7405
 
    free(*arg);
7406
 
  }
7407
 
}
7408
 
 
7409
 
static
7410
 
void test_parse_arguments_user_zero_invalid(__attribute__((unused))
7411
 
                                            test_fixture *fixture,
7412
 
                                            __attribute__((unused))
7413
 
                                            gconstpointer user_data){
7414
 
  char *argv[] = {
7415
 
    strdup("prgname"),
7416
 
    strdup("--user"),
7417
 
    strdup("0"),
7418
 
    NULL };
7419
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7420
 
 
7421
 
  char *agent_directory = NULL;
7422
 
  __attribute__((cleanup(cleanup_string)))
7423
 
    char *helper_directory = NULL;
7424
 
  uid_t user = 0;
7425
 
  gid_t group = 0;
7426
 
  __attribute__((cleanup(cleanup_string)))
7427
 
    char *mandos_argz = NULL;
7428
 
  size_t mandos_argz_length = 0;
7429
 
 
7430
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
7431
 
                                         &agent_directory,
7432
 
                                         &helper_directory, &user,
7433
 
                                         &group, &mandos_argz,
7434
 
                                         &mandos_argz_length));
7435
 
 
7436
 
  g_assert_null(helper_directory);
7437
 
  g_assert_null(agent_directory);
7438
 
  g_assert_cmpuint((unsigned int)user, ==, 0);
7439
 
  g_assert_true(group == 0);
7440
 
  g_assert_null(mandos_argz);
7441
 
  g_assert_true(mandos_argz_length == 0);
7442
 
 
7443
 
  for(char **arg = argv; *arg != NULL; arg++){
7444
 
    free(*arg);
7445
 
  }
7446
 
}
7447
 
 
7448
 
static void test_parse_arguments_group(__attribute__((unused))
7449
 
                                       test_fixture *fixture,
7450
 
                                       __attribute__((unused))
7451
 
                                       gconstpointer user_data){
7452
 
  char *argv[] = {
7453
 
    strdup("prgname"),
7454
 
    strdup("--group"),
7455
 
    strdup("1000"),
7456
 
    NULL };
7457
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7458
 
 
7459
 
  char *agent_directory = NULL;
7460
 
  __attribute__((cleanup(cleanup_string)))
7461
 
    char *helper_directory = NULL;
7462
 
  uid_t user = 0;
7463
 
  gid_t group = 0;
7464
 
  __attribute__((cleanup(cleanup_string)))
7465
 
    char *mandos_argz = NULL;
7466
 
  size_t mandos_argz_length = 0;
7467
 
 
7468
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7469
 
                                &helper_directory, &user, &group,
7470
 
                                &mandos_argz, &mandos_argz_length));
7471
 
 
7472
 
  g_assert_null(helper_directory);
7473
 
  g_assert_null(agent_directory);
7474
 
  g_assert_true(user == 0);
7475
 
  g_assert_cmpuint((unsigned int)group, ==, 1000);
7476
 
  g_assert_null(mandos_argz);
7477
 
  g_assert_true(mandos_argz_length == 0);
7478
 
 
7479
 
  for(char **arg = argv; *arg != NULL; arg++){
7480
 
    free(*arg);
7481
 
  }
7482
 
}
7483
 
 
7484
 
static void test_parse_arguments_group_invalid(__attribute__((unused))
7485
 
                                               test_fixture *fixture,
7486
 
                                               __attribute__((unused))
7487
 
                                               gconstpointer
7488
 
                                               user_data){
7489
 
  char *argv[] = {
7490
 
    strdup("prgname"),
7491
 
    strdup("--group"),
7492
 
    strdup("invalid"),
7493
 
    NULL };
7494
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7495
 
 
7496
 
  char *agent_directory = NULL;
7497
 
  __attribute__((cleanup(cleanup_string)))
7498
 
    char *helper_directory = NULL;
7499
 
  uid_t user = 0;
7500
 
  gid_t group = 0;
7501
 
  __attribute__((cleanup(cleanup_string)))
7502
 
    char *mandos_argz = NULL;
7503
 
  size_t mandos_argz_length = 0;
7504
 
 
7505
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
7506
 
                                         &agent_directory,
7507
 
                                         &helper_directory, &user,
7508
 
                                         &group, &mandos_argz,
7509
 
                                         &mandos_argz_length));
7510
 
 
7511
 
  g_assert_null(helper_directory);
7512
 
  g_assert_null(agent_directory);
7513
 
  g_assert_true(user == 0);
7514
 
  g_assert_true(group == 0);
7515
 
  g_assert_null(mandos_argz);
7516
 
  g_assert_true(mandos_argz_length == 0);
7517
 
 
7518
 
  for(char **arg = argv; *arg != NULL; arg++){
7519
 
    free(*arg);
7520
 
  }
7521
 
}
7522
 
 
7523
 
static
7524
 
void test_parse_arguments_group_zero_invalid(__attribute__((unused))
7525
 
                                             test_fixture *fixture,
7526
 
                                             __attribute__((unused))
7527
 
                                             gconstpointer user_data){
7528
 
  char *argv[] = {
7529
 
    strdup("prgname"),
7530
 
    strdup("--group"),
7531
 
    strdup("0"),
7532
 
    NULL };
7533
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7534
 
 
7535
 
  char *agent_directory = NULL;
7536
 
  __attribute__((cleanup(cleanup_string)))
7537
 
    char *helper_directory = NULL;
7538
 
  uid_t user = 0;
7539
 
  gid_t group = 0;
7540
 
  __attribute__((cleanup(cleanup_string)))
7541
 
    char *mandos_argz = NULL;
7542
 
  size_t mandos_argz_length = 0;
7543
 
 
7544
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
7545
 
                                         &agent_directory,
7546
 
                                         &helper_directory, &user,
7547
 
                                         &group, &mandos_argz,
7548
 
                                         &mandos_argz_length));
7549
 
 
7550
 
  g_assert_null(helper_directory);
7551
 
  g_assert_null(agent_directory);
7552
 
  g_assert_cmpuint((unsigned int)group, ==, 0);
7553
 
  g_assert_true(group == 0);
7554
 
  g_assert_null(mandos_argz);
7555
 
  g_assert_true(mandos_argz_length == 0);
7556
 
 
7557
 
  for(char **arg = argv; *arg != NULL; arg++){
7558
 
    free(*arg);
7559
 
  }
7560
 
}
7561
 
 
7562
 
static void test_parse_arguments_mandos_noargs(__attribute__((unused))
7563
 
                                               test_fixture *fixture,
7564
 
                                               __attribute__((unused))
7565
 
                                               gconstpointer
7566
 
                                               user_data){
7567
 
  char *argv[] = {
7568
 
    strdup("prgname"),
7569
 
    strdup("mandos-client"),
7570
 
    NULL };
7571
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7572
 
 
7573
 
  __attribute__((cleanup(cleanup_string)))
7574
 
    char *agent_directory = NULL;
7575
 
  __attribute__((cleanup(cleanup_string)))
7576
 
    char *helper_directory = NULL;
7577
 
  uid_t user = 0;
7578
 
  gid_t group = 0;
7579
 
  __attribute__((cleanup(cleanup_string)))
7580
 
    char *mandos_argz = NULL;
7581
 
  size_t mandos_argz_length = 0;
7582
 
 
7583
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7584
 
                                &helper_directory, &user, &group,
7585
 
                                &mandos_argz, &mandos_argz_length));
7586
 
 
7587
 
  g_assert_null(agent_directory);
7588
 
  g_assert_null(helper_directory);
7589
 
  g_assert_true(user == 0);
7590
 
  g_assert_true(group == 0);
7591
 
  g_assert_cmpstr(mandos_argz, ==, "mandos-client");
7592
 
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7593
 
                                            mandos_argz_length),
7594
 
                   ==, 1);
7595
 
 
7596
 
  for(char **arg = argv; *arg != NULL; arg++){
7597
 
    free(*arg);
7598
 
  }
7599
 
}
7600
 
 
7601
 
static void test_parse_arguments_mandos_args(__attribute__((unused))
7602
 
                                             test_fixture *fixture,
7603
 
                                             __attribute__((unused))
7604
 
                                             gconstpointer user_data){
7605
 
  char *argv[] = {
7606
 
    strdup("prgname"),
7607
 
    strdup("mandos-client"),
7608
 
    strdup("one"),
7609
 
    strdup("two"),
7610
 
    strdup("three"),
7611
 
    NULL };
7612
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7613
 
 
7614
 
  __attribute__((cleanup(cleanup_string)))
7615
 
    char *agent_directory = NULL;
7616
 
  __attribute__((cleanup(cleanup_string)))
7617
 
    char *helper_directory = NULL;
7618
 
  uid_t user = 0;
7619
 
  gid_t group = 0;
7620
 
  __attribute__((cleanup(cleanup_string)))
7621
 
    char *mandos_argz = NULL;
7622
 
  size_t mandos_argz_length = 0;
7623
 
 
7624
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7625
 
                                &helper_directory, &user, &group,
7626
 
                                &mandos_argz, &mandos_argz_length));
7627
 
 
7628
 
  g_assert_null(agent_directory);
7629
 
  g_assert_null(helper_directory);
7630
 
  g_assert_true(user == 0);
7631
 
  g_assert_true(group == 0);
7632
 
  char *marg = mandos_argz;
7633
 
  g_assert_cmpstr(marg, ==, "mandos-client");
7634
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
7635
 
  g_assert_cmpstr(marg, ==, "one");
7636
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
7637
 
  g_assert_cmpstr(marg, ==, "two");
7638
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
7639
 
  g_assert_cmpstr(marg, ==, "three");
7640
 
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7641
 
                                            mandos_argz_length),
7642
 
                   ==, 4);
7643
 
 
7644
 
  for(char **arg = argv; *arg != NULL; arg++){
7645
 
    free(*arg);
7646
 
  }
7647
 
}
7648
 
 
7649
 
static void test_parse_arguments_all_args(__attribute__((unused))
7650
 
                                          test_fixture *fixture,
7651
 
                                          __attribute__((unused))
7652
 
                                          gconstpointer user_data){
7653
 
  char *argv[] = {
7654
 
    strdup("prgname"),
7655
 
    strdup("--agent-directory"),
7656
 
    strdup("/tmp"),
7657
 
    strdup("--helper-directory"),
7658
 
    strdup("/var/tmp"),
7659
 
    strdup("--user"),
7660
 
    strdup("1"),
7661
 
    strdup("--group"),
7662
 
    strdup("2"),
7663
 
    strdup("mandos-client"),
7664
 
    strdup("one"),
7665
 
    strdup("two"),
7666
 
    strdup("three"),
7667
 
    NULL };
7668
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7669
 
 
7670
 
  __attribute__((cleanup(cleanup_string)))
7671
 
    char *agent_directory = NULL;
7672
 
  __attribute__((cleanup(cleanup_string)))
7673
 
    char *helper_directory = NULL;
7674
 
  uid_t user = 0;
7675
 
  gid_t group = 0;
7676
 
  __attribute__((cleanup(cleanup_string)))
7677
 
    char *mandos_argz = NULL;
7678
 
  size_t mandos_argz_length = 0;
7679
 
 
7680
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7681
 
                                &helper_directory, &user, &group,
7682
 
                                &mandos_argz, &mandos_argz_length));
7683
 
 
7684
 
  g_assert_cmpstr(agent_directory, ==, "/tmp");
7685
 
  g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7686
 
  g_assert_true(user == 1);
7687
 
  g_assert_true(group == 2);
7688
 
  char *marg = mandos_argz;
7689
 
  g_assert_cmpstr(marg, ==, "mandos-client");
7690
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
7691
 
  g_assert_cmpstr(marg, ==, "one");
7692
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
7693
 
  g_assert_cmpstr(marg, ==, "two");
7694
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
7695
 
  g_assert_cmpstr(marg, ==, "three");
7696
 
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7697
 
                                            mandos_argz_length),
7698
 
                   ==, 4);
7699
 
 
7700
 
  for(char **arg = argv; *arg != NULL; arg++){
7701
 
    free(*arg);
7702
 
  }
7703
 
}
7704
 
 
7705
 
static void test_parse_arguments_mixed(__attribute__((unused))
7706
 
                                       test_fixture *fixture,
7707
 
                                       __attribute__((unused))
7708
 
                                       gconstpointer user_data){
7709
 
  char *argv[] = {
7710
 
    strdup("prgname"),
7711
 
    strdup("mandos-client"),
7712
 
    strdup("--user"),
7713
 
    strdup("1"),
7714
 
    strdup("one"),
7715
 
    strdup("--agent-directory"),
7716
 
    strdup("/tmp"),
7717
 
    strdup("two"),
7718
 
    strdup("three"),
7719
 
    strdup("--helper-directory=/var/tmp"),
7720
 
    NULL };
7721
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
7722
 
 
7723
 
  __attribute__((cleanup(cleanup_string)))
7724
 
    char *agent_directory = NULL;
7725
 
  __attribute__((cleanup(cleanup_string)))
7726
 
    char *helper_directory = NULL;
7727
 
  uid_t user = 0;
7728
 
  gid_t group = 0;
7729
 
  __attribute__((cleanup(cleanup_string)))
7730
 
    char *mandos_argz = NULL;
7731
 
  size_t mandos_argz_length = 0;
7732
 
 
7733
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
7734
 
                                &helper_directory, &user, &group,
7735
 
                                &mandos_argz, &mandos_argz_length));
7736
 
 
7737
 
  g_assert_cmpstr(agent_directory, ==, "/tmp");
7738
 
  g_assert_cmpstr(helper_directory, ==, "/var/tmp");
7739
 
  g_assert_true(user == 1);
7740
 
  g_assert_true(group == 0);
7741
 
  char *marg = mandos_argz;
7742
 
  g_assert_cmpstr(marg, ==, "mandos-client");
7743
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
7744
 
  g_assert_cmpstr(marg, ==, "one");
7745
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
7746
 
  g_assert_cmpstr(marg, ==, "two");
7747
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
7748
 
  g_assert_cmpstr(marg, ==, "three");
7749
 
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
7750
 
                                            mandos_argz_length),
7751
 
                   ==, 4);
7752
 
 
7753
 
  for(char **arg = argv; *arg != NULL; arg++){
7754
 
    free(*arg);
7755
 
  }
7756
 
}
7757
 
 
7758
 
/* End of tests section */
7759
 
 
7760
 
/* Test boilerplate section; New tests should be added to the test
7761
 
   suite definition here, in the "run_tests" function.
7762
 
 
7763
 
   Finally, this section also contains the should_only_run_tests()
7764
 
   function used by main() for deciding if tests should be run or to
7765
 
   start normally. */
7766
 
 
7767
 
__attribute__((cold))
7768
 
static bool run_tests(int argc, char *argv[]){
7769
 
  g_test_init(&argc, &argv, NULL);
7770
 
 
7771
 
  /* A macro to add a test with no setup or teardown functions */
7772
 
#define test_add(testpath, testfunc)                    \
7773
 
  do {                                                  \
7774
 
    g_test_add((testpath), test_fixture, NULL, NULL,    \
7775
 
               (testfunc), NULL);                       \
7776
 
  } while(false)
7777
 
 
7778
 
  /* Test the signal-related functions first, since some other tests
7779
 
     depend on these functions in their setups and teardowns */
7780
 
  test_add("/signal-handling/setup", test_setup_signal_handler);
7781
 
  test_add("/signal-handling/restore", test_restore_signal_handler);
7782
 
  test_add("/signal-handling/block", test_block_sigchld);
7783
 
  test_add("/signal-handling/restore-sigmask", test_restore_sigmask);
7784
 
 
7785
 
  /* Regular non-signal-related tests; these use no setups or
7786
 
     teardowns */
7787
 
  test_add("/parse_arguments/noargs", test_parse_arguments_noargs);
7788
 
  test_add("/parse_arguments/invalid", test_parse_arguments_invalid);
7789
 
  test_add("/parse_arguments/long-dir",
7790
 
           test_parse_arguments_long_dir);
7791
 
  test_add("/parse_arguments/short-dir",
7792
 
           test_parse_arguments_short_dir);
7793
 
  test_add("/parse_arguments/helper-directory",
7794
 
           test_parse_arguments_helper_directory);
7795
 
  test_add("/parse_arguments/plugin-helper-dir",
7796
 
           test_parse_arguments_plugin_helper_dir);
7797
 
  test_add("/parse_arguments/user", test_parse_arguments_user);
7798
 
  test_add("/parse_arguments/user-invalid",
7799
 
           test_parse_arguments_user_invalid);
7800
 
  test_add("/parse_arguments/user-zero-invalid",
7801
 
           test_parse_arguments_user_zero_invalid);
7802
 
  test_add("/parse_arguments/group", test_parse_arguments_group);
7803
 
  test_add("/parse_arguments/group-invalid",
7804
 
           test_parse_arguments_group_invalid);
7805
 
  test_add("/parse_arguments/group-zero-invalid",
7806
 
           test_parse_arguments_group_zero_invalid);
7807
 
  test_add("/parse_arguments/mandos-noargs",
7808
 
           test_parse_arguments_mandos_noargs);
7809
 
  test_add("/parse_arguments/mandos-args",
7810
 
           test_parse_arguments_mandos_args);
7811
 
  test_add("/parse_arguments/all-args",
7812
 
           test_parse_arguments_all_args);
7813
 
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7814
 
  test_add("/queue/create", test_create_queue);
7815
 
  test_add("/queue/add", test_add_to_queue);
7816
 
  test_add("/queue/has_question/empty",
7817
 
           test_queue_has_question_empty);
7818
 
  test_add("/queue/has_question/false",
7819
 
           test_queue_has_question_false);
7820
 
  test_add("/queue/has_question/true", test_queue_has_question_true);
7821
 
  test_add("/queue/has_question/false2",
7822
 
           test_queue_has_question_false2);
7823
 
  test_add("/queue/has_question/true2",
7824
 
           test_queue_has_question_true2);
7825
 
  test_add("/buffer/cleanup", test_cleanup_buffer);
7826
 
  test_add("/string_set/net-set-contains-nothing",
7827
 
           test_string_set_new_set_contains_nothing);
7828
 
  test_add("/string_set/with-added-string-contains-it",
7829
 
           test_string_set_with_added_string_contains_it);
7830
 
  test_add("/string_set/cleared-does-not-contain-string",
7831
 
           test_string_set_cleared_does_not_contain_str);
7832
 
  test_add("/string_set/swap/one-with-empty",
7833
 
           test_string_set_swap_one_with_empty);
7834
 
  test_add("/string_set/swap/empty-with-one",
7835
 
           test_string_set_swap_empty_with_one);
7836
 
  test_add("/string_set/swap/one-with-one",
7837
 
           test_string_set_swap_one_with_one);
7838
 
 
7839
 
  /* A macro to add a test using the setup and teardown functions */
7840
 
#define test_add_st(path, func)                                 \
7841
 
  do {                                                          \
7842
 
    g_test_add((path), test_fixture, NULL, test_setup, (func),  \
7843
 
               test_teardown);                                  \
7844
 
  } while(false)
7845
 
 
7846
 
  /* Signal-related tests; these use setups and teardowns which
7847
 
     establish, during each test run, a signal handler for, and a
7848
 
     signal mask blocking, the SIGCHLD signal, just like main() */
7849
 
  test_add_st("/wait_for_event/timeout", test_wait_for_event_timeout);
7850
 
  test_add_st("/wait_for_event/event", test_wait_for_event_event);
7851
 
  test_add_st("/wait_for_event/sigchld", test_wait_for_event_sigchld);
7852
 
  test_add_st("/run_queue/zeroes-next-run",
7853
 
              test_run_queue_zeroes_next_run);
7854
 
  test_add_st("/run_queue/clears-cancelled_filenames",
7855
 
              test_run_queue_clears_cancelled_filenames);
7856
 
  test_add_st("/run_queue/skips-cancelled-filenames",
7857
 
              test_run_queue_skips_cancelled_filenames);
7858
 
  test_add_st("/run_queue/one-task", test_run_queue_one_task);
7859
 
  test_add_st("/run_queue/two-tasks", test_run_queue_two_tasks);
7860
 
  test_add_st("/run_queue/two-tasks/quit",
7861
 
              test_run_queue_two_tasks_quit);
7862
 
  test_add_st("/run_queue/two-tasks-cleanup",
7863
 
              test_run_queue_two_tasks_cleanup);
7864
 
  test_add_st("/task-creators/start_mandos_client",
7865
 
              test_start_mandos_client);
7866
 
  test_add_st("/task-creators/start_mandos_client/execv",
7867
 
              test_start_mandos_client_execv);
7868
 
  test_add_st("/task-creators/start_mandos_client/suid/euid",
7869
 
              test_start_mandos_client_suid_euid);
7870
 
  test_add_st("/task-creators/start_mandos_client/suid/egid",
7871
 
              test_start_mandos_client_suid_egid);
7872
 
  test_add_st("/task-creators/start_mandos_client/suid/ruid",
7873
 
              test_start_mandos_client_suid_ruid);
7874
 
  test_add_st("/task-creators/start_mandos_client/suid/rgid",
7875
 
              test_start_mandos_client_suid_rgid);
7876
 
  test_add_st("/task-creators/start_mandos_client/read",
7877
 
              test_start_mandos_client_read);
7878
 
  test_add_st("/task-creators/start_mandos_client/helper-directory",
7879
 
              test_start_mandos_client_helper_directory);
7880
 
  test_add_st("/task-creators/start_mandos_client/sigmask",
7881
 
              test_start_mandos_client_sigmask);
7882
 
  test_add_st("/task/wait_for_mandos_client_exit/badpid",
7883
 
              test_wait_for_mandos_client_exit_badpid);
7884
 
  test_add_st("/task/wait_for_mandos_client_exit/noexit",
7885
 
              test_wait_for_mandos_client_exit_noexit);
7886
 
  test_add_st("/task/wait_for_mandos_client_exit/success",
7887
 
              test_wait_for_mandos_client_exit_success);
7888
 
  test_add_st("/task/wait_for_mandos_client_exit/failure",
7889
 
              test_wait_for_mandos_client_exit_failure);
7890
 
  test_add_st("/task/wait_for_mandos_client_exit/killed",
7891
 
              test_wait_for_mandos_client_exit_killed);
7892
 
  test_add_st("/task/read_mandos_client_output/readerror",
7893
 
              test_read_mandos_client_output_readerror);
7894
 
  test_add_st("/task/read_mandos_client_output/nodata",
7895
 
              test_read_mandos_client_output_nodata);
7896
 
  test_add_st("/task/read_mandos_client_output/eof",
7897
 
              test_read_mandos_client_output_eof);
7898
 
  test_add_st("/task/read_mandos_client_output/once",
7899
 
              test_read_mandos_client_output_once);
7900
 
  test_add_st("/task/read_mandos_client_output/malloc",
7901
 
              test_read_mandos_client_output_malloc);
7902
 
  test_add_st("/task/read_mandos_client_output/append",
7903
 
              test_read_mandos_client_output_append);
7904
 
  test_add_st("/task-creators/add_inotify_dir_watch",
7905
 
              test_add_inotify_dir_watch);
7906
 
  test_add_st("/task-creators/add_inotify_dir_watch/fail",
7907
 
              test_add_inotify_dir_watch_fail);
7908
 
  test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
7909
 
              test_add_inotify_dir_watch_EAGAIN);
7910
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
7911
 
              test_add_inotify_dir_watch_IN_CLOSE_WRITE);
7912
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
7913
 
              test_add_inotify_dir_watch_IN_MOVED_TO);
7914
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
7915
 
              test_add_inotify_dir_watch_IN_MOVED_FROM);
7916
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
7917
 
              test_add_inotify_dir_watch_IN_EXCL_UNLINK);
7918
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
7919
 
              test_add_inotify_dir_watch_IN_DELETE);
7920
 
  test_add_st("/task/read_inotify_event/readerror",
7921
 
              test_read_inotify_event_readerror);
7922
 
  test_add_st("/task/read_inotify_event/bad-epoll",
7923
 
              test_read_inotify_event_bad_epoll);
7924
 
  test_add_st("/task/read_inotify_event/nodata",
7925
 
              test_read_inotify_event_nodata);
7926
 
  test_add_st("/task/read_inotify_event/eof",
7927
 
              test_read_inotify_event_eof);
7928
 
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE",
7929
 
              test_read_inotify_event_IN_CLOSE_WRITE);
7930
 
  test_add_st("/task/read_inotify_event/IN_MOVED_TO",
7931
 
              test_read_inotify_event_IN_MOVED_TO);
7932
 
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
7933
 
              test_read_inotify_event_IN_MOVED_FROM);
7934
 
  test_add_st("/task/read_inotify_event/IN_DELETE",
7935
 
              test_read_inotify_event_IN_DELETE);
7936
 
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
7937
 
              test_read_inotify_event_IN_CLOSE_WRITE_badname);
7938
 
  test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
7939
 
              test_read_inotify_event_IN_MOVED_TO_badname);
7940
 
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
7941
 
              test_read_inotify_event_IN_MOVED_FROM_badname);
7942
 
  test_add_st("/task/read_inotify_event/IN_DELETE/badname",
7943
 
              test_read_inotify_event_IN_DELETE_badname);
7944
 
  test_add_st("/task/open_and_parse_question/ENOENT",
7945
 
              test_open_and_parse_question_ENOENT);
7946
 
  test_add_st("/task/open_and_parse_question/EIO",
7947
 
              test_open_and_parse_question_EIO);
7948
 
  test_add_st("/task/open_and_parse_question/parse-error",
7949
 
              test_open_and_parse_question_parse_error);
7950
 
  test_add_st("/task/open_and_parse_question/nosocket",
7951
 
              test_open_and_parse_question_nosocket);
7952
 
  test_add_st("/task/open_and_parse_question/badsocket",
7953
 
              test_open_and_parse_question_badsocket);
7954
 
  test_add_st("/task/open_and_parse_question/nopid",
7955
 
              test_open_and_parse_question_nopid);
7956
 
  test_add_st("/task/open_and_parse_question/badpid",
7957
 
              test_open_and_parse_question_badpid);
7958
 
  test_add_st("/task/open_and_parse_question/noexist_pid",
7959
 
              test_open_and_parse_question_noexist_pid);
7960
 
  test_add_st("/task/open_and_parse_question/no-notafter",
7961
 
              test_open_and_parse_question_no_notafter);
7962
 
  test_add_st("/task/open_and_parse_question/bad-notafter",
7963
 
              test_open_and_parse_question_bad_notafter);
7964
 
  test_add_st("/task/open_and_parse_question/notafter-0",
7965
 
              test_open_and_parse_question_notafter_0);
7966
 
  test_add_st("/task/open_and_parse_question/notafter-1",
7967
 
              test_open_and_parse_question_notafter_1);
7968
 
  test_add_st("/task/open_and_parse_question/notafter-1-1",
7969
 
              test_open_and_parse_question_notafter_1_1);
7970
 
  test_add_st("/task/open_and_parse_question/notafter-1-2",
7971
 
              test_open_and_parse_question_notafter_1_2);
7972
 
  test_add_st("/task/open_and_parse_question/equal-notafter",
7973
 
              test_open_and_parse_question_equal_notafter);
7974
 
  test_add_st("/task/open_and_parse_question/late-notafter",
7975
 
              test_open_and_parse_question_late_notafter);
7976
 
  test_add_st("/task/cancel_old_question/0-1-2",
7977
 
              test_cancel_old_question_0_1_2);
7978
 
  test_add_st("/task/cancel_old_question/0-2-1",
7979
 
              test_cancel_old_question_0_2_1);
7980
 
  test_add_st("/task/cancel_old_question/1-2-3",
7981
 
              test_cancel_old_question_1_2_3);
7982
 
  test_add_st("/task/cancel_old_question/1-3-2",
7983
 
              test_cancel_old_question_1_3_2);
7984
 
  test_add_st("/task/cancel_old_question/2-1-3",
7985
 
              test_cancel_old_question_2_1_3);
7986
 
  test_add_st("/task/cancel_old_question/2-3-1",
7987
 
              test_cancel_old_question_2_3_1);
7988
 
  test_add_st("/task/cancel_old_question/3-1-2",
7989
 
              test_cancel_old_question_3_1_2);
7990
 
  test_add_st("/task/cancel_old_question/3-2-1",
7991
 
              test_cancel_old_question_3_2_1);
7992
 
  test_add_st("/task/connect_question_socket/name-too-long",
7993
 
              test_connect_question_socket_name_too_long);
7994
 
  test_add_st("/task/connect_question_socket/connect-fail",
7995
 
              test_connect_question_socket_connect_fail);
7996
 
  test_add_st("/task/connect_question_socket/bad-epoll",
7997
 
              test_connect_question_socket_bad_epoll);
7998
 
  test_add_st("/task/connect_question_socket/usable",
7999
 
              test_connect_question_socket_usable);
8000
 
  test_add_st("/task/send_password_to_socket/client-not-exited",
8001
 
              test_send_password_to_socket_client_not_exited);
8002
 
  test_add_st("/task/send_password_to_socket/password-not-read",
8003
 
              test_send_password_to_socket_password_not_read);
8004
 
  test_add_st("/task/send_password_to_socket/EMSGSIZE",
8005
 
              test_send_password_to_socket_EMSGSIZE);
8006
 
  test_add_st("/task/send_password_to_socket/retry",
8007
 
              test_send_password_to_socket_retry);
8008
 
  test_add_st("/task/send_password_to_socket/bad-epoll",
8009
 
              test_send_password_to_socket_bad_epoll);
8010
 
  test_add_st("/task/send_password_to_socket/null-password",
8011
 
              test_send_password_to_socket_null_password);
8012
 
  test_add_st("/task/send_password_to_socket/empty-password",
8013
 
              test_send_password_to_socket_empty_password);
8014
 
  test_add_st("/task/send_password_to_socket/empty-str-password",
8015
 
              test_send_password_to_socket_empty_str_pass);
8016
 
  test_add_st("/task/send_password_to_socket/text-password",
8017
 
              test_send_password_to_socket_text_password);
8018
 
  test_add_st("/task/send_password_to_socket/binary-password",
8019
 
              test_send_password_to_socket_binary_password);
8020
 
  test_add_st("/task/send_password_to_socket/nuls-in-password",
8021
 
              test_send_password_to_socket_nuls_in_password);
8022
 
  test_add_st("/task-creators/add_existing_questions/ENOENT",
8023
 
              test_add_existing_questions_ENOENT);
8024
 
  test_add_st("/task-creators/add_existing_questions/no-questions",
8025
 
              test_add_existing_questions_no_questions);
8026
 
  test_add_st("/task-creators/add_existing_questions/one-question",
8027
 
              test_add_existing_questions_one_question);
8028
 
  test_add_st("/task-creators/add_existing_questions/two-questions",
8029
 
              test_add_existing_questions_two_questions);
8030
 
  test_add_st("/task-creators/add_existing_questions/non-questions",
8031
 
              test_add_existing_questions_non_questions);
8032
 
  test_add_st("/task-creators/add_existing_questions/both-types",
8033
 
              test_add_existing_questions_both_types);
8034
 
 
8035
 
  return g_test_run() == 0;
8036
 
}
8037
 
 
8038
 
static bool should_only_run_tests(int *argc_p, char **argv_p[]){
8039
 
  GOptionContext *context = g_option_context_new("");
8040
 
 
8041
 
  g_option_context_set_help_enabled(context, FALSE);
8042
 
  g_option_context_set_ignore_unknown_options(context, TRUE);
8043
 
 
8044
 
  gboolean run_tests = FALSE;
8045
 
  GOptionEntry entries[] = {
8046
 
    { "test", 0, 0, G_OPTION_ARG_NONE,
8047
 
      &run_tests, "Run tests", NULL },
8048
 
    { NULL }
8049
 
  };
8050
 
  g_option_context_add_main_entries(context, entries, NULL);
8051
 
 
8052
 
  GError *error = NULL;
8053
 
 
8054
 
  if(g_option_context_parse(context, argc_p, argv_p, &error) != TRUE){
8055
 
    g_option_context_free(context);
8056
 
    g_error("Failed to parse options: %s", error->message);
8057
 
  }
8058
 
 
8059
 
  g_option_context_free(context);
8060
 
  return run_tests != FALSE;
8061
 
}