/mandos/release

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

« back to all changes in this revision

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

  • Committer: Teddy Hogeborn
  • Date: 2019-08-03 11:39:38 UTC
  • mto: This revision was merged to the branch mainline in revision 386.
  • Revision ID: teddy@recompile.se-20190803113938-httj8rqsnct08txx
dracut-module/password-agent.c: Bug fix: Ignore deleted files

If a question file ("ask.*") is opened, deleted, and then closed, we
still get an IN_CLOSE_WRITE ievent after the IN_DELETE ievent.  We
should pass the IN_EXCL_UNLINK flag to inotify_add_watch() to avoid
this.

* dracut-module/password-agent.c (add_inotify_dir_watch): Add
  "IN_EXCL_UNLINK" flag to inotify_add_watch().
  (test_add_inotify_dir_watch_IN_EXCL_UNLINK): New test.
  (run_tests): Add new test.

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
}