/mandos/trunk

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

« back to all changes in this revision

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

  • Committer: teddy at recompile
  • Date: 2020-02-09 17:57:08 UTC
  • Revision ID: teddy@recompile.se-20200209175708-bmprnuxqmr2lkhfh
initramfs-tools-hook: Properly quote argument to test -n

* initramfs-tools-hook: Quote output from subshell so that test -n
  always get exactly one argument, never more than one.

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