/mandos/release

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

« back to all changes in this revision

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

  • Committer: Teddy Hogeborn
  • Date: 2019-08-03 12:27:14 UTC
  • mto: This revision was merged to the branch mainline in revision 386.
  • Revision ID: teddy@recompile.se-20190803122714-efudxsyomr1k0zbt
dracut-module/password-agent.c: Use O_NOCTTY

Whenever a file is opened using open(), use the O_NOCTTY flag, just to
be safe.

* dracut-module/password-agent.c (test_start_mandos_client_execv): Use
  O_NOCTTY.
  (test_wait_for_mandos_client_exit_failure): - '' -
  (test_wait_for_mandos_client_exit_killed): - '' -
  (test_read_mandos_client_output_readerror): - '' -
  (test_read_inotify_event_readerror): - '' -
  (test_connect_question_socket_bad_epoll): - '' -
  (test_send_password_to_socket_bad_epoll): - '' -
  (assert_add_existing_questions_to_devnull): - '' -

Show diffs side-by-side

added added

removed removed

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