/mandos/release

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

« back to all changes in this revision

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

  • Committer: teddy at recompile
  • Date: 2020-02-05 20:32:33 UTC
  • mto: This revision was merged to the branch mainline in revision 396.
  • Revision ID: teddy@recompile.se-20200205203233-450ojm36jseglq4m
Server: Stagger checker runs when creating clients

To avoid checkers for all clients all running at the same time
periodically, schedule every initially scheduled future checker to run
at a time in the future a random amount of the interval, from the
current time.

* mandos (Client.init_checker): Schedule the first scheduled future
  run of a checker to be a randomly chosen amount of this client's
  "interval" (instead of a full interval).

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