1
 
/* -*- coding: utf-8; lexical-binding: t -*- */
 
3
 
 * Mandos password agent - Simple password agent to run Mandos client
 
5
 
 * Copyright © 2019-2022 Teddy Hogeborn
 
6
 
 * Copyright © 2019-2022 Björn Påhlsson
 
8
 
 * This file is part of Mandos.
 
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.
 
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.
 
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/>.
 
23
 
 * Contact the authors at <mandos@recompile.se>.
 
26
 
#define _GNU_SOURCE             /* pipe2(), O_CLOEXEC, setresgid(),
 
27
 
                                   setresuid(), asprintf(), getline(),
 
29
 
#include <inttypes.h>           /* uintmax_t, strtoumax(), PRIuMAX,
 
30
 
                                   PRIdMAX, intmax_t, uint32_t,
 
31
 
                                   SCNx32, SCNuMAX, SCNxMAX */
 
32
 
#include <stddef.h>             /* size_t, NULL */
 
33
 
#include <sys/types.h>          /* pid_t, uid_t, gid_t, getuid(),
 
35
 
#include <stdbool.h>            /* bool, true, false */
 
36
 
#include <signal.h>             /* struct sigaction, sigset_t,
 
37
 
                                   sigemptyset(), sigaddset(),
 
38
 
                                   SIGCHLD, pthread_sigmask(),
 
39
 
                                   SIG_BLOCK, SIG_SETMASK, SA_RESTART,
 
40
 
                                   SA_NOCLDSTOP, sigfillset(), kill(),
 
41
 
                                   SIGTERM, sigdelset(), SIGKILL,
 
42
 
                                   NSIG, sigismember(), SA_ONSTACK,
 
43
 
                                   SIG_DFL, SIG_IGN, SIGINT, SIGQUIT,
 
44
 
                                   SIGHUP, SIGSTOP, SIG_UNBLOCK */
 
45
 
#include <unistd.h>             /* uid_t, gid_t, close(), pipe2(),
 
46
 
                                   fork(), _exit(), dup2(),
 
47
 
                                   STDOUT_FILENO, setresgid(),
 
48
 
                                   setresuid(), execv(), ssize_t,
 
49
 
                                   read(), dup3(), getuid(), dup(),
 
50
 
                                   STDERR_FILENO, pause(), write(),
 
51
 
                                   rmdir(), unlink(), getpid() */
 
52
 
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE,
 
53
 
                                   malloc(), free(), realloc(),
 
54
 
                                   setenv(), calloc(), mkdtemp(),
 
56
 
#include <iso646.h>             /* not, or, and, xor */
 
57
 
#include <error.h>              /* error() */
 
58
 
#include <sysexits.h>           /* EX_USAGE, EX_OSERR, EX_OSFILE */
 
59
 
#include <errno.h>              /* errno, error_t, EACCES,
 
60
 
                                   ENAMETOOLONG, ENOENT, ENOTDIR,
 
61
 
                                   ENOMEM, EEXIST, ECHILD, EPERM,
 
62
 
                                   EAGAIN, EINTR, ENOBUFS, EADDRINUSE,
 
63
 
                                   ECONNREFUSED, ECONNRESET,
 
64
 
                                   ETOOMANYREFS, EMSGSIZE, EBADF,
 
66
 
#include <string.h>             /* strdup(), memcpy(),
 
67
 
                                   explicit_bzero(), memset(),
 
68
 
                                   strcmp(), strlen(), strncpy(),
 
69
 
                                   memcmp(), basename(), strerror() */
 
70
 
#include <argz.h>               /* argz_create(), argz_count(),
 
71
 
                                   argz_extract(), argz_next(),
 
73
 
#include <sys/epoll.h>          /* epoll_create1(), EPOLL_CLOEXEC,
 
74
 
                                   epoll_ctl(), EPOLL_CTL_ADD,
 
75
 
                                   struct epoll_event, EPOLLIN,
 
78
 
#include <time.h>               /* struct timespec, clock_gettime(),
 
80
 
#include <argp.h>               /* struct argp_option, OPTION_HIDDEN,
 
81
 
                                   OPTION_ALIAS, struct argp_state,
 
82
 
                                   ARGP_ERR_UNKNOWN, ARGP_KEY_ARGS,
 
83
 
                                   struct argp, argp_parse(),
 
85
 
#include <stdint.h>             /* SIZE_MAX, uint32_t */
 
86
 
#include <sys/mman.h>           /* munlock(), mlock() */
 
87
 
#include <fcntl.h>              /* O_CLOEXEC, O_NONBLOCK, fcntl(),
 
88
 
                                   F_GETFD, F_GETFL, FD_CLOEXEC,
 
89
 
                                   open(), O_WRONLY, O_NOCTTY,
 
90
 
                                   O_RDONLY, O_NOFOLLOW */
 
91
 
#include <sys/wait.h>           /* waitpid(), WNOHANG, WIFEXITED(),
 
93
 
#include <limits.h>             /* PIPE_BUF, NAME_MAX, INT_MAX */
 
94
 
#include <sys/inotify.h>        /* inotify_init1(), IN_NONBLOCK,
 
95
 
                                   IN_CLOEXEC, inotify_add_watch(),
 
96
 
                                   IN_CLOSE_WRITE, IN_MOVED_TO,
 
97
 
                                   IN_MOVED_FROM, IN_DELETE,
 
98
 
                                   IN_EXCL_UNLINK, IN_ONLYDIR,
 
99
 
                                   struct inotify_event */
 
100
 
#include <fnmatch.h>            /* fnmatch(), FNM_FILE_NAME */
 
101
 
#include <stdio.h>              /* asprintf(), FILE, stderr, fopen(),
 
102
 
                                   fclose(), getline(), sscanf(),
 
103
 
                                   feof(), ferror(), rename(),
 
104
 
                                   fdopen(), fprintf(), fscanf() */
 
105
 
#include <glib.h>    /* GKeyFile, g_key_file_free(), g_key_file_new(),
 
106
 
                        GError, g_key_file_load_from_file(),
 
107
 
                        G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
 
108
 
                        g_key_file_get_string(), guint64,
 
109
 
                        g_key_file_get_uint64(),
 
110
 
                        G_KEY_FILE_ERROR_KEY_NOT_FOUND, gconstpointer,
 
111
 
                        g_assert_true(), g_assert_nonnull(),
 
112
 
                        g_assert_null(), g_assert_false(),
 
113
 
                        g_assert_cmpint(), g_assert_cmpuint(),
 
114
 
                        g_test_skip(), g_assert_cmpstr(),
 
115
 
                        g_test_message(), g_test_init(), g_test_add(),
 
116
 
                        g_test_run(), GOptionContext,
 
117
 
                        g_option_context_new(),
 
118
 
                        g_option_context_set_help_enabled(), FALSE,
 
119
 
                        g_option_context_set_ignore_unknown_options(),
 
120
 
                        gboolean, GOptionEntry, G_OPTION_ARG_NONE,
 
121
 
                        g_option_context_add_main_entries(),
 
122
 
                        g_option_context_parse(),
 
123
 
                        g_option_context_free(), g_error() */
 
124
 
#include <sys/un.h>             /* struct sockaddr_un, SUN_LEN */
 
125
 
#include <sys/socket.h>         /* AF_LOCAL, socket(), PF_LOCAL,
 
126
 
                                   SOCK_DGRAM, SOCK_NONBLOCK,
 
127
 
                                   SOCK_CLOEXEC, connect(),
 
128
 
                                   struct sockaddr, socklen_t,
 
129
 
                                   shutdown(), SHUT_RD, send(),
 
130
 
                                   MSG_NOSIGNAL, bind(), recv(),
 
132
 
#include <glob.h>               /* globfree(), glob_t, glob(),
 
133
 
                                   GLOB_ERR, GLOB_NOSORT, GLOB_MARK,
 
134
 
                                   GLOB_ABORTED, GLOB_NOMATCH,
 
137
 
/* End of includes */
 
139
 
/* Start of declarations of private types and functions */
 
141
 
/* microseconds of CLOCK_MONOTONIC absolute time; 0 means unset */
 
142
 
typedef uintmax_t mono_microsecs;
 
144
 
/* "task_queue" - A queue of tasks to be run */
 
146
 
  struct task_struct *tasks;    /* Tasks in this queue */
 
147
 
  size_t length;                /* Number of tasks */
 
148
 
  /* Memory allocated for "tasks", in bytes */
 
150
 
  /* Time when this queue should be run, at the latest */
 
151
 
  mono_microsecs next_run;
 
152
 
} __attribute__((designated_init)) task_queue;
 
154
 
/* "task_func" - A function type for task functions
 
156
 
   I.e. functions for the code which runs when a task is run, all have
 
158
 
typedef void (task_func) (const struct task_struct,
 
160
 
  __attribute__((nonnull));
 
162
 
/* "buffer" - A data buffer for a growing array of bytes
 
164
 
   Used for the "password" variable */
 
169
 
} __attribute__((designated_init)) buffer;
 
171
 
/* "string_set" - A set type which can contain strings
 
173
 
   Used by the "cancelled_filenames" variable */
 
175
 
  char *argz;                   /* Do not access these except in */
 
176
 
  size_t argz_len;              /* the string_set_* functions */
 
177
 
} __attribute__((designated_init)) string_set;
 
179
 
/* "task_context" - local variables for tasks
 
181
 
   This data structure distinguishes between different tasks which are
 
182
 
   using the same function.  This data structure is passed to every
 
183
 
   task function when each task is run.
 
185
 
   Note that not every task uses every struct member. */
 
186
 
typedef struct task_struct {
 
187
 
  task_func *const func;         /* The function run by this task */
 
188
 
  char *const question_filename; /* The question file */
 
189
 
  const pid_t pid;               /* Mandos client process ID */
 
190
 
  const int epoll_fd;            /* The epoll set file descriptor */
 
191
 
  bool *const quit_now;          /* Set to true on fatal errors */
 
192
 
  const int fd;                  /* General purpose file descriptor */
 
193
 
  bool *const mandos_client_exited; /* Set true when client exits */
 
194
 
  buffer *const password;           /* As read from client process */
 
195
 
  bool *const password_is_read;     /* "password" is done growing */
 
196
 
  char *filename;                   /* General purpose file name */
 
197
 
  /* A set of strings of all the file names of questions which have
 
198
 
     been cancelled for any reason; tasks pertaining to these question
 
199
 
     files should not be run */
 
200
 
  string_set *const cancelled_filenames;
 
201
 
  const mono_microsecs notafter; /* "NotAfter" from question file */
 
202
 
  /* Updated before each queue run; is compared with queue.next_run */
 
203
 
  const mono_microsecs *const current_time;
 
204
 
} __attribute__((designated_init)) task_context;
 
206
 
/* Declare all our functions here so we can define them in any order
 
207
 
   below.  Note: test functions are *not* declared here, they are
 
208
 
   declared in the test section. */
 
209
 
__attribute__((warn_unused_result))
 
210
 
static bool should_only_run_tests(int *, char **[]);
 
211
 
__attribute__((warn_unused_result, cold))
 
212
 
static bool run_tests(int, char *[]);
 
213
 
static void handle_sigchld(__attribute__((unused)) int sig){}
 
214
 
__attribute__((warn_unused_result, malloc))
 
215
 
task_queue *create_queue(void);
 
216
 
__attribute__((nonnull, warn_unused_result))
 
217
 
bool add_to_queue(task_queue *const, const task_context);
 
218
 
__attribute__((nonnull))
 
219
 
void cleanup_task(const task_context *const);
 
220
 
__attribute__((nonnull))
 
221
 
void cleanup_queue(task_queue *const *const);
 
222
 
__attribute__((pure, nonnull, warn_unused_result))
 
223
 
bool queue_has_question(const task_queue *const);
 
224
 
__attribute__((nonnull))
 
225
 
void cleanup_close(const int *const);
 
226
 
__attribute__((nonnull))
 
227
 
void cleanup_string(char *const *const);
 
228
 
__attribute__((nonnull))
 
229
 
void cleanup_buffer(buffer *const);
 
230
 
__attribute__((pure, nonnull, warn_unused_result))
 
231
 
bool string_set_contains(const string_set, const char *const);
 
232
 
__attribute__((nonnull, warn_unused_result))
 
233
 
bool string_set_add(string_set *const, const char *const);
 
234
 
__attribute__((nonnull))
 
235
 
void string_set_clear(string_set *);
 
236
 
void string_set_swap(string_set *const, string_set *const);
 
237
 
__attribute__((nonnull, warn_unused_result))
 
238
 
bool start_mandos_client(task_queue *const, const int, bool *const,
 
239
 
                         bool *const, buffer *const, bool *const,
 
240
 
                         const struct sigaction *const,
 
241
 
                         const sigset_t, const char *const,
 
242
 
                         const uid_t, const gid_t,
 
243
 
                         const char *const *const);
 
244
 
__attribute__((nonnull))
 
245
 
task_func wait_for_mandos_client_exit;
 
246
 
__attribute__((nonnull))
 
247
 
task_func read_mandos_client_output;
 
248
 
__attribute__((warn_unused_result))
 
249
 
bool add_inotify_dir_watch(task_queue *const, const int, bool *const,
 
250
 
                           buffer *const, const char *const,
 
251
 
                           string_set *, const mono_microsecs *const,
 
252
 
                           bool *const, bool *const);
 
253
 
__attribute__((nonnull))
 
254
 
task_func read_inotify_event;
 
255
 
__attribute__((nonnull))
 
256
 
task_func open_and_parse_question;
 
257
 
__attribute__((nonnull))
 
258
 
task_func cancel_old_question;
 
259
 
__attribute__((nonnull))
 
260
 
task_func connect_question_socket;
 
261
 
__attribute__((nonnull))
 
262
 
task_func send_password_to_socket;
 
263
 
__attribute__((warn_unused_result))
 
264
 
bool add_existing_questions(task_queue *const, const int,
 
265
 
                            buffer *const, string_set *,
 
266
 
                            const mono_microsecs *const,
 
267
 
                            bool *const, bool *const,
 
269
 
__attribute__((nonnull, warn_unused_result))
 
270
 
bool wait_for_event(const int, const mono_microsecs,
 
271
 
                    const mono_microsecs);
 
272
 
bool run_queue(task_queue **const, string_set *const, bool *const);
 
273
 
bool clear_all_fds_from_epoll_set(const int);
 
274
 
mono_microsecs get_current_time(void);
 
275
 
__attribute__((nonnull, warn_unused_result))
 
276
 
bool setup_signal_handler(struct sigaction *const);
 
277
 
__attribute__((nonnull))
 
278
 
bool restore_signal_handler(const struct sigaction *const);
 
279
 
__attribute__((nonnull, warn_unused_result))
 
280
 
bool block_sigchld(sigset_t *const);
 
281
 
__attribute__((nonnull))
 
282
 
bool restore_sigmask(const sigset_t *const);
 
283
 
__attribute__((nonnull))
 
284
 
bool parse_arguments(int, char *[], const bool, char **, char **,
 
285
 
                     uid_t *const , gid_t *const, char **, size_t *);
 
287
 
/* End of declarations of private types and functions */
 
289
 
/* Start of "main" section; this section LACKS TESTS!
 
291
 
   Code here should be as simple as possible. */
 
293
 
/* These are required to be global by Argp */
 
294
 
const char *argp_program_version = "password-agent " VERSION;
 
295
 
const char *argp_program_bug_address = "<mandos@recompile.se>";
 
297
 
int main(int argc, char *argv[]){
 
299
 
  /* If the --test option is passed, skip all normal operations and
 
300
 
     instead only run the run_tests() function, which also does all
 
301
 
     its own option parsing, so we don't have to do anything here. */
 
302
 
  if(should_only_run_tests(&argc, &argv)){
 
303
 
    if(run_tests(argc, argv)){
 
304
 
      return EXIT_SUCCESS;      /* All tests successful */
 
306
 
    return EXIT_FAILURE;        /* Some test(s) failed */
 
309
 
  __attribute__((cleanup(cleanup_string)))
 
310
 
    char *agent_directory = NULL;
 
312
 
  __attribute__((cleanup(cleanup_string)))
 
313
 
    char *helper_directory = NULL;
 
318
 
  __attribute__((cleanup(cleanup_string)))
 
319
 
    char *mandos_argz = NULL;
 
320
 
  size_t mandos_argz_length = 0;
 
322
 
  if(not parse_arguments(argc, argv, true, &agent_directory,
 
323
 
                         &helper_directory, &user, &group,
 
324
 
                         &mandos_argz, &mandos_argz_length)){
 
325
 
    /* This should never happen, since "true" is passed as the third
 
326
 
       argument to parse_arguments() above, which should make
 
327
 
       argp_parse() call exit() if any parsing error occurs. */
 
328
 
    error(EX_USAGE, errno, "Failed to parse arguments");
 
331
 
  const char default_agent_directory[] = "/run/systemd/ask-password";
 
332
 
  const char default_helper_directory[]
 
333
 
    = "/lib/mandos/plugin-helpers";
 
334
 
  const char *const default_argv[]
 
335
 
    = {"/lib/mandos/plugins.d/mandos-client", NULL };
 
337
 
  /* Set variables to default values if unset */
 
338
 
  if(agent_directory == NULL){
 
339
 
    agent_directory = strdup(default_agent_directory);
 
340
 
    if(agent_directory == NULL){
 
341
 
      error(EX_OSERR, errno, "Failed strdup()");
 
344
 
  if(helper_directory == NULL){
 
345
 
    helper_directory = strdup(default_helper_directory);
 
346
 
    if(helper_directory == NULL){
 
347
 
      error(EX_OSERR, errno, "Failed strdup()");
 
351
 
    user = 65534;               /* nobody */
 
354
 
    group = 65534;              /* nogroup */
 
356
 
  /* If parse_opt did not create an argz vector, create one with
 
358
 
  if(mandos_argz == NULL){
 
360
 
#pragma GCC diagnostic push
 
361
 
    /* argz_create() takes a non-const argv for some unknown reason -
 
362
 
       argz_create() isn't modifying the strings, just copying them.
 
363
 
       Therefore, this cast to non-const should be safe. */
 
364
 
#pragma GCC diagnostic ignored "-Wcast-qual"
 
366
 
    errno = argz_create((char *const *)default_argv, &mandos_argz,
 
367
 
                        &mandos_argz_length);
 
369
 
#pragma GCC diagnostic pop
 
372
 
      error(EX_OSERR, errno, "Failed argz_create()");
 
375
 
  /* Use argz vector to create a normal argv, usable by execv() */
 
377
 
  char **mandos_argv = malloc((argz_count(mandos_argz,
 
379
 
                               + 1) * sizeof(char *));
 
380
 
  if(mandos_argv == NULL){
 
381
 
    error_t saved_errno = errno;
 
383
 
    error(EX_OSERR, saved_errno, "Failed malloc()");
 
385
 
  argz_extract(mandos_argz, mandos_argz_length, mandos_argv);
 
387
 
  sigset_t orig_sigmask;
 
388
 
  if(not block_sigchld(&orig_sigmask)){
 
392
 
  struct sigaction old_sigchld_action;
 
393
 
  if(not setup_signal_handler(&old_sigchld_action)){
 
397
 
  mono_microsecs current_time = 0;
 
399
 
  bool mandos_client_exited = false;
 
400
 
  bool quit_now = false;
 
401
 
  __attribute__((cleanup(cleanup_close)))
 
402
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
404
 
    error(EX_OSERR, errno, "Failed to create epoll set fd");
 
406
 
  __attribute__((cleanup(cleanup_queue)))
 
407
 
    task_queue *queue = create_queue();
 
409
 
    error(EX_OSERR, errno, "Failed to create task queue");
 
412
 
  __attribute__((cleanup(cleanup_buffer)))
 
413
 
    buffer password = {};
 
414
 
  bool password_is_read = false;
 
416
 
  __attribute__((cleanup(string_set_clear)))
 
417
 
    string_set cancelled_filenames = {};
 
419
 
  /* Add tasks to queue */
 
420
 
  if(not start_mandos_client(queue, epoll_fd, &mandos_client_exited,
 
421
 
                             &quit_now, &password, &password_is_read,
 
422
 
                             &old_sigchld_action, orig_sigmask,
 
423
 
                             helper_directory, user, group,
 
424
 
                             (const char *const *)mandos_argv)){
 
425
 
    return EX_OSERR;            /* Error has already been printed */
 
427
 
  /* These variables were only for start_mandos_client() and are not
 
432
 
  if(not add_inotify_dir_watch(queue, epoll_fd, &quit_now, &password,
 
433
 
                               agent_directory, &cancelled_filenames,
 
434
 
                               ¤t_time, &mandos_client_exited,
 
436
 
    switch(errno){              /* Error has already been printed */
 
446
 
  if(not add_existing_questions(queue, epoll_fd, &password,
 
447
 
                                &cancelled_filenames, ¤t_time,
 
448
 
                                &mandos_client_exited,
 
449
 
                                &password_is_read, agent_directory)){
 
450
 
    return EXIT_FAILURE;        /* Error has already been printed */
 
455
 
    current_time = get_current_time();
 
456
 
    if(not wait_for_event(epoll_fd, queue->next_run, current_time)){
 
457
 
      const error_t saved_errno = errno;
 
458
 
      error(EXIT_FAILURE, saved_errno, "Failure while waiting for"
 
462
 
    current_time = get_current_time();
 
463
 
    if(not run_queue(&queue, &cancelled_filenames, &quit_now)){
 
464
 
      const error_t saved_errno = errno;
 
465
 
      error(EXIT_FAILURE, saved_errno, "Failure while running queue");
 
468
 
    /*  When no tasks about questions are left in the queue, break out
 
469
 
        of the loop (and implicitly exit the program) */
 
470
 
  } while(queue_has_question(queue));
 
472
 
  restore_signal_handler(&old_sigchld_action);
 
473
 
  restore_sigmask(&orig_sigmask);
 
478
 
__attribute__((warn_unused_result))
 
479
 
mono_microsecs get_current_time(void){
 
480
 
  struct timespec currtime;
 
481
 
  if(clock_gettime(CLOCK_MONOTONIC, &currtime) != 0){
 
482
 
    error(0, errno, "Failed to get current time");
 
485
 
  return ((mono_microsecs)currtime.tv_sec * 1000000) /* seconds */
 
486
 
    + ((mono_microsecs)currtime.tv_nsec / 1000);     /* nanoseconds */
 
489
 
/* End of "main" section */
 
491
 
/* Start of regular code section; ALL this code has tests */
 
493
 
__attribute__((nonnull))
 
494
 
bool parse_arguments(int argc, char *argv[], const bool exit_failure,
 
495
 
                     char **agent_directory, char **helper_directory,
 
496
 
                     uid_t *const user, gid_t *const group,
 
497
 
                     char **mandos_argz, size_t *mandos_argz_length){
 
499
 
  const struct argp_option options[] = {
 
500
 
    { .name="agent-directory",.key='d', .arg="DIRECTORY",
 
501
 
      .doc="Systemd password agent directory" },
 
502
 
    { .name="helper-directory",.key=128, .arg="DIRECTORY",
 
503
 
      .doc="Mandos Client password helper directory" },
 
504
 
    { .name="plugin-helper-dir", .key=129, /* From plugin-runner */
 
505
 
      .flags=OPTION_HIDDEN | OPTION_ALIAS },
 
506
 
    { .name="user", .key='u', .arg="USERID",
 
507
 
      .doc="User ID the Mandos Client will use as its unprivileged"
 
509
 
    { .name="userid", .key=130, /* From plugin--runner */
 
510
 
      .flags=OPTION_HIDDEN | OPTION_ALIAS },
 
511
 
    { .name="group", .key='g', .arg="GROUPID",
 
512
 
      .doc="Group ID the Mandos Client will use as its unprivileged"
 
514
 
    { .name="groupid", .key=131, /* From plugin--runner */
 
515
 
      .flags=OPTION_HIDDEN | OPTION_ALIAS },
 
516
 
    { .name="test", .key=255, /* See should_only_run_tests() */
 
517
 
      .doc="Skip normal operation, and only run self-tests.  See"
 
518
 
      " --test --help.", .group=10, },
 
522
 
  __attribute__((nonnull(3)))
 
523
 
    error_t parse_opt(int key, char *arg, struct argp_state *state){
 
526
 
    case 'd':                   /* --agent-directory */
 
527
 
      *agent_directory = strdup(arg);
 
529
 
    case 128:                   /* --helper-directory */
 
530
 
    case 129:                   /* --plugin-helper-dir */
 
531
 
      *helper_directory = strdup(arg);
 
533
 
    case 'u':                   /* --user */
 
534
 
    case 130:                   /* --userid */
 
537
 
        uintmax_t tmp_id = 0;
 
539
 
        tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
 
540
 
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
541
 
           or tmp_id != (uid_t)tmp_id or (uid_t)tmp_id == 0){
 
542
 
          return ARGP_ERR_UNKNOWN;
 
544
 
        *user = (uid_t)tmp_id;
 
548
 
    case 'g':                   /* --group */
 
549
 
    case 131:                   /* --groupid */
 
552
 
        uintmax_t tmp_id = 0;
 
554
 
        tmp_id = (uid_t)strtoumax(arg, &tmp, 10);
 
555
 
        if(errno != 0 or tmp == arg or *tmp != '\0'
 
556
 
           or tmp_id != (gid_t)tmp_id or (gid_t)tmp_id == 0){
 
557
 
          return ARGP_ERR_UNKNOWN;
 
559
 
        *group = (gid_t)tmp_id;
 
564
 
      /* Copy arguments into argz vector */
 
565
 
      return argz_create(state->argv + state->next, mandos_argz,
 
568
 
      return ARGP_ERR_UNKNOWN;
 
573
 
  const struct argp argp = {
 
576
 
    .args_doc="[MANDOS_CLIENT [OPTION...]]\n--test",
 
577
 
    .doc = "Mandos password agent -- runs Mandos client as a"
 
578
 
    " systemd password agent",
 
581
 
  errno = argp_parse(&argp, argc, argv,
 
582
 
                     exit_failure ? 0 : ARGP_NO_EXIT, NULL, NULL);
 
587
 
__attribute__((nonnull, warn_unused_result))
 
588
 
bool block_sigchld(sigset_t *const orig_sigmask){
 
589
 
  sigset_t sigchld_sigmask;
 
590
 
  if(sigemptyset(&sigchld_sigmask) < 0){
 
591
 
    error(0, errno, "Failed to empty signal set");
 
594
 
  if(sigaddset(&sigchld_sigmask, SIGCHLD) < 0){
 
595
 
    error(0, errno, "Failed to add SIGCHLD to signal set");
 
598
 
  if(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask, orig_sigmask) != 0){
 
599
 
    error(0, errno, "Failed to block SIGCHLD signal");
 
605
 
__attribute__((nonnull, warn_unused_result, const))
 
606
 
bool restore_sigmask(const sigset_t *const orig_sigmask){
 
607
 
  if(pthread_sigmask(SIG_SETMASK, orig_sigmask, NULL) != 0){
 
608
 
    error(0, errno, "Failed to restore blocked signals");
 
614
 
__attribute__((nonnull, warn_unused_result))
 
615
 
bool setup_signal_handler(struct sigaction *const old_sigchld_action){
 
616
 
  struct sigaction sigchld_action = {
 
617
 
    .sa_handler=handle_sigchld,
 
618
 
    .sa_flags=SA_RESTART | SA_NOCLDSTOP,
 
620
 
  /* Set all signals in "sa_mask" struct member; this makes all
 
621
 
     signals automatically blocked during signal handler */
 
622
 
  if(sigfillset(&sigchld_action.sa_mask) != 0){
 
623
 
    error(0, errno, "Failed to do sigfillset()");
 
626
 
  if(sigaction(SIGCHLD, &sigchld_action, old_sigchld_action) != 0){
 
627
 
    error(0, errno, "Failed to set SIGCHLD signal handler");
 
633
 
__attribute__((nonnull, warn_unused_result))
 
634
 
bool restore_signal_handler(const struct sigaction *const
 
636
 
  if(sigaction(SIGCHLD, old_sigchld_action, NULL) != 0){
 
637
 
    error(0, errno, "Failed to restore signal handler");
 
643
 
__attribute__((warn_unused_result, malloc))
 
644
 
task_queue *create_queue(void){
 
645
 
  task_queue *queue = malloc(sizeof(task_queue));
 
649
 
    queue->allocated = 0;
 
655
 
__attribute__((nonnull, warn_unused_result))
 
656
 
bool add_to_queue(task_queue *const queue, const task_context task){
 
657
 
  if((queue->length + 1) > (SIZE_MAX / sizeof(task_context))){
 
659
 
    error(0, ENOMEM, "Failed to allocate %" PRIuMAX
 
660
 
          " tasks for queue->tasks", (uintmax_t)(queue->length + 1));
 
664
 
  const size_t needed_size = sizeof(task_context)*(queue->length + 1);
 
665
 
  if(needed_size > (queue->allocated)){
 
666
 
    task_context *const new_tasks = realloc(queue->tasks,
 
668
 
    if(new_tasks == NULL){
 
669
 
      error(0, errno, "Failed to allocate %" PRIuMAX
 
670
 
            " bytes for queue->tasks", (uintmax_t)needed_size);
 
673
 
    queue->tasks = new_tasks;
 
674
 
    queue->allocated = needed_size;
 
676
 
  /* Using memcpy here is necessary because doing */
 
677
 
  /* queue->tasks[queue->length++] = task; */
 
678
 
  /* would violate const-ness of task members */
 
679
 
  memcpy(&(queue->tasks[queue->length++]), &task,
 
680
 
         sizeof(task_context));
 
684
 
__attribute__((nonnull))
 
685
 
void cleanup_task(const task_context *const task){
 
686
 
  const error_t saved_errno = errno;
 
687
 
  /* free and close all task data */
 
688
 
  free(task->question_filename);
 
689
 
  if(task->filename != task->question_filename){
 
690
 
    free(task->filename);
 
693
 
    kill(task->pid, SIGTERM);
 
701
 
__attribute__((nonnull))
 
702
 
void free_queue(task_queue *const queue){
 
707
 
__attribute__((nonnull))
 
708
 
void cleanup_queue(task_queue *const *const queue){
 
712
 
  for(size_t i = 0; i < (*queue)->length; i++){
 
713
 
    const task_context *const task = ((*queue)->tasks)+i;
 
719
 
__attribute__((pure, nonnull, warn_unused_result))
 
720
 
bool queue_has_question(const task_queue *const queue){
 
721
 
  for(size_t i=0; i < queue->length; i++){
 
722
 
    if(queue->tasks[i].question_filename != NULL){
 
729
 
__attribute__((nonnull))
 
730
 
void cleanup_close(const int *const fd){
 
731
 
  const error_t saved_errno = errno;
 
736
 
__attribute__((nonnull))
 
737
 
void cleanup_string(char *const *const ptr){
 
741
 
__attribute__((nonnull))
 
742
 
void cleanup_buffer(buffer *buf){
 
743
 
  if(buf->allocated > 0){
 
744
 
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
 
745
 
    explicit_bzero(buf->data, buf->allocated);
 
747
 
    memset(buf->data, '\0', buf->allocated);
 
750
 
  if(buf->data != NULL){
 
751
 
    if(munlock(buf->data, buf->allocated) != 0){
 
752
 
      error(0, errno, "Failed to unlock memory of old buffer");
 
761
 
__attribute__((pure, nonnull, warn_unused_result))
 
762
 
bool string_set_contains(const string_set set, const char *const str){
 
763
 
  for(const char *s = set.argz; s != NULL and set.argz_len > 0;
 
764
 
      s = argz_next(set.argz, set.argz_len, s)){
 
765
 
    if(strcmp(s, str) == 0){
 
772
 
__attribute__((nonnull, warn_unused_result))
 
773
 
bool string_set_add(string_set *const set, const char *const str){
 
774
 
  if(string_set_contains(*set, str)){
 
777
 
  error_t error = argz_add(&set->argz, &set->argz_len, str);
 
785
 
__attribute__((nonnull))
 
786
 
void string_set_clear(string_set *set){
 
792
 
__attribute__((nonnull))
 
793
 
void string_set_swap(string_set *const set1, string_set *const set2){
 
794
 
  /* Swap contents of two string sets */
 
796
 
    char *const tmp_argz = set1->argz;
 
797
 
    set1->argz = set2->argz;
 
798
 
    set2->argz = tmp_argz;
 
801
 
    const size_t tmp_argz_len = set1->argz_len;
 
802
 
    set1->argz_len = set2->argz_len;
 
803
 
    set2->argz_len = tmp_argz_len;
 
807
 
__attribute__((nonnull, warn_unused_result))
 
808
 
bool start_mandos_client(task_queue *const queue,
 
810
 
                         bool *const mandos_client_exited,
 
811
 
                         bool *const quit_now, buffer *const password,
 
812
 
                         bool *const password_is_read,
 
813
 
                         const struct sigaction *const
 
814
 
                         old_sigchld_action, const sigset_t sigmask,
 
815
 
                         const char *const helper_directory,
 
816
 
                         const uid_t user, const gid_t group,
 
817
 
                         const char *const *const argv){
 
819
 
  if(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK) != 0){
 
820
 
    error(0, errno, "Failed to pipe2(..., O_CLOEXEC | O_NONBLOCK)");
 
824
 
  const pid_t pid = fork();
 
826
 
    if(not restore_signal_handler(old_sigchld_action)){
 
829
 
    if(not restore_sigmask(&sigmask)){
 
832
 
    if(close(pipefds[0]) != 0){
 
833
 
      error(0, errno, "Failed to close() parent pipe fd");
 
836
 
    if(dup2(pipefds[1], STDOUT_FILENO) == -1){
 
837
 
      error(0, errno, "Failed to dup2() pipe fd to stdout");
 
840
 
    if(close(pipefds[1]) != 0){
 
841
 
      error(0, errno, "Failed to close() old child pipe fd");
 
844
 
    if(setenv("MANDOSPLUGINHELPERDIR", helper_directory, 1) != 0){
 
845
 
      error(0, errno, "Failed to setenv(\"MANDOSPLUGINHELPERDIR\","
 
846
 
            " \"%s\", 1)", helper_directory);
 
849
 
    if(group != 0 and setresgid(group, 0, 0) == -1){
 
850
 
      error(0, errno, "Failed to setresgid(-1, %" PRIuMAX ", %"
 
851
 
            PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
 
854
 
    if(user != 0 and setresuid(user, 0, 0) == -1){
 
855
 
      error(0, errno, "Failed to setresuid(-1, %" PRIuMAX ", %"
 
856
 
            PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
 
860
 
#pragma GCC diagnostic push
 
861
 
    /* For historical reasons, the "argv" argument to execv() is not
 
862
 
       const, but it is safe to override this. */
 
863
 
#pragma GCC diagnostic ignored "-Wcast-qual"
 
865
 
    execv(argv[0], (char **)argv);
 
867
 
#pragma GCC diagnostic pop
 
869
 
    error(0, errno, "execv(\"%s\", ...) failed", argv[0]);
 
875
 
    error(0, errno, "Failed to fork()");
 
880
 
  if(not add_to_queue(queue, (task_context){
 
881
 
        .func=wait_for_mandos_client_exit,
 
883
 
        .mandos_client_exited=mandos_client_exited,
 
886
 
    error(0, errno, "Failed to add wait_for_mandos_client to queue");
 
891
 
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, pipefds[0],
 
892
 
                            &(struct epoll_event)
 
893
 
                            { .events=EPOLLIN | EPOLLRDHUP });
 
894
 
  if(ret != 0 and errno != EEXIST){
 
895
 
    error(0, errno, "Failed to add file descriptor to epoll set");
 
900
 
  return add_to_queue(queue, (task_context){
 
901
 
      .func=read_mandos_client_output,
 
906
 
      .password_is_read=password_is_read,
 
910
 
__attribute__((nonnull))
 
911
 
void wait_for_mandos_client_exit(const task_context task,
 
912
 
                                 task_queue *const queue){
 
913
 
  const pid_t pid = task.pid;
 
914
 
  bool *const mandos_client_exited = task.mandos_client_exited;
 
915
 
  bool *const quit_now = task.quit_now;
 
918
 
  switch(waitpid(pid, &status, WNOHANG)){
 
919
 
  case 0:                       /* Not exited yet */
 
920
 
    if(not add_to_queue(queue, task)){
 
921
 
      error(0, errno, "Failed to add myself to queue");
 
926
 
    error(0, errno, "waitpid(%" PRIdMAX ") failed", (intmax_t)pid);
 
932
 
  default:                      /* Has exited */
 
933
 
    *mandos_client_exited = true;
 
934
 
    if((not WIFEXITED(status))
 
935
 
       or (WEXITSTATUS(status) != EXIT_SUCCESS)){
 
936
 
      error(0, 0, "Mandos client failed or was killed");
 
942
 
__attribute__((nonnull))
 
943
 
void read_mandos_client_output(const task_context task,
 
944
 
                               task_queue *const queue){
 
945
 
  buffer *const password = task.password;
 
946
 
  bool *const quit_now = task.quit_now;
 
947
 
  bool *const password_is_read = task.password_is_read;
 
948
 
  const int fd = task.fd;
 
949
 
  const int epoll_fd = task.epoll_fd;
 
951
 
  const size_t new_potential_size = (password->length + PIPE_BUF);
 
952
 
  if(password->allocated < new_potential_size){
 
953
 
    char *const new_buffer = calloc(new_potential_size, 1);
 
954
 
    if(new_buffer == NULL){
 
955
 
      error(0, errno, "Failed to allocate %" PRIuMAX
 
956
 
            " bytes for password", (uintmax_t)new_potential_size);
 
961
 
    if(mlock(new_buffer, new_potential_size) != 0){
 
962
 
      /* Warn but do not treat as fatal error */
 
963
 
      if(errno != EPERM and errno != ENOMEM){
 
964
 
        error(0, errno, "Failed to lock memory for password");
 
967
 
    if(password->length > 0){
 
968
 
      memcpy(new_buffer, password->data, password->length);
 
969
 
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
 
970
 
      explicit_bzero(password->data, password->allocated);
 
972
 
      memset(password->data, '\0', password->allocated);
 
975
 
    if(password->data != NULL){
 
976
 
      if(munlock(password->data, password->allocated) != 0){
 
977
 
        error(0, errno, "Failed to unlock memory of old buffer");
 
979
 
      free(password->data);
 
981
 
    password->data = new_buffer;
 
982
 
    password->allocated = new_potential_size;
 
985
 
  const ssize_t read_length = read(fd, password->data
 
986
 
                                   + password->length, PIPE_BUF);
 
988
 
  if(read_length == 0){ /* EOF */
 
989
 
    *password_is_read = true;
 
993
 
  if(read_length < 0 and errno != EAGAIN){ /* Actual error */
 
994
 
    error(0, errno, "Failed to read password from Mandos client");
 
999
 
  if(read_length > 0){          /* Data has been read */
 
1000
 
    password->length += (size_t)read_length;
 
1003
 
  /* Either data was read, or EAGAIN was indicated, meaning no data
 
1006
 
  /* Re-add the fd to the epoll set */
 
1007
 
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
1008
 
                            &(struct epoll_event)
 
1009
 
                            { .events=EPOLLIN | EPOLLRDHUP });
 
1010
 
  if(ret != 0 and errno != EEXIST){
 
1011
 
    error(0, errno, "Failed to re-add file descriptor to epoll set");
 
1017
 
  /* Re-add myself to the queue */
 
1018
 
  if(not add_to_queue(queue, task)){
 
1019
 
    error(0, errno, "Failed to add myself to queue");
 
1025
 
__attribute__((nonnull, warn_unused_result))
 
1026
 
bool add_inotify_dir_watch(task_queue *const queue,
 
1027
 
                           const int epoll_fd, bool *const quit_now,
 
1028
 
                           buffer *const password,
 
1029
 
                           const char *const dir,
 
1030
 
                           string_set *cancelled_filenames,
 
1031
 
                           const mono_microsecs *const current_time,
 
1032
 
                           bool *const mandos_client_exited,
 
1033
 
                           bool *const password_is_read){
 
1034
 
  const int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
 
1036
 
    error(0, errno, "Failed to create inotify instance");
 
1040
 
  if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO
 
1041
 
                       | IN_MOVED_FROM| IN_DELETE | IN_EXCL_UNLINK
 
1044
 
    error(0, errno, "Failed to create inotify watch on %s", dir);
 
1048
 
  /* Add the inotify fd to the epoll set */
 
1049
 
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
1050
 
                            &(struct epoll_event)
 
1051
 
                            { .events=EPOLLIN | EPOLLRDHUP });
 
1052
 
  if(ret != 0 and errno != EEXIST){
 
1053
 
    error(0, errno, "Failed to add file descriptor to epoll set");
 
1058
 
  const task_context read_inotify_event_task = {
 
1059
 
    .func=read_inotify_event,
 
1064
 
    .filename=strdup(dir),
 
1065
 
    .cancelled_filenames=cancelled_filenames,
 
1066
 
    .current_time=current_time,
 
1067
 
    .mandos_client_exited=mandos_client_exited,
 
1068
 
    .password_is_read=password_is_read,
 
1070
 
  if(read_inotify_event_task.filename == NULL){
 
1071
 
    error(0, errno, "Failed to strdup(\"%s\")", dir);
 
1076
 
  return add_to_queue(queue, read_inotify_event_task);
 
1079
 
__attribute__((nonnull))
 
1080
 
void read_inotify_event(const task_context task,
 
1081
 
                        task_queue *const queue){
 
1082
 
  const int fd = task.fd;
 
1083
 
  const int epoll_fd = task.epoll_fd;
 
1084
 
  char *const filename = task.filename;
 
1085
 
  bool *quit_now = task.quit_now;
 
1086
 
  buffer *const password = task.password;
 
1087
 
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
1088
 
  const mono_microsecs *const current_time = task.current_time;
 
1089
 
  bool *const mandos_client_exited = task.mandos_client_exited;
 
1090
 
  bool *const password_is_read = task.password_is_read;
 
1092
 
  /* "sufficient to read at least one event." - inotify(7) */
 
1093
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
1096
 
    struct inotify_event event;
 
1097
 
    char name_buffer[NAME_MAX + 1];
 
1099
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
1101
 
#if defined(__GNUC__) and __GNUC__ >= 7
 
1102
 
#pragma GCC diagnostic push
 
1103
 
  /* ievent is pointing into a struct which is of sufficient size */
 
1104
 
#pragma GCC diagnostic ignored "-Wstringop-overflow"
 
1106
 
  const ssize_t read_length = read(fd, ievent, ievent_size);
 
1107
 
#if defined(__GNUC__) and __GNUC__ >= 7
 
1108
 
#pragma GCC diagnostic pop
 
1110
 
  if(read_length == 0){ /* EOF */
 
1111
 
    error(0, 0, "Got EOF from inotify fd for directory %s", filename);
 
1113
 
    cleanup_task(&task);
 
1116
 
  if(read_length < 0 and errno != EAGAIN){ /* Actual error */
 
1117
 
    error(0, errno, "Failed to read from inotify fd for directory %s",
 
1120
 
    cleanup_task(&task);
 
1123
 
  if(read_length > 0            /* Data has been read */
 
1124
 
     and fnmatch("ask.*", ievent->name, FNM_FILE_NAME) == 0){
 
1125
 
    char *question_filename = NULL;
 
1126
 
    const ssize_t question_filename_length
 
1127
 
      = asprintf(&question_filename, "%s/%s", filename, ievent->name);
 
1128
 
    if(question_filename_length < 0){
 
1129
 
      error(0, errno, "Failed to create file name from directory name"
 
1130
 
            " %s and file name %s", filename, ievent->name);
 
1132
 
      if(ievent->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)){
 
1133
 
        if(not add_to_queue(queue, (task_context){
 
1134
 
              .func=open_and_parse_question,
 
1136
 
              .question_filename=question_filename,
 
1137
 
              .filename=question_filename,
 
1139
 
              .cancelled_filenames=cancelled_filenames,
 
1140
 
              .current_time=current_time,
 
1141
 
              .mandos_client_exited=mandos_client_exited,
 
1142
 
              .password_is_read=password_is_read,
 
1144
 
          error(0, errno, "Failed to add open_and_parse_question task"
 
1145
 
                " for file name %s to queue", filename);
 
1147
 
          /* Force the added task (open_and_parse_question) to run
 
1149
 
          queue->next_run = 1;
 
1151
 
      } else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
 
1152
 
        if(not string_set_add(cancelled_filenames,
 
1153
 
                              question_filename)){
 
1154
 
          error(0, errno, "Could not add question %s to"
 
1155
 
                " cancelled_questions", question_filename);
 
1157
 
          free(question_filename);
 
1158
 
          cleanup_task(&task);
 
1161
 
        free(question_filename);
 
1166
 
  /* Either data was read, or EAGAIN was indicated, meaning no data
 
1169
 
  /* Re-add myself to the queue */
 
1170
 
  if(not add_to_queue(queue, task)){
 
1171
 
    error(0, errno, "Failed to re-add read_inotify_event(%s) to"
 
1172
 
          " queue", filename);
 
1174
 
    cleanup_task(&task);
 
1178
 
  /* Re-add the fd to the epoll set */
 
1179
 
  const int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
1180
 
                            &(struct epoll_event)
 
1181
 
                            { .events=EPOLLIN | EPOLLRDHUP });
 
1182
 
  if(ret != 0 and errno != EEXIST){
 
1183
 
    error(0, errno, "Failed to re-add inotify file descriptor %d for"
 
1184
 
          " directory %s to epoll set", fd, filename);
 
1185
 
    /* Force the added task (read_inotify_event) to run again, at most
 
1186
 
       one second from now */
 
1187
 
    if((queue->next_run == 0)
 
1188
 
       or (queue->next_run > (*current_time + 1000000))){
 
1189
 
      queue->next_run = *current_time + 1000000;
 
1194
 
__attribute__((nonnull))
 
1195
 
void open_and_parse_question(const task_context task,
 
1196
 
                             task_queue *const queue){
 
1197
 
  __attribute__((cleanup(cleanup_string)))
 
1198
 
    char *question_filename = task.question_filename;
 
1199
 
  const int epoll_fd = task.epoll_fd;
 
1200
 
  buffer *const password = task.password;
 
1201
 
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
1202
 
  const mono_microsecs *const current_time = task.current_time;
 
1203
 
  bool *const mandos_client_exited = task.mandos_client_exited;
 
1204
 
  bool *const password_is_read = task.password_is_read;
 
1206
 
  /* We use the GLib "Key-value file parser" functions to parse the
 
1207
 
     question file.  See <https://systemd.io/PASSWORD_AGENTS/> for
 
1208
 
     specification of contents */
 
1209
 
  __attribute__((nonnull))
 
1210
 
    void cleanup_g_key_file(GKeyFile **key_file){
 
1211
 
    if(*key_file != NULL){
 
1212
 
      g_key_file_free(*key_file);
 
1216
 
  __attribute__((cleanup(cleanup_g_key_file)))
 
1217
 
    GKeyFile *key_file = g_key_file_new();
 
1218
 
  if(key_file == NULL){
 
1219
 
    error(0, errno, "Failed g_key_file_new() for \"%s\"",
 
1223
 
  GError *glib_error = NULL;
 
1224
 
  if(g_key_file_load_from_file(key_file, question_filename,
 
1225
 
                               G_KEY_FILE_NONE, &glib_error) != TRUE){
 
1226
 
    /* If a file was removed, we should ignore it, so */
 
1227
 
    /* only show error message if file actually existed */
 
1228
 
    if(glib_error->code != G_FILE_ERROR_NOENT){
 
1229
 
      error(0, 0, "Failed to load question data from file \"%s\": %s",
 
1230
 
            question_filename, glib_error->message);
 
1235
 
  __attribute__((cleanup(cleanup_string)))
 
1236
 
    char *socket_name = g_key_file_get_string(key_file, "Ask",
 
1239
 
  if(socket_name == NULL){
 
1240
 
    error(0, 0, "Question file \"%s\" did not contain \"Socket\": %s",
 
1241
 
          question_filename, glib_error->message);
 
1245
 
  if(strlen(socket_name) == 0){
 
1246
 
    error(0, 0, "Question file \"%s\" had empty \"Socket\" value",
 
1251
 
  const guint64 pid = g_key_file_get_uint64(key_file, "Ask", "PID",
 
1253
 
  if(glib_error != NULL){
 
1254
 
    error(0, 0, "Question file \"%s\" contained bad \"PID\": %s",
 
1255
 
          question_filename, glib_error->message);
 
1259
 
  if((pid != (guint64)((pid_t)pid))
 
1260
 
     or (kill((pid_t)pid, 0) != 0)){
 
1261
 
    error(0, 0, "PID %" PRIuMAX " in question file \"%s\" is bad or"
 
1262
 
          " does not exist", (uintmax_t)pid, question_filename);
 
1266
 
  guint64 notafter = g_key_file_get_uint64(key_file, "Ask",
 
1267
 
                                           "NotAfter", &glib_error);
 
1268
 
  if(glib_error != NULL){
 
1269
 
    if(glib_error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND){
 
1270
 
      error(0, 0, "Question file \"%s\" contained bad \"NotAfter\":"
 
1271
 
            " %s", question_filename, glib_error->message);
 
1276
 
    if(queue->next_run == 0 or (queue->next_run > notafter)){
 
1277
 
      queue->next_run = notafter;
 
1279
 
    if(*current_time >= notafter){
 
1284
 
  const task_context connect_question_socket_task = {
 
1285
 
    .func=connect_question_socket,
 
1286
 
    .question_filename=strdup(question_filename),
 
1289
 
    .filename=strdup(socket_name),
 
1290
 
    .cancelled_filenames=task.cancelled_filenames,
 
1291
 
    .mandos_client_exited=mandos_client_exited,
 
1292
 
    .password_is_read=password_is_read,
 
1293
 
    .current_time=current_time,
 
1295
 
  if(connect_question_socket_task.question_filename == NULL
 
1296
 
     or connect_question_socket_task.filename == NULL
 
1297
 
     or not add_to_queue(queue, connect_question_socket_task)){
 
1298
 
    error(0, errno, "Failed to add connect_question_socket for socket"
 
1299
 
          " %s (from \"%s\") to queue", socket_name,
 
1301
 
    cleanup_task(&connect_question_socket_task);
 
1304
 
  /* Force the added task (connect_question_socket) to run
 
1306
 
  queue->next_run = 1;
 
1309
 
    char *const dup_filename = strdup(question_filename);
 
1310
 
    const task_context cancel_old_question_task = {
 
1311
 
      .func=cancel_old_question,
 
1312
 
      .question_filename=dup_filename,
 
1314
 
      .filename=dup_filename,
 
1315
 
      .cancelled_filenames=cancelled_filenames,
 
1316
 
      .current_time=current_time,
 
1318
 
    if(cancel_old_question_task.question_filename == NULL
 
1319
 
       or not add_to_queue(queue, cancel_old_question_task)){
 
1320
 
      error(0, errno, "Failed to add cancel_old_question for file "
 
1321
 
            "\"%s\" to queue", question_filename);
 
1322
 
      cleanup_task(&cancel_old_question_task);
 
1328
 
__attribute__((nonnull))
 
1329
 
void cancel_old_question(const task_context task,
 
1330
 
                         task_queue *const queue){
 
1331
 
  char *const question_filename = task.question_filename;
 
1332
 
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
1333
 
  const mono_microsecs notafter = task.notafter;
 
1334
 
  const mono_microsecs *const current_time = task.current_time;
 
1336
 
  if(*current_time >= notafter){
 
1337
 
    if(not string_set_add(cancelled_filenames, question_filename)){
 
1338
 
      error(0, errno, "Failed to cancel question for file %s",
 
1341
 
    cleanup_task(&task);
 
1345
 
  if(not add_to_queue(queue, task)){
 
1346
 
    error(0, errno, "Failed to add cancel_old_question for file "
 
1347
 
          "%s to queue", question_filename);
 
1348
 
    cleanup_task(&task);
 
1352
 
  if((queue->next_run == 0) or (queue->next_run > notafter)){
 
1353
 
    queue->next_run = notafter;
 
1357
 
__attribute__((nonnull))
 
1358
 
void connect_question_socket(const task_context task,
 
1359
 
                             task_queue *const queue){
 
1360
 
  char *const question_filename = task.question_filename;
 
1361
 
  char *const filename = task.filename;
 
1362
 
  const int epoll_fd = task.epoll_fd;
 
1363
 
  buffer *const password = task.password;
 
1364
 
  string_set *const cancelled_filenames = task.cancelled_filenames;
 
1365
 
  bool *const mandos_client_exited = task.mandos_client_exited;
 
1366
 
  bool *const password_is_read = task.password_is_read;
 
1367
 
  const mono_microsecs *const current_time = task.current_time;
 
1369
 
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
 
1371
 
  if(sizeof(sock_name.sun_path) <= strlen(filename)){
 
1372
 
    error(0, 0, "Socket filename is larger than"
 
1373
 
          " sizeof(sockaddr_un.sun_path); %" PRIuMAX ": \"%s\"",
 
1374
 
          (uintmax_t)sizeof(sock_name.sun_path), filename);
 
1375
 
    if(not string_set_add(cancelled_filenames, question_filename)){
 
1376
 
      error(0, errno, "Failed to cancel question for file %s",
 
1379
 
    cleanup_task(&task);
 
1383
 
  const int fd = socket(PF_LOCAL, SOCK_DGRAM
 
1384
 
                        | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 
1387
 
          "Failed to create socket(PF_LOCAL, SOCK_DGRAM, 0)");
 
1388
 
    if(not add_to_queue(queue, task)){
 
1389
 
      error(0, errno, "Failed to add connect_question_socket for file"
 
1390
 
            " \"%s\" and socket \"%s\" to queue", question_filename,
 
1392
 
      cleanup_task(&task);
 
1394
 
      /* Force the added task (connect_question_socket) to run
 
1396
 
      queue->next_run = 1;
 
1401
 
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
 
1402
 
  if(connect(fd, (struct sockaddr *)&sock_name,
 
1403
 
             (socklen_t)SUN_LEN(&sock_name)) != 0){
 
1404
 
    error(0, errno, "Failed to connect socket to \"%s\"", filename);
 
1405
 
    if(not add_to_queue(queue, task)){
 
1406
 
      error(0, errno, "Failed to add connect_question_socket for file"
 
1407
 
            " \"%s\" and socket \"%s\" to queue", question_filename,
 
1409
 
      cleanup_task(&task);
 
1411
 
      /* Force the added task (connect_question_socket) to run again,
 
1412
 
         at most one second from now */
 
1413
 
      if((queue->next_run == 0)
 
1414
 
         or (queue->next_run > (*current_time + 1000000))){
 
1415
 
        queue->next_run = *current_time + 1000000;
 
1421
 
  /* Not necessary, but we can try, and merely warn on failure */
 
1422
 
  if(shutdown(fd, SHUT_RD) != 0){
 
1423
 
    error(0, errno, "Failed to shutdown reading from socket \"%s\"",
 
1427
 
  /* Add the fd to the epoll set */
 
1428
 
  if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
1429
 
               &(struct epoll_event){ .events=EPOLLOUT })
 
1431
 
    error(0, errno, "Failed to add inotify file descriptor %d for"
 
1432
 
          " socket %s to epoll set", fd, filename);
 
1433
 
    if(not add_to_queue(queue, task)){
 
1434
 
      error(0, errno, "Failed to add connect_question_socket for file"
 
1435
 
            " \"%s\" and socket \"%s\" to queue", question_filename,
 
1437
 
      cleanup_task(&task);
 
1439
 
      /* Force the added task (connect_question_socket) to run again,
 
1440
 
         at most one second from now */
 
1441
 
      if((queue->next_run == 0)
 
1442
 
         or (queue->next_run > (*current_time + 1000000))){
 
1443
 
        queue->next_run = *current_time + 1000000;
 
1449
 
  /* add task send_password_to_socket to queue */
 
1450
 
  const task_context send_password_to_socket_task = {
 
1451
 
    .func=send_password_to_socket,
 
1452
 
    .question_filename=question_filename,
 
1457
 
    .cancelled_filenames=cancelled_filenames,
 
1458
 
    .mandos_client_exited=mandos_client_exited,
 
1459
 
    .password_is_read=password_is_read,
 
1460
 
    .current_time=current_time,
 
1463
 
  if(not add_to_queue(queue, send_password_to_socket_task)){
 
1464
 
    error(0, errno, "Failed to add send_password_to_socket for"
 
1465
 
          " file \"%s\" and socket \"%s\" to queue",
 
1466
 
          question_filename, filename);
 
1467
 
    cleanup_task(&send_password_to_socket_task);
 
1471
 
__attribute__((nonnull))
 
1472
 
void send_password_to_socket(const task_context task,
 
1473
 
                             task_queue *const queue){
 
1474
 
  char *const question_filename=task.question_filename;
 
1475
 
  char *const filename=task.filename;
 
1476
 
  const int epoll_fd=task.epoll_fd;
 
1477
 
  const int fd=task.fd;
 
1478
 
  buffer *const password=task.password;
 
1479
 
  string_set *const cancelled_filenames=task.cancelled_filenames;
 
1480
 
  bool *const mandos_client_exited = task.mandos_client_exited;
 
1481
 
  bool *const password_is_read = task.password_is_read;
 
1482
 
  const mono_microsecs *const current_time = task.current_time;
 
1484
 
  if(*mandos_client_exited and *password_is_read){
 
1486
 
    const size_t send_buffer_length = password->length + 2;
 
1487
 
    char *send_buffer = malloc(send_buffer_length);
 
1488
 
    if(send_buffer == NULL){
 
1489
 
      error(0, errno, "Failed to allocate send_buffer");
 
1491
 
      if(mlock(send_buffer, send_buffer_length) != 0){
 
1492
 
        /* Warn but do not treat as fatal error */
 
1493
 
        if(errno != EPERM and errno != ENOMEM){
 
1494
 
          error(0, errno, "Failed to lock memory for password"
 
1498
 
      /* “[…] send a single datagram to the socket consisting of the
 
1499
 
         password string either prefixed with "+" or with "-"
 
1500
 
         depending on whether the password entry was successful or
 
1501
 
         not. You may but don't have to include a final NUL byte in
 
1504
 
         — <https://systemd.io/PASSWORD_AGENTS/> (Tue, 15 Sep 2020
 
1507
 
      send_buffer[0] = '+';     /* Prefix with "+" */
 
1508
 
      /* Always add an extra NUL */
 
1509
 
      send_buffer[password->length + 1] = '\0';
 
1510
 
      if(password->length > 0){
 
1511
 
        memcpy(send_buffer + 1, password->data, password->length);
 
1514
 
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
 
1516
 
      const error_t saved_errno = (ssret < 0) ? errno : 0;
 
1517
 
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
 
1518
 
      explicit_bzero(send_buffer, send_buffer_length);
 
1520
 
      memset(send_buffer, '\0', send_buffer_length);
 
1522
 
      if(munlock(send_buffer, send_buffer_length) != 0){
 
1523
 
        error(0, errno, "Failed to unlock memory of send buffer");
 
1526
 
      if(ssret < 0 or ssret < (ssize_t)send_buffer_length){
 
1527
 
        switch(saved_errno){
 
1540
 
          error(0, saved_errno, "Password of size %" PRIuMAX
 
1541
 
                " is too big", (uintmax_t)password->length);
 
1545
 
          __attribute__((fallthrough));
 
1548
 
          if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
 
1549
 
            error(0, 0, "Password only partially sent to socket %s: %"
 
1550
 
                  PRIuMAX " out of %" PRIuMAX " bytes sent", filename,
 
1551
 
                  (uintmax_t)ssret, (uintmax_t)send_buffer_length);
 
1556
 
          __attribute__((fallthrough));
 
1559
 
          error(0, saved_errno, "Failed to send() to socket %s",
 
1561
 
          if(not string_set_add(cancelled_filenames,
 
1562
 
                                question_filename)){
 
1563
 
            error(0, errno, "Failed to cancel question for file %s",
 
1566
 
          cleanup_task(&task);
 
1571
 
        cleanup_task(&task);
 
1577
 
  /* We failed or are not ready yet; retry later */
 
1579
 
  if(not add_to_queue(queue, task)){
 
1580
 
    error(0, errno, "Failed to add send_password_to_socket for"
 
1581
 
          " file %s and socket %s to queue", question_filename,
 
1583
 
    cleanup_task(&task);
 
1586
 
  /* Add the fd to the epoll set */
 
1587
 
  if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
1588
 
               &(struct epoll_event){ .events=EPOLLOUT })
 
1590
 
    error(0, errno, "Failed to add socket file descriptor %d for"
 
1591
 
          " socket %s to epoll set", fd, filename);
 
1592
 
    /* Force the added task (send_password_to_socket) to run again, at
 
1593
 
       most one second from now */
 
1594
 
    if((queue->next_run == 0)
 
1595
 
       or (queue->next_run > (*current_time + 1000000))){
 
1596
 
      queue->next_run = *current_time + 1000000;
 
1601
 
__attribute__((warn_unused_result))
 
1602
 
bool add_existing_questions(task_queue *const queue,
 
1604
 
                            buffer *const password,
 
1605
 
                            string_set *cancelled_filenames,
 
1606
 
                            const mono_microsecs *const current_time,
 
1607
 
                            bool *const mandos_client_exited,
 
1608
 
                            bool *const password_is_read,
 
1609
 
                            const char *const dirname){
 
1610
 
  __attribute__((cleanup(cleanup_string)))
 
1611
 
    char *dir_pattern = NULL;
 
1612
 
  const int ret = asprintf(&dir_pattern, "%s/ask.*", dirname);
 
1613
 
  if(ret < 0 or dir_pattern == NULL){
 
1614
 
    error(0, errno, "Could not create glob pattern for directory %s",
 
1618
 
  __attribute__((cleanup(globfree)))
 
1619
 
    glob_t question_filenames = {};
 
1620
 
  switch(glob(dir_pattern, GLOB_ERR | GLOB_NOSORT | GLOB_MARK,
 
1621
 
              NULL, &question_filenames)){
 
1624
 
    error(0, errno, "Failed to open directory %s", dirname);
 
1627
 
    error(0, errno, "There are no question files in %s", dirname);
 
1630
 
    error(0, errno, "Could not allocate memory for question file"
 
1631
 
          " names in %s", dirname);
 
1635
 
    __attribute__((fallthrough));
 
1638
 
    for(size_t i = 0; i < question_filenames.gl_pathc; i++){
 
1639
 
      char *const question_filename = strdup(question_filenames
 
1641
 
      const task_context task = {
 
1642
 
        .func=open_and_parse_question,
 
1644
 
        .question_filename=question_filename,
 
1645
 
        .filename=question_filename,
 
1647
 
        .cancelled_filenames=cancelled_filenames,
 
1648
 
        .current_time=current_time,
 
1649
 
        .mandos_client_exited=mandos_client_exited,
 
1650
 
        .password_is_read=password_is_read,
 
1653
 
      if(question_filename == NULL
 
1654
 
         or not add_to_queue(queue, task)){
 
1655
 
        error(0, errno, "Failed to add open_and_parse_question for"
 
1656
 
              " file %s to queue",
 
1657
 
              question_filenames.gl_pathv[i]);
 
1658
 
        free(question_filename);
 
1660
 
        queue->next_run = 1;
 
1667
 
__attribute__((nonnull, warn_unused_result))
 
1668
 
bool wait_for_event(const int epoll_fd,
 
1669
 
                    const mono_microsecs queue_next_run,
 
1670
 
                    const mono_microsecs current_time){
 
1671
 
  __attribute__((const))
 
1672
 
    int milliseconds_to_wait(const mono_microsecs currtime,
 
1673
 
                             const mono_microsecs nextrun){
 
1674
 
    if(currtime >= nextrun){
 
1677
 
    const uintmax_t wait_time_ms = (nextrun - currtime) / 1000;
 
1678
 
    if(wait_time_ms > (uintmax_t)INT_MAX){
 
1681
 
    return (int)wait_time_ms;
 
1684
 
  const int wait_time_ms = milliseconds_to_wait(current_time,
 
1687
 
  /* Prepare unblocking of SIGCHLD during epoll_pwait */
 
1688
 
  sigset_t temporary_unblocked_sigmask;
 
1689
 
  /* Get current signal mask */
 
1690
 
  if(pthread_sigmask(-1, NULL, &temporary_unblocked_sigmask) != 0){
 
1693
 
  /* Remove SIGCHLD from the signal mask */
 
1694
 
  if(sigdelset(&temporary_unblocked_sigmask, SIGCHLD) != 0){
 
1697
 
  struct epoll_event events[8]; /* Ignored */
 
1698
 
  int ret = epoll_pwait(epoll_fd, events,
 
1699
 
                        sizeof(events) / sizeof(struct epoll_event),
 
1700
 
                        queue_next_run == 0 ? -1 : (int)wait_time_ms,
 
1701
 
                        &temporary_unblocked_sigmask);
 
1702
 
  if(ret < 0 and errno != EINTR){
 
1703
 
    error(0, errno, "Failed epoll_pwait(epfd=%d, ..., timeout=%d,"
 
1705
 
          queue_next_run == 0 ? -1 : (int)wait_time_ms);
 
1708
 
  return clear_all_fds_from_epoll_set(epoll_fd);
 
1711
 
bool clear_all_fds_from_epoll_set(const int epoll_fd){
 
1712
 
  /* Create a new empty epoll set */
 
1713
 
  __attribute__((cleanup(cleanup_close)))
 
1714
 
    const int new_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
1715
 
  if(new_epoll_fd < 0){
 
1718
 
  /* dup3() the new epoll set fd over the old one, replacing it */
 
1719
 
  if(dup3(new_epoll_fd, epoll_fd, O_CLOEXEC) < 0){
 
1725
 
__attribute__((nonnull, warn_unused_result))
 
1726
 
bool run_queue(task_queue **const queue,
 
1727
 
               string_set *const cancelled_filenames,
 
1728
 
               bool *const quit_now){
 
1730
 
  task_queue *new_queue = create_queue();
 
1731
 
  if(new_queue == NULL){
 
1735
 
  __attribute__((cleanup(string_set_clear)))
 
1736
 
    string_set old_cancelled_filenames = {};
 
1737
 
  string_set_swap(cancelled_filenames, &old_cancelled_filenames);
 
1739
 
  /* Declare i outside the for loop, since we might need i after the
 
1740
 
     loop in case we aborted in the middle */
 
1742
 
  for(i=0; i < (*queue)->length and not *quit_now; i++){
 
1743
 
    task_context *const task = &((*queue)->tasks[i]);
 
1744
 
    const char *const question_filename = task->question_filename;
 
1745
 
    /* Skip any task referencing a cancelled question filename */
 
1746
 
    if(question_filename != NULL
 
1747
 
       and string_set_contains(old_cancelled_filenames,
 
1748
 
                               question_filename)){
 
1752
 
    task->func(*task, new_queue);
 
1756
 
    /* we might be in the middle of the queue, so clean up any
 
1757
 
       remaining tasks in the current queue */
 
1758
 
    for(; i < (*queue)->length; i++){
 
1759
 
      cleanup_task(&((*queue)->tasks[i]));
 
1773
 
/* End of regular code section */
 
1775
 
/* Start of tests section; here are the tests for the above code */
 
1777
 
/* This "fixture" data structure is used by the test setup and
 
1778
 
   teardown functions */
 
1780
 
  struct sigaction orig_sigaction;
 
1781
 
  sigset_t orig_sigmask;
 
1784
 
static void test_setup(test_fixture *fixture,
 
1785
 
                       __attribute__((unused))
 
1786
 
                       gconstpointer user_data){
 
1787
 
  g_assert_true(setup_signal_handler(&fixture->orig_sigaction));
 
1788
 
  g_assert_true(block_sigchld(&fixture->orig_sigmask));
 
1791
 
static void test_teardown(test_fixture *fixture,
 
1792
 
                          __attribute__((unused))
 
1793
 
                          gconstpointer user_data){
 
1794
 
  g_assert_true(restore_signal_handler(&fixture->orig_sigaction));
 
1795
 
  g_assert_true(restore_sigmask(&fixture->orig_sigmask));
 
1798
 
/* Utility function used by tests to search queue for matching task */
 
1799
 
__attribute__((pure, nonnull, warn_unused_result))
 
1800
 
static task_context *find_matching_task(const task_queue *const queue,
 
1801
 
                                        const task_context task){
 
1802
 
  /* The argument "task" structure is a pattern to match; 0 in any
 
1803
 
     member means any value matches, otherwise the value must match.
 
1804
 
     The filename strings are compared by strcmp(), not by pointer. */
 
1805
 
  for(size_t i = 0; i < queue->length; i++){
 
1806
 
    task_context *const current_task = queue->tasks+i;
 
1807
 
    /* Check all members of task_context, if set to a non-zero value.
 
1808
 
       If a member does not match, continue to next task in queue */
 
1810
 
    /* task_func *const func */
 
1811
 
    if(task.func != NULL and current_task->func != task.func){
 
1814
 
    /* char *const question_filename; */
 
1815
 
    if(task.question_filename != NULL
 
1816
 
       and (current_task->question_filename == NULL
 
1817
 
            or strcmp(current_task->question_filename,
 
1818
 
                      task.question_filename) != 0)){
 
1821
 
    /* const pid_t pid; */
 
1822
 
    if(task.pid != 0 and current_task->pid != task.pid){
 
1825
 
    /* const int epoll_fd; */
 
1826
 
    if(task.epoll_fd != 0
 
1827
 
       and current_task->epoll_fd != task.epoll_fd){
 
1830
 
    /* bool *const quit_now; */
 
1831
 
    if(task.quit_now != NULL
 
1832
 
       and current_task->quit_now != task.quit_now){
 
1836
 
    if(task.fd != 0 and current_task->fd != task.fd){
 
1839
 
    /* bool *const mandos_client_exited; */
 
1840
 
    if(task.mandos_client_exited != NULL
 
1841
 
       and current_task->mandos_client_exited
 
1842
 
       != task.mandos_client_exited){
 
1845
 
    /* buffer *const password; */
 
1846
 
    if(task.password != NULL
 
1847
 
       and current_task->password != task.password){
 
1850
 
    /* bool *const password_is_read; */
 
1851
 
    if(task.password_is_read != NULL
 
1852
 
       and current_task->password_is_read != task.password_is_read){
 
1855
 
    /* char *filename; */
 
1856
 
    if(task.filename != NULL
 
1857
 
       and (current_task->filename == NULL
 
1858
 
            or strcmp(current_task->filename, task.filename) != 0)){
 
1861
 
    /* string_set *const cancelled_filenames; */
 
1862
 
    if(task.cancelled_filenames != NULL
 
1863
 
       and current_task->cancelled_filenames
 
1864
 
       != task.cancelled_filenames){
 
1867
 
    /* const mono_microsecs notafter; */
 
1868
 
    if(task.notafter != 0
 
1869
 
       and current_task->notafter != task.notafter){
 
1872
 
    /* const mono_microsecs *const current_time; */
 
1873
 
    if(task.current_time != NULL
 
1874
 
       and current_task->current_time != task.current_time){
 
1877
 
    /* Current task matches all members; return it */
 
1878
 
    return current_task;
 
1880
 
  /* No task in queue matches passed pattern task */
 
1884
 
static void test_create_queue(__attribute__((unused))
 
1885
 
                              test_fixture *fixture,
 
1886
 
                              __attribute__((unused))
 
1887
 
                              gconstpointer user_data){
 
1888
 
  __attribute__((cleanup(cleanup_queue)))
 
1889
 
    task_queue *const queue = create_queue();
 
1890
 
  g_assert_nonnull(queue);
 
1891
 
  g_assert_null(queue->tasks);
 
1892
 
  g_assert_true(queue->length == 0);
 
1893
 
  g_assert_true(queue->next_run == 0);
 
1896
 
static task_func dummy_func;
 
1898
 
static void test_add_to_queue(__attribute__((unused))
 
1899
 
                              test_fixture *fixture,
 
1900
 
                              __attribute__((unused))
 
1901
 
                              gconstpointer user_data){
 
1902
 
  __attribute__((cleanup(cleanup_queue)))
 
1903
 
    task_queue *queue = create_queue();
 
1904
 
  g_assert_nonnull(queue);
 
1906
 
  g_assert_true(add_to_queue(queue,
 
1907
 
                             (task_context){ .func=dummy_func }));
 
1908
 
  g_assert_true(queue->length == 1);
 
1909
 
  g_assert_nonnull(queue->tasks);
 
1910
 
  g_assert_true(queue->tasks[0].func == dummy_func);
 
1913
 
static void test_add_to_queue_overflow(__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
 
  g_assert_true(queue->length == 0);
 
1921
 
  queue->length = SIZE_MAX / sizeof(task_context); /* fake max size */
 
1923
 
  FILE *real_stderr = stderr;
 
1924
 
  FILE *devnull = fopen("/dev/null", "we");
 
1925
 
  g_assert_nonnull(devnull);
 
1927
 
  const bool ret = add_to_queue(queue,
 
1928
 
                                (task_context){ .func=dummy_func });
 
1929
 
  g_assert_true(errno == ENOMEM);
 
1930
 
  g_assert_false(ret);
 
1931
 
  stderr = real_stderr;
 
1932
 
  g_assert_cmpint(fclose(devnull), ==, 0);
 
1933
 
  queue->length = 0;            /* Restore real size */
 
1936
 
static void dummy_func(__attribute__((unused))
 
1937
 
                       const task_context task,
 
1938
 
                       __attribute__((unused))
 
1939
 
                       task_queue *const queue){
 
1942
 
static void test_queue_has_question_empty(__attribute__((unused))
 
1943
 
                                          test_fixture *fixture,
 
1944
 
                                          __attribute__((unused))
 
1945
 
                                          gconstpointer user_data){
 
1946
 
  __attribute__((cleanup(cleanup_queue)))
 
1947
 
    task_queue *queue = create_queue();
 
1948
 
  g_assert_nonnull(queue);
 
1949
 
  g_assert_false(queue_has_question(queue));
 
1952
 
static void test_queue_has_question_false(__attribute__((unused))
 
1953
 
                                          test_fixture *fixture,
 
1954
 
                                          __attribute__((unused))
 
1955
 
                                          gconstpointer user_data){
 
1956
 
  __attribute__((cleanup(cleanup_queue)))
 
1957
 
    task_queue *queue = create_queue();
 
1958
 
  g_assert_nonnull(queue);
 
1959
 
  g_assert_true(add_to_queue(queue,
 
1960
 
                             (task_context){ .func=dummy_func }));
 
1961
 
  g_assert_false(queue_has_question(queue));
 
1964
 
static void test_queue_has_question_true(__attribute__((unused))
 
1965
 
                                         test_fixture *fixture,
 
1966
 
                                         __attribute__((unused))
 
1967
 
                                         gconstpointer user_data){
 
1968
 
  __attribute__((cleanup(cleanup_queue)))
 
1969
 
    task_queue *queue = create_queue();
 
1970
 
  g_assert_nonnull(queue);
 
1971
 
  char *const question_filename
 
1972
 
    = strdup("/nonexistent/question_filename");
 
1973
 
  g_assert_nonnull(question_filename);
 
1974
 
  task_context task = {
 
1976
 
    .question_filename=question_filename,
 
1978
 
  g_assert_true(add_to_queue(queue, task));
 
1979
 
  g_assert_true(queue_has_question(queue));
 
1982
 
static void test_queue_has_question_false2(__attribute__((unused))
 
1983
 
                                           test_fixture *fixture,
 
1984
 
                                           __attribute__((unused))
 
1985
 
                                           gconstpointer user_data){
 
1986
 
  __attribute__((cleanup(cleanup_queue)))
 
1987
 
    task_queue *queue = create_queue();
 
1988
 
  g_assert_nonnull(queue);
 
1989
 
  task_context task = { .func=dummy_func };
 
1990
 
  g_assert_true(add_to_queue(queue, task));
 
1991
 
  g_assert_true(add_to_queue(queue, task));
 
1992
 
  g_assert_cmpint((int)queue->length, ==, 2);
 
1993
 
  g_assert_false(queue_has_question(queue));
 
1996
 
static void test_queue_has_question_true2(__attribute__((unused))
 
1997
 
                                          test_fixture *fixture,
 
1998
 
                                          __attribute__((unused))
 
1999
 
                                          gconstpointer user_data){
 
2000
 
  __attribute__((cleanup(cleanup_queue)))
 
2001
 
    task_queue *queue = create_queue();
 
2002
 
  g_assert_nonnull(queue);
 
2003
 
  task_context task1 = { .func=dummy_func };
 
2004
 
  g_assert_true(add_to_queue(queue, task1));
 
2005
 
  char *const question_filename
 
2006
 
    = strdup("/nonexistent/question_filename");
 
2007
 
  g_assert_nonnull(question_filename);
 
2008
 
  task_context task2 = {
 
2010
 
    .question_filename=question_filename,
 
2012
 
  g_assert_true(add_to_queue(queue, task2));
 
2013
 
  g_assert_cmpint((int)queue->length, ==, 2);
 
2014
 
  g_assert_true(queue_has_question(queue));
 
2017
 
static void test_cleanup_buffer(__attribute__((unused))
 
2018
 
                                test_fixture *fixture,
 
2019
 
                                __attribute__((unused))
 
2020
 
                                gconstpointer user_data){
 
2023
 
  const size_t buffersize = 10;
 
2025
 
  buf.data = malloc(buffersize);
 
2026
 
  g_assert_nonnull(buf.data);
 
2027
 
  if(mlock(buf.data, buffersize) != 0){
 
2028
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
2031
 
  cleanup_buffer(&buf);
 
2032
 
  g_assert_null(buf.data);
 
2036
 
void test_string_set_new_set_contains_nothing(__attribute__((unused))
 
2037
 
                                              test_fixture *fixture,
 
2038
 
                                              __attribute__((unused))
 
2041
 
  __attribute__((cleanup(string_set_clear)))
 
2042
 
    string_set set = {};
 
2043
 
  g_assert_false(string_set_contains(set, "")); /* Empty string */
 
2044
 
  g_assert_false(string_set_contains(set, "test_string"));
 
2048
 
test_string_set_with_added_string_contains_it(__attribute__((unused))
 
2049
 
                                              test_fixture *fixture,
 
2050
 
                                              __attribute__((unused))
 
2053
 
  __attribute__((cleanup(string_set_clear)))
 
2054
 
    string_set set = {};
 
2055
 
  g_assert_true(string_set_add(&set, "test_string"));
 
2056
 
  g_assert_true(string_set_contains(set, "test_string"));
 
2060
 
test_string_set_cleared_does_not_contain_str(__attribute__((unused))
 
2061
 
                                             test_fixture *fixture,
 
2062
 
                                             __attribute__((unused))
 
2063
 
                                             gconstpointer user_data){
 
2064
 
  __attribute__((cleanup(string_set_clear)))
 
2065
 
    string_set set = {};
 
2066
 
  g_assert_true(string_set_add(&set, "test_string"));
 
2067
 
  string_set_clear(&set);
 
2068
 
  g_assert_false(string_set_contains(set, "test_string"));
 
2072
 
void test_string_set_swap_one_with_empty(__attribute__((unused))
 
2073
 
                                         test_fixture *fixture,
 
2074
 
                                         __attribute__((unused))
 
2075
 
                                         gconstpointer user_data){
 
2076
 
  __attribute__((cleanup(string_set_clear)))
 
2077
 
    string_set set1 = {};
 
2078
 
  __attribute__((cleanup(string_set_clear)))
 
2079
 
    string_set set2 = {};
 
2080
 
  g_assert_true(string_set_add(&set1, "test_string1"));
 
2081
 
  string_set_swap(&set1, &set2);
 
2082
 
  g_assert_false(string_set_contains(set1, "test_string1"));
 
2083
 
  g_assert_true(string_set_contains(set2, "test_string1"));
 
2087
 
void test_string_set_swap_empty_with_one(__attribute__((unused))
 
2088
 
                                         test_fixture *fixture,
 
2089
 
                                         __attribute__((unused))
 
2090
 
                                         gconstpointer user_data){
 
2091
 
  __attribute__((cleanup(string_set_clear)))
 
2092
 
    string_set set1 = {};
 
2093
 
  __attribute__((cleanup(string_set_clear)))
 
2094
 
    string_set set2 = {};
 
2095
 
  g_assert_true(string_set_add(&set2, "test_string2"));
 
2096
 
  string_set_swap(&set1, &set2);
 
2097
 
  g_assert_true(string_set_contains(set1, "test_string2"));
 
2098
 
  g_assert_false(string_set_contains(set2, "test_string2"));
 
2101
 
static void test_string_set_swap_one_with_one(__attribute__((unused))
 
2102
 
                                              test_fixture *fixture,
 
2103
 
                                              __attribute__((unused))
 
2106
 
  __attribute__((cleanup(string_set_clear)))
 
2107
 
    string_set set1 = {};
 
2108
 
  __attribute__((cleanup(string_set_clear)))
 
2109
 
    string_set set2 = {};
 
2110
 
  g_assert_true(string_set_add(&set1, "test_string1"));
 
2111
 
  g_assert_true(string_set_add(&set2, "test_string2"));
 
2112
 
  string_set_swap(&set1, &set2);
 
2113
 
  g_assert_false(string_set_contains(set1, "test_string1"));
 
2114
 
  g_assert_true(string_set_contains(set1, "test_string2"));
 
2115
 
  g_assert_false(string_set_contains(set2, "test_string2"));
 
2116
 
  g_assert_true(string_set_contains(set2, "test_string1"));
 
2119
 
static bool fd_has_cloexec_and_nonblock(const int);
 
2121
 
static bool epoll_set_contains(int, int, uint32_t);
 
2123
 
static void test_start_mandos_client(test_fixture *fixture,
 
2124
 
                                     __attribute__((unused))
 
2125
 
                                     gconstpointer user_data){
 
2127
 
  bool mandos_client_exited = false;
 
2128
 
  bool quit_now = false;
 
2129
 
  __attribute__((cleanup(cleanup_close)))
 
2130
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2131
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2132
 
  __attribute__((cleanup(cleanup_queue)))
 
2133
 
    task_queue *queue = create_queue();
 
2134
 
  g_assert_nonnull(queue);
 
2135
 
  buffer password = {};
 
2136
 
  bool password_is_read = false;
 
2137
 
  const char helper_directory[] = "/nonexistent";
 
2138
 
  const char *const argv[] = { "/bin/true", NULL };
 
2140
 
  g_assert_true(start_mandos_client(queue, epoll_fd,
 
2141
 
                                    &mandos_client_exited, &quit_now,
 
2142
 
                                    &password, &password_is_read,
 
2143
 
                                    &fixture->orig_sigaction,
 
2144
 
                                    fixture->orig_sigmask,
 
2145
 
                                    helper_directory, 0, 0, argv));
 
2147
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
2149
 
  const task_context *const added_wait_task
 
2150
 
    = find_matching_task(queue, (task_context){
 
2151
 
        .func=wait_for_mandos_client_exit,
 
2152
 
        .mandos_client_exited=&mandos_client_exited,
 
2153
 
        .quit_now=&quit_now,
 
2155
 
  g_assert_nonnull(added_wait_task);
 
2156
 
  g_assert_cmpint(added_wait_task->pid, >, 0);
 
2157
 
  g_assert_cmpint(kill(added_wait_task->pid, SIGKILL), ==, 0);
 
2158
 
  waitpid(added_wait_task->pid, NULL, 0);
 
2160
 
  const task_context *const added_read_task
 
2161
 
    = find_matching_task(queue, (task_context){
 
2162
 
        .func=read_mandos_client_output,
 
2164
 
        .password=&password,
 
2165
 
        .password_is_read=&password_is_read,
 
2166
 
        .quit_now=&quit_now,
 
2168
 
  g_assert_nonnull(added_read_task);
 
2169
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
2170
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
2171
 
  g_assert_true(epoll_set_contains(epoll_fd, added_read_task->fd,
 
2172
 
                                   EPOLLIN | EPOLLRDHUP));
 
2175
 
static bool fd_has_cloexec_and_nonblock(const int fd){
 
2176
 
  const int socket_fd_flags = fcntl(fd, F_GETFD, 0);
 
2177
 
  const int socket_file_flags = fcntl(fd, F_GETFL, 0);
 
2178
 
  return ((socket_fd_flags >= 0)
 
2179
 
          and (socket_fd_flags & FD_CLOEXEC)
 
2180
 
          and (socket_file_flags >= 0)
 
2181
 
          and (socket_file_flags & O_NONBLOCK));
 
2184
 
__attribute__((const))
 
2185
 
bool is_privileged(void){
 
2186
 
  uid_t user = getuid() + 1;
 
2187
 
  if(user == 0){                /* Overflow check */
 
2190
 
  gid_t group = getuid() + 1;
 
2191
 
  if(group == 0){               /* Overflow check */
 
2194
 
  const pid_t pid = fork();
 
2195
 
  if(pid == 0){                 /* Child */
 
2196
 
    if(setresgid((uid_t)-1, group, group) == -1){
 
2198
 
        error(EXIT_FAILURE, errno, "Failed to setresgid(-1, %" PRIuMAX
 
2199
 
              ", %" PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
 
2203
 
    if(setresuid((uid_t)-1, user, user) == -1){
 
2205
 
        error(EXIT_FAILURE, errno, "Failed to setresuid(-1, %" PRIuMAX
 
2206
 
              ", %" PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
 
2213
 
    error(EXIT_FAILURE, errno, "Failed to fork()");
 
2217
 
  waitpid(pid, &status, 0);
 
2218
 
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
 
2224
 
static bool epoll_set_contains(int epoll_fd, int fd, uint32_t events){
 
2225
 
  /* Only scan for events in this eventmask */
 
2226
 
  const uint32_t eventmask = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
 
2227
 
  __attribute__((cleanup(cleanup_string)))
 
2228
 
    char *fdinfo_name = NULL;
 
2229
 
  int ret = asprintf(&fdinfo_name, "/proc/self/fdinfo/%d", epoll_fd);
 
2230
 
  g_assert_cmpint(ret, >, 0);
 
2231
 
  g_assert_nonnull(fdinfo_name);
 
2233
 
  FILE *fdinfo = fopen(fdinfo_name, "r");
 
2234
 
  g_assert_nonnull(fdinfo);
 
2235
 
  uint32_t reported_events;
 
2240
 
    if(getline(&line.data, &line.allocated, fdinfo) < 0){
 
2243
 
    /* See proc(5) for format of /proc/PID/fdinfo/FD for epoll fd's */
 
2244
 
    if(sscanf(line.data, "tfd: %d events: %" SCNx32 " ",
 
2245
 
              &found_fd, &reported_events) == 2){
 
2250
 
  } while(not feof(fdinfo) and not ferror(fdinfo));
 
2251
 
  g_assert_cmpint(fclose(fdinfo), ==, 0);
 
2258
 
    /* Don't check events if none are given */
 
2261
 
  return (reported_events & eventmask) == (events & eventmask);
 
2264
 
static void test_start_mandos_client_execv(test_fixture *fixture,
 
2265
 
                                           __attribute__((unused))
 
2266
 
                                           gconstpointer user_data){
 
2267
 
  bool mandos_client_exited = false;
 
2268
 
  bool quit_now = false;
 
2269
 
  __attribute__((cleanup(cleanup_close)))
 
2270
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2271
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2272
 
  __attribute__((cleanup(cleanup_queue)))
 
2273
 
    task_queue *queue = create_queue();
 
2274
 
  g_assert_nonnull(queue);
 
2275
 
  __attribute__((cleanup(cleanup_buffer)))
 
2276
 
    buffer password = {};
 
2277
 
  const char helper_directory[] = "/nonexistent";
 
2278
 
  /* Can't execv("/", ...), so this should fail */
 
2279
 
  const char *const argv[] = { "/", NULL };
 
2282
 
    __attribute__((cleanup(cleanup_close)))
 
2283
 
      const int devnull_fd = open("/dev/null",
 
2284
 
                                  O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2285
 
    g_assert_cmpint(devnull_fd, >=, 0);
 
2286
 
    __attribute__((cleanup(cleanup_close)))
 
2287
 
      const int real_stderr_fd = dup(STDERR_FILENO);
 
2288
 
    g_assert_cmpint(real_stderr_fd, >=, 0);
 
2289
 
    dup2(devnull_fd, STDERR_FILENO);
 
2291
 
    const bool success = start_mandos_client(queue, epoll_fd,
 
2292
 
                                             &mandos_client_exited,
 
2296
 
                                             &fixture->orig_sigaction,
 
2297
 
                                             fixture->orig_sigmask,
 
2298
 
                                             helper_directory, 0, 0,
 
2300
 
    dup2(real_stderr_fd, STDERR_FILENO);
 
2301
 
    g_assert_true(success);
 
2303
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 2);
 
2305
 
  struct timespec starttime, currtime;
 
2306
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2308
 
    queue->next_run = 0;
 
2309
 
    string_set cancelled_filenames = {};
 
2312
 
      __attribute__((cleanup(cleanup_close)))
 
2313
 
        const int devnull_fd = open("/dev/null",
 
2314
 
                                    O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2315
 
      g_assert_cmpint(devnull_fd, >=, 0);
 
2316
 
      __attribute__((cleanup(cleanup_close)))
 
2317
 
        const int real_stderr_fd = dup(STDERR_FILENO);
 
2318
 
      g_assert_cmpint(real_stderr_fd, >=, 0);
 
2319
 
      g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2320
 
      dup2(devnull_fd, STDERR_FILENO);
 
2321
 
      const bool success = run_queue(&queue, &cancelled_filenames,
 
2323
 
      dup2(real_stderr_fd, STDERR_FILENO);
 
2328
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2329
 
  } while(((queue->length) > 0)
 
2331
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2333
 
  g_assert_true(quit_now);
 
2334
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2335
 
  g_assert_true(mandos_client_exited);
 
2338
 
static void test_start_mandos_client_suid_euid(test_fixture *fixture,
 
2339
 
                                               __attribute__((unused))
 
2342
 
  if(not is_privileged()){
 
2343
 
    g_test_skip("Not privileged");
 
2347
 
  bool mandos_client_exited = false;
 
2348
 
  bool quit_now = false;
 
2349
 
  __attribute__((cleanup(cleanup_close)))
 
2350
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2351
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2352
 
  __attribute__((cleanup(cleanup_queue)))
 
2353
 
    task_queue *queue = create_queue();
 
2354
 
  g_assert_nonnull(queue);
 
2355
 
  __attribute__((cleanup(cleanup_buffer)))
 
2356
 
    buffer password = {};
 
2357
 
  bool password_is_read = false;
 
2358
 
  const char helper_directory[] = "/nonexistent";
 
2359
 
  const char *const argv[] = { "/usr/bin/id", "--user", NULL };
 
2363
 
  const bool success = start_mandos_client(queue, epoll_fd,
 
2364
 
                                           &mandos_client_exited,
 
2365
 
                                           &quit_now, &password,
 
2367
 
                                           &fixture->orig_sigaction,
 
2368
 
                                           fixture->orig_sigmask,
 
2369
 
                                           helper_directory, user,
 
2371
 
  g_assert_true(success);
 
2372
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
2374
 
  struct timespec starttime, currtime;
 
2375
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2377
 
    queue->next_run = 0;
 
2378
 
    string_set cancelled_filenames = {};
 
2379
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2380
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2381
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2382
 
  } while(((queue->length) > 0)
 
2384
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2386
 
  g_assert_false(quit_now);
 
2387
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2388
 
  g_assert_true(mandos_client_exited);
 
2390
 
  g_assert_true(password_is_read);
 
2391
 
  g_assert_nonnull(password.data);
 
2394
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
2396
 
  g_assert_true((uid_t)id == id);
 
2398
 
  g_assert_cmpuint((unsigned int)id, ==, 0);
 
2401
 
static void test_start_mandos_client_suid_egid(test_fixture *fixture,
 
2402
 
                                               __attribute__((unused))
 
2405
 
  if(not is_privileged()){
 
2406
 
    g_test_skip("Not privileged");
 
2410
 
  bool mandos_client_exited = false;
 
2411
 
  bool quit_now = false;
 
2412
 
  __attribute__((cleanup(cleanup_close)))
 
2413
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2414
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2415
 
  __attribute__((cleanup(cleanup_queue)))
 
2416
 
    task_queue *queue = create_queue();
 
2417
 
  g_assert_nonnull(queue);
 
2418
 
  __attribute__((cleanup(cleanup_buffer)))
 
2419
 
    buffer password = {};
 
2420
 
  bool password_is_read = false;
 
2421
 
  const char helper_directory[] = "/nonexistent";
 
2422
 
  const char *const argv[] = { "/usr/bin/id", "--group", NULL };
 
2426
 
  const bool success = start_mandos_client(queue, epoll_fd,
 
2427
 
                                           &mandos_client_exited,
 
2428
 
                                           &quit_now, &password,
 
2430
 
                                           &fixture->orig_sigaction,
 
2431
 
                                           fixture->orig_sigmask,
 
2432
 
                                           helper_directory, user,
 
2434
 
  g_assert_true(success);
 
2435
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
2437
 
  struct timespec starttime, currtime;
 
2438
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2440
 
    queue->next_run = 0;
 
2441
 
    string_set cancelled_filenames = {};
 
2442
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2443
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2444
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2445
 
  } while(((queue->length) > 0)
 
2447
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2449
 
  g_assert_false(quit_now);
 
2450
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2451
 
  g_assert_true(mandos_client_exited);
 
2453
 
  g_assert_true(password_is_read);
 
2454
 
  g_assert_nonnull(password.data);
 
2457
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
2459
 
  g_assert_true((gid_t)id == id);
 
2461
 
  g_assert_cmpuint((unsigned int)id, ==, 0);
 
2464
 
static void test_start_mandos_client_suid_ruid(test_fixture *fixture,
 
2465
 
                                               __attribute__((unused))
 
2468
 
  if(not is_privileged()){
 
2469
 
    g_test_skip("Not privileged");
 
2473
 
  bool mandos_client_exited = false;
 
2474
 
  bool quit_now = false;
 
2475
 
  __attribute__((cleanup(cleanup_close)))
 
2476
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2477
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2478
 
  __attribute__((cleanup(cleanup_queue)))
 
2479
 
    task_queue *queue = create_queue();
 
2480
 
  g_assert_nonnull(queue);
 
2481
 
  __attribute__((cleanup(cleanup_buffer)))
 
2482
 
    buffer password = {};
 
2483
 
  bool password_is_read = false;
 
2484
 
  const char helper_directory[] = "/nonexistent";
 
2485
 
  const char *const argv[] = { "/usr/bin/id", "--user", "--real",
 
2490
 
  const bool success = start_mandos_client(queue, epoll_fd,
 
2491
 
                                           &mandos_client_exited,
 
2492
 
                                           &quit_now, &password,
 
2494
 
                                           &fixture->orig_sigaction,
 
2495
 
                                           fixture->orig_sigmask,
 
2496
 
                                           helper_directory, user,
 
2498
 
  g_assert_true(success);
 
2499
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
2501
 
  struct timespec starttime, currtime;
 
2502
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2504
 
    queue->next_run = 0;
 
2505
 
    string_set cancelled_filenames = {};
 
2506
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2507
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2508
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2509
 
  } while(((queue->length) > 0)
 
2511
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2513
 
  g_assert_false(quit_now);
 
2514
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2515
 
  g_assert_true(mandos_client_exited);
 
2517
 
  g_assert_true(password_is_read);
 
2518
 
  g_assert_nonnull(password.data);
 
2521
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
2523
 
  g_assert_true((uid_t)id == id);
 
2525
 
  g_assert_cmpuint((unsigned int)id, ==, user);
 
2528
 
static void test_start_mandos_client_suid_rgid(test_fixture *fixture,
 
2529
 
                                               __attribute__((unused))
 
2532
 
  if(not is_privileged()){
 
2533
 
    g_test_skip("Not privileged");
 
2537
 
  bool mandos_client_exited = false;
 
2538
 
  bool quit_now = false;
 
2539
 
  __attribute__((cleanup(cleanup_close)))
 
2540
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2541
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2542
 
  __attribute__((cleanup(cleanup_queue)))
 
2543
 
    task_queue *queue = create_queue();
 
2544
 
  g_assert_nonnull(queue);
 
2545
 
  __attribute__((cleanup(cleanup_buffer)))
 
2546
 
    buffer password = {};
 
2547
 
  bool password_is_read = false;
 
2548
 
  const char helper_directory[] = "/nonexistent";
 
2549
 
  const char *const argv[] = { "/usr/bin/id", "--group", "--real",
 
2554
 
  const bool success = start_mandos_client(queue, epoll_fd,
 
2555
 
                                           &mandos_client_exited,
 
2556
 
                                           &quit_now, &password,
 
2558
 
                                           &fixture->orig_sigaction,
 
2559
 
                                           fixture->orig_sigmask,
 
2560
 
                                           helper_directory, user,
 
2562
 
  g_assert_true(success);
 
2563
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
2565
 
  struct timespec starttime, currtime;
 
2566
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2568
 
    queue->next_run = 0;
 
2569
 
    string_set cancelled_filenames = {};
 
2570
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2571
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2572
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2573
 
  } while(((queue->length) > 0)
 
2575
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2577
 
  g_assert_false(quit_now);
 
2578
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2579
 
  g_assert_true(mandos_client_exited);
 
2581
 
  g_assert_true(password_is_read);
 
2582
 
  g_assert_nonnull(password.data);
 
2585
 
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
2587
 
  g_assert_true((gid_t)id == id);
 
2589
 
  g_assert_cmpuint((unsigned int)id, ==, group);
 
2592
 
static void test_start_mandos_client_read(test_fixture *fixture,
 
2593
 
                                          __attribute__((unused))
 
2594
 
                                          gconstpointer user_data){
 
2595
 
  bool mandos_client_exited = false;
 
2596
 
  bool quit_now = false;
 
2597
 
  __attribute__((cleanup(cleanup_close)))
 
2598
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2599
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2600
 
  __attribute__((cleanup(cleanup_queue)))
 
2601
 
    task_queue *queue = create_queue();
 
2602
 
  g_assert_nonnull(queue);
 
2603
 
  __attribute__((cleanup(cleanup_buffer)))
 
2604
 
    buffer password = {};
 
2605
 
  bool password_is_read = false;
 
2606
 
  const char dummy_test_password[] = "dummy test password";
 
2607
 
  const char helper_directory[] = "/nonexistent";
 
2608
 
  const char *const argv[] = { "/bin/echo", "-n", dummy_test_password,
 
2611
 
  const bool success = start_mandos_client(queue, epoll_fd,
 
2612
 
                                           &mandos_client_exited,
 
2613
 
                                           &quit_now, &password,
 
2615
 
                                           &fixture->orig_sigaction,
 
2616
 
                                           fixture->orig_sigmask,
 
2617
 
                                           helper_directory, 0, 0,
 
2619
 
  g_assert_true(success);
 
2620
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
2622
 
  struct timespec starttime, currtime;
 
2623
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
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)
 
2632
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2634
 
  g_assert_false(quit_now);
 
2635
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2636
 
  g_assert_true(mandos_client_exited);
 
2638
 
  g_assert_true(password_is_read);
 
2639
 
  g_assert_cmpint((int)password.length, ==,
 
2640
 
                  sizeof(dummy_test_password)-1);
 
2641
 
  g_assert_nonnull(password.data);
 
2642
 
  g_assert_cmpint(memcmp(dummy_test_password, password.data,
 
2643
 
                         sizeof(dummy_test_password)-1), ==, 0);
 
2647
 
void test_start_mandos_client_helper_directory(test_fixture *fixture,
 
2648
 
                                               __attribute__((unused))
 
2651
 
  bool mandos_client_exited = false;
 
2652
 
  bool quit_now = false;
 
2653
 
  __attribute__((cleanup(cleanup_close)))
 
2654
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2655
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2656
 
  __attribute__((cleanup(cleanup_queue)))
 
2657
 
    task_queue *queue = create_queue();
 
2658
 
  g_assert_nonnull(queue);
 
2659
 
  __attribute__((cleanup(cleanup_buffer)))
 
2660
 
    buffer password = {};
 
2661
 
  bool password_is_read = false;
 
2662
 
  const char helper_directory[] = "/nonexistent";
 
2663
 
  const char *const argv[] = { "/bin/sh", "-c",
 
2664
 
    "printf %s \"${MANDOSPLUGINHELPERDIR}\"", NULL };
 
2666
 
  const bool success = start_mandos_client(queue, epoll_fd,
 
2667
 
                                           &mandos_client_exited,
 
2668
 
                                           &quit_now, &password,
 
2670
 
                                           &fixture->orig_sigaction,
 
2671
 
                                           fixture->orig_sigmask,
 
2672
 
                                           helper_directory, 0, 0,
 
2674
 
  g_assert_true(success);
 
2675
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
2677
 
  struct timespec starttime, currtime;
 
2678
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2680
 
    queue->next_run = 0;
 
2681
 
    string_set cancelled_filenames = {};
 
2682
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2683
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2684
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2685
 
  } while(((queue->length) > 0)
 
2687
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2689
 
  g_assert_false(quit_now);
 
2690
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2691
 
  g_assert_true(mandos_client_exited);
 
2693
 
  g_assert_true(password_is_read);
 
2694
 
  g_assert_cmpint((int)password.length, ==,
 
2695
 
                  sizeof(helper_directory)-1);
 
2696
 
  g_assert_nonnull(password.data);
 
2697
 
  g_assert_cmpint(memcmp(helper_directory, password.data,
 
2698
 
                         sizeof(helper_directory)-1), ==, 0);
 
2701
 
__attribute__((nonnull, warn_unused_result))
 
2702
 
static bool proc_status_sigblk_to_sigset(const char *const,
 
2705
 
static void test_start_mandos_client_sigmask(test_fixture *fixture,
 
2706
 
                                             __attribute__((unused))
 
2707
 
                                             gconstpointer user_data){
 
2708
 
  bool mandos_client_exited = false;
 
2709
 
  bool quit_now = false;
 
2710
 
  __attribute__((cleanup(cleanup_close)))
 
2711
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2712
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2713
 
  __attribute__((cleanup(cleanup_queue)))
 
2714
 
    task_queue *queue = create_queue();
 
2715
 
  g_assert_nonnull(queue);
 
2716
 
  __attribute__((cleanup(cleanup_buffer)))
 
2717
 
    buffer password = {};
 
2718
 
  bool password_is_read = false;
 
2719
 
  const char helper_directory[] = "/nonexistent";
 
2720
 
  /* see proc(5) for format of /proc/self/status */
 
2721
 
  const char *const argv[] = { "/usr/bin/awk",
 
2722
 
    "$1==\"SigBlk:\"{ print $2 }", "/proc/self/status", NULL };
 
2724
 
  g_assert_true(start_mandos_client(queue, epoll_fd,
 
2725
 
                                    &mandos_client_exited, &quit_now,
 
2726
 
                                    &password, &password_is_read,
 
2727
 
                                    &fixture->orig_sigaction,
 
2728
 
                                    fixture->orig_sigmask,
 
2729
 
                                    helper_directory, 0, 0, argv));
 
2731
 
  struct timespec starttime, currtime;
 
2732
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2734
 
    queue->next_run = 0;
 
2735
 
    string_set cancelled_filenames = {};
 
2736
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2737
 
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
2738
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2739
 
  } while((not (mandos_client_exited and password_is_read))
 
2741
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2742
 
  g_assert_true(mandos_client_exited);
 
2743
 
  g_assert_true(password_is_read);
 
2745
 
  sigset_t parsed_sigmask;
 
2746
 
  g_assert_true(proc_status_sigblk_to_sigset(password.data,
 
2749
 
  for(int signum = 1; signum < NSIG; signum++){
 
2750
 
    const bool has_signal = sigismember(&parsed_sigmask, signum);
 
2751
 
    if(sigismember(&fixture->orig_sigmask, signum)){
 
2752
 
      g_assert_true(has_signal);
 
2754
 
      g_assert_false(has_signal);
 
2759
 
__attribute__((nonnull, warn_unused_result))
 
2760
 
static bool proc_status_sigblk_to_sigset(const char *const sigblk,
 
2761
 
                                         sigset_t *const sigmask){
 
2762
 
  /* parse /proc/PID/status SigBlk value and convert to a sigset_t */
 
2763
 
  uintmax_t scanned_sigmask;
 
2764
 
  if(sscanf(sigblk, "%" SCNxMAX " ", &scanned_sigmask) != 1){
 
2767
 
  if(sigemptyset(sigmask) != 0){
 
2770
 
  for(int signum = 1; signum < NSIG; signum++){
 
2771
 
    if(scanned_sigmask & ((uintmax_t)1 << (signum-1))){
 
2772
 
      if(sigaddset(sigmask, signum) != 0){
 
2780
 
static void run_task_with_stderr_to_dev_null(const task_context task,
 
2781
 
                                             task_queue *const queue);
 
2784
 
void test_wait_for_mandos_client_exit_badpid(__attribute__((unused))
 
2785
 
                                             test_fixture *fixture,
 
2786
 
                                             __attribute__((unused))
 
2787
 
                                             gconstpointer user_data){
 
2789
 
  bool mandos_client_exited = false;
 
2790
 
  bool quit_now = false;
 
2792
 
  __attribute__((cleanup(cleanup_queue)))
 
2793
 
    task_queue *queue = create_queue();
 
2794
 
  g_assert_nonnull(queue);
 
2795
 
  const task_context task = {
 
2796
 
    .func=wait_for_mandos_client_exit,
 
2798
 
    .mandos_client_exited=&mandos_client_exited,
 
2799
 
    .quit_now=&quit_now,
 
2801
 
  run_task_with_stderr_to_dev_null(task, queue);
 
2803
 
  g_assert_false(mandos_client_exited);
 
2804
 
  g_assert_true(quit_now);
 
2805
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2808
 
static void run_task_with_stderr_to_dev_null(const task_context task,
 
2809
 
                                             task_queue *const queue){
 
2810
 
  FILE *real_stderr = stderr;
 
2811
 
  FILE *devnull = fopen("/dev/null", "we");
 
2812
 
  g_assert_nonnull(devnull);
 
2815
 
  task.func(task, queue);
 
2816
 
  stderr = real_stderr;
 
2818
 
  g_assert_cmpint(fclose(devnull), ==, 0);
 
2822
 
void test_wait_for_mandos_client_exit_noexit(test_fixture *fixture,
 
2823
 
                                             __attribute__((unused))
 
2824
 
                                             gconstpointer user_data){
 
2825
 
  bool mandos_client_exited = false;
 
2826
 
  bool quit_now = false;
 
2828
 
  pid_t create_eternal_process(void){
 
2829
 
    const pid_t pid = fork();
 
2830
 
    if(pid == 0){               /* Child */
 
2831
 
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
2832
 
        _exit(EXIT_FAILURE);
 
2834
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
2835
 
        _exit(EXIT_FAILURE);
 
2843
 
  pid_t pid = create_eternal_process();
 
2844
 
  g_assert_true(pid != -1);
 
2846
 
  __attribute__((cleanup(cleanup_queue)))
 
2847
 
    task_queue *queue = create_queue();
 
2848
 
  g_assert_nonnull(queue);
 
2849
 
  const task_context task = {
 
2850
 
    .func=wait_for_mandos_client_exit,
 
2852
 
    .mandos_client_exited=&mandos_client_exited,
 
2853
 
    .quit_now=&quit_now,
 
2855
 
  task.func(task, queue);
 
2857
 
  g_assert_false(mandos_client_exited);
 
2858
 
  g_assert_false(quit_now);
 
2859
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
2861
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
2862
 
        .func=wait_for_mandos_client_exit,
 
2864
 
        .mandos_client_exited=&mandos_client_exited,
 
2865
 
        .quit_now=&quit_now,
 
2870
 
void test_wait_for_mandos_client_exit_success(test_fixture *fixture,
 
2871
 
                                              __attribute__((unused))
 
2874
 
  bool mandos_client_exited = false;
 
2875
 
  bool quit_now = false;
 
2877
 
  pid_t create_successful_process(void){
 
2878
 
    const pid_t pid = fork();
 
2879
 
    if(pid == 0){               /* Child */
 
2880
 
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
2881
 
        _exit(EXIT_FAILURE);
 
2883
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
2884
 
        _exit(EXIT_FAILURE);
 
2890
 
  const pid_t pid = create_successful_process();
 
2891
 
  g_assert_true(pid != -1);
 
2893
 
  __attribute__((cleanup(cleanup_queue)))
 
2894
 
    task_queue *queue = create_queue();
 
2895
 
  g_assert_nonnull(queue);
 
2896
 
  const task_context initial_task = {
 
2897
 
    .func=wait_for_mandos_client_exit,
 
2899
 
    .mandos_client_exited=&mandos_client_exited,
 
2900
 
    .quit_now=&quit_now,
 
2902
 
  g_assert_true(add_to_queue(queue, initial_task));
 
2904
 
  struct timespec starttime, currtime;
 
2905
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2906
 
  __attribute__((cleanup(cleanup_close)))
 
2907
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2909
 
    queue->next_run = 0;
 
2910
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2911
 
    g_assert_true(run_queue(&queue, (string_set[]){{}}, &quit_now));
 
2912
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2913
 
  } while((not mandos_client_exited)
 
2915
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2917
 
  g_assert_true(mandos_client_exited);
 
2918
 
  g_assert_false(quit_now);
 
2919
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2923
 
void test_wait_for_mandos_client_exit_failure(test_fixture *fixture,
 
2924
 
                                              __attribute__((unused))
 
2927
 
  bool mandos_client_exited = false;
 
2928
 
  bool quit_now = false;
 
2930
 
  pid_t create_failing_process(void){
 
2931
 
    const pid_t pid = fork();
 
2932
 
    if(pid == 0){               /* Child */
 
2933
 
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
2934
 
        _exit(EXIT_FAILURE);
 
2936
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
2937
 
        _exit(EXIT_FAILURE);
 
2943
 
  const pid_t pid = create_failing_process();
 
2944
 
  g_assert_true(pid != -1);
 
2946
 
  __attribute__((cleanup(string_set_clear)))
 
2947
 
    string_set cancelled_filenames = {};
 
2948
 
  __attribute__((cleanup(cleanup_close)))
 
2949
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
2950
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
2951
 
  __attribute__((cleanup(cleanup_queue)))
 
2952
 
    task_queue *queue = create_queue();
 
2953
 
  g_assert_nonnull(queue);
 
2954
 
  g_assert_true(add_to_queue(queue, (task_context){
 
2955
 
        .func=wait_for_mandos_client_exit,
 
2957
 
        .mandos_client_exited=&mandos_client_exited,
 
2958
 
        .quit_now=&quit_now,
 
2961
 
  g_assert_true(sigismember(&fixture->orig_sigmask, SIGCHLD) == 0);
 
2963
 
  __attribute__((cleanup(cleanup_close)))
 
2964
 
    const int devnull_fd = open("/dev/null",
 
2965
 
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2966
 
  g_assert_cmpint(devnull_fd, >=, 0);
 
2967
 
  __attribute__((cleanup(cleanup_close)))
 
2968
 
    const int real_stderr_fd = dup(STDERR_FILENO);
 
2969
 
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
2971
 
  struct timespec starttime, currtime;
 
2972
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
2974
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
2975
 
    dup2(devnull_fd, STDERR_FILENO);
 
2976
 
    const bool success = run_queue(&queue, &cancelled_filenames,
 
2978
 
    dup2(real_stderr_fd, STDERR_FILENO);
 
2983
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
2984
 
  } while((not mandos_client_exited)
 
2986
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
2988
 
  g_assert_true(quit_now);
 
2989
 
  g_assert_true(mandos_client_exited);
 
2990
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
2994
 
void test_wait_for_mandos_client_exit_killed(test_fixture *fixture,
 
2995
 
                                             __attribute__((unused))
 
2996
 
                                             gconstpointer user_data){
 
2997
 
  bool mandos_client_exited = false;
 
2998
 
  bool quit_now = false;
 
3000
 
  pid_t create_killed_process(void){
 
3001
 
    const pid_t pid = fork();
 
3002
 
    if(pid == 0){               /* Child */
 
3003
 
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
3004
 
        _exit(EXIT_FAILURE);
 
3006
 
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
3007
 
        _exit(EXIT_FAILURE);
 
3016
 
  const pid_t pid = create_killed_process();
 
3017
 
  g_assert_true(pid != -1);
 
3019
 
  __attribute__((cleanup(string_set_clear)))
 
3020
 
    string_set cancelled_filenames = {};
 
3021
 
  __attribute__((cleanup(cleanup_close)))
 
3022
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3023
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3024
 
  __attribute__((cleanup(cleanup_queue)))
 
3025
 
    task_queue *queue = create_queue();
 
3026
 
  g_assert_nonnull(queue);
 
3027
 
  g_assert_true(add_to_queue(queue, (task_context){
 
3028
 
        .func=wait_for_mandos_client_exit,
 
3030
 
        .mandos_client_exited=&mandos_client_exited,
 
3031
 
        .quit_now=&quit_now,
 
3034
 
  __attribute__((cleanup(cleanup_close)))
 
3035
 
    const int devnull_fd = open("/dev/null",
 
3036
 
                                O_WRONLY | O_CLOEXEC, O_NOCTTY);
 
3037
 
  g_assert_cmpint(devnull_fd, >=, 0);
 
3038
 
  __attribute__((cleanup(cleanup_close)))
 
3039
 
    const int real_stderr_fd = dup(STDERR_FILENO);
 
3040
 
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
3042
 
  struct timespec starttime, currtime;
 
3043
 
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
3045
 
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
3046
 
    dup2(devnull_fd, STDERR_FILENO);
 
3047
 
    const bool success = run_queue(&queue, &cancelled_filenames,
 
3049
 
    dup2(real_stderr_fd, STDERR_FILENO);
 
3054
 
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
3055
 
  } while((not mandos_client_exited)
 
3057
 
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
3059
 
  g_assert_true(mandos_client_exited);
 
3060
 
  g_assert_true(quit_now);
 
3061
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
3064
 
static bool epoll_set_does_not_contain(int, int);
 
3067
 
void test_read_mandos_client_output_readerror(__attribute__((unused))
 
3068
 
                                              test_fixture *fixture,
 
3069
 
                                              __attribute__((unused))
 
3072
 
  __attribute__((cleanup(cleanup_close)))
 
3073
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3074
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3076
 
  __attribute__((cleanup(cleanup_buffer)))
 
3077
 
    buffer password = {};
 
3079
 
  /* Reading /proc/self/mem from offset 0 will always give EIO */
 
3080
 
  const int fd = open("/proc/self/mem",
 
3081
 
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
 
3083
 
  bool password_is_read = false;
 
3084
 
  bool quit_now = false;
 
3085
 
  __attribute__((cleanup(cleanup_queue)))
 
3086
 
    task_queue *queue = create_queue();
 
3087
 
  g_assert_nonnull(queue);
 
3089
 
  task_context task = {
 
3090
 
    .func=read_mandos_client_output,
 
3093
 
    .password=&password,
 
3094
 
    .password_is_read=&password_is_read,
 
3095
 
    .quit_now=&quit_now,
 
3097
 
  run_task_with_stderr_to_dev_null(task, queue);
 
3098
 
  g_assert_false(password_is_read);
 
3099
 
  g_assert_cmpint((int)password.length, ==, 0);
 
3100
 
  g_assert_true(quit_now);
 
3101
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
3103
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
 
3105
 
  g_assert_cmpint(close(fd), ==, -1);
 
3108
 
static bool epoll_set_does_not_contain(int epoll_fd, int fd){
 
3109
 
  return not epoll_set_contains(epoll_fd, fd, 0);
 
3113
 
void test_read_mandos_client_output_nodata(__attribute__((unused))
 
3114
 
                                           test_fixture *fixture,
 
3115
 
                                           __attribute__((unused))
 
3116
 
                                           gconstpointer user_data){
 
3117
 
  __attribute__((cleanup(cleanup_close)))
 
3118
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3119
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3122
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3124
 
  __attribute__((cleanup(cleanup_buffer)))
 
3125
 
    buffer password = {};
 
3127
 
  bool password_is_read = false;
 
3128
 
  bool quit_now = false;
 
3129
 
  __attribute__((cleanup(cleanup_queue)))
 
3130
 
    task_queue *queue = create_queue();
 
3131
 
  g_assert_nonnull(queue);
 
3133
 
  task_context task = {
 
3134
 
    .func=read_mandos_client_output,
 
3137
 
    .password=&password,
 
3138
 
    .password_is_read=&password_is_read,
 
3139
 
    .quit_now=&quit_now,
 
3141
 
  task.func(task, queue);
 
3142
 
  g_assert_false(password_is_read);
 
3143
 
  g_assert_cmpint((int)password.length, ==, 0);
 
3144
 
  g_assert_false(quit_now);
 
3145
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
3147
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
3148
 
        .func=read_mandos_client_output,
 
3151
 
        .password=&password,
 
3152
 
        .password_is_read=&password_is_read,
 
3153
 
        .quit_now=&quit_now,
 
3156
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
3157
 
                                   EPOLLIN | EPOLLRDHUP));
 
3159
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
3162
 
static void test_read_mandos_client_output_eof(__attribute__((unused))
 
3163
 
                                               test_fixture *fixture,
 
3164
 
                                               __attribute__((unused))
 
3167
 
  __attribute__((cleanup(cleanup_close)))
 
3168
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3169
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3172
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3173
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
3175
 
  __attribute__((cleanup(cleanup_buffer)))
 
3176
 
    buffer password = {};
 
3178
 
  bool password_is_read = false;
 
3179
 
  bool quit_now = false;
 
3180
 
  __attribute__((cleanup(cleanup_queue)))
 
3181
 
    task_queue *queue = create_queue();
 
3182
 
  g_assert_nonnull(queue);
 
3184
 
  task_context task = {
 
3185
 
    .func=read_mandos_client_output,
 
3188
 
    .password=&password,
 
3189
 
    .password_is_read=&password_is_read,
 
3190
 
    .quit_now=&quit_now,
 
3192
 
  task.func(task, queue);
 
3193
 
  g_assert_true(password_is_read);
 
3194
 
  g_assert_cmpint((int)password.length, ==, 0);
 
3195
 
  g_assert_false(quit_now);
 
3196
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
3198
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
 
3200
 
  g_assert_cmpint(close(pipefds[0]), ==, -1);
 
3204
 
void test_read_mandos_client_output_once(__attribute__((unused))
 
3205
 
                                         test_fixture *fixture,
 
3206
 
                                         __attribute__((unused))
 
3207
 
                                         gconstpointer user_data){
 
3208
 
  __attribute__((cleanup(cleanup_close)))
 
3209
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3210
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3213
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3215
 
  const char dummy_test_password[] = "dummy test password";
 
3216
 
  /* Start with a pre-allocated buffer */
 
3217
 
  __attribute__((cleanup(cleanup_buffer)))
 
3219
 
    .data=malloc(sizeof(dummy_test_password)),
 
3221
 
    .allocated=sizeof(dummy_test_password),
 
3223
 
  g_assert_nonnull(password.data);
 
3224
 
  if(mlock(password.data, password.allocated) != 0){
 
3225
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
3228
 
  bool password_is_read = false;
 
3229
 
  bool quit_now = false;
 
3230
 
  __attribute__((cleanup(cleanup_queue)))
 
3231
 
    task_queue *queue = create_queue();
 
3232
 
  g_assert_nonnull(queue);
 
3234
 
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
3235
 
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
3236
 
                             sizeof(dummy_test_password)),
 
3237
 
                  ==, (int)sizeof(dummy_test_password));
 
3239
 
  task_context task = {
 
3240
 
    .func=read_mandos_client_output,
 
3243
 
    .password=&password,
 
3244
 
    .password_is_read=&password_is_read,
 
3245
 
    .quit_now=&quit_now,
 
3247
 
  task.func(task, queue);
 
3249
 
  g_assert_false(password_is_read);
 
3250
 
  g_assert_cmpint((int)password.length, ==,
 
3251
 
                  (int)sizeof(dummy_test_password));
 
3252
 
  g_assert_nonnull(password.data);
 
3253
 
  g_assert_cmpint(memcmp(password.data, dummy_test_password,
 
3254
 
                         sizeof(dummy_test_password)), ==, 0);
 
3256
 
  g_assert_false(quit_now);
 
3257
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
3259
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
3260
 
        .func=read_mandos_client_output,
 
3263
 
        .password=&password,
 
3264
 
        .password_is_read=&password_is_read,
 
3265
 
        .quit_now=&quit_now,
 
3268
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
3269
 
                                   EPOLLIN | EPOLLRDHUP));
 
3271
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
3275
 
void test_read_mandos_client_output_malloc(__attribute__((unused))
 
3276
 
                                           test_fixture *fixture,
 
3277
 
                                           __attribute__((unused))
 
3278
 
                                           gconstpointer user_data){
 
3279
 
  __attribute__((cleanup(cleanup_close)))
 
3280
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3281
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3284
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3286
 
  const char dummy_test_password[] = "dummy test password";
 
3287
 
  /* Start with an empty buffer */
 
3288
 
  __attribute__((cleanup(cleanup_buffer)))
 
3289
 
    buffer password = {};
 
3291
 
  bool password_is_read = false;
 
3292
 
  bool quit_now = false;
 
3293
 
  __attribute__((cleanup(cleanup_queue)))
 
3294
 
    task_queue *queue = create_queue();
 
3295
 
  g_assert_nonnull(queue);
 
3297
 
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
3298
 
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
3299
 
                             sizeof(dummy_test_password)),
 
3300
 
                  ==, (int)sizeof(dummy_test_password));
 
3302
 
  task_context task = {
 
3303
 
    .func=read_mandos_client_output,
 
3306
 
    .password=&password,
 
3307
 
    .password_is_read=&password_is_read,
 
3308
 
    .quit_now=&quit_now,
 
3310
 
  task.func(task, queue);
 
3312
 
  g_assert_false(password_is_read);
 
3313
 
  g_assert_cmpint((int)password.length, ==,
 
3314
 
                  (int)sizeof(dummy_test_password));
 
3315
 
  g_assert_nonnull(password.data);
 
3316
 
  g_assert_cmpint(memcmp(password.data, dummy_test_password,
 
3317
 
                         sizeof(dummy_test_password)), ==, 0);
 
3319
 
  g_assert_false(quit_now);
 
3320
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
3322
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
3323
 
        .func=read_mandos_client_output,
 
3326
 
        .password=&password,
 
3327
 
        .password_is_read=&password_is_read,
 
3328
 
        .quit_now=&quit_now,
 
3331
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
3332
 
                                   EPOLLIN | EPOLLRDHUP));
 
3334
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
3338
 
void test_read_mandos_client_output_append(__attribute__((unused))
 
3339
 
                                           test_fixture *fixture,
 
3340
 
                                           __attribute__((unused))
 
3341
 
                                           gconstpointer user_data){
 
3342
 
  __attribute__((cleanup(cleanup_close)))
 
3343
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3344
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3347
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
3349
 
  const char dummy_test_password[] = "dummy test password";
 
3350
 
  __attribute__((cleanup(cleanup_buffer)))
 
3352
 
    .data=malloc(PIPE_BUF),
 
3354
 
    .allocated=PIPE_BUF,
 
3356
 
  g_assert_nonnull(password.data);
 
3357
 
  if(mlock(password.data, password.allocated) != 0){
 
3358
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
3361
 
  memset(password.data, 'x', PIPE_BUF);
 
3362
 
  char password_expected[PIPE_BUF];
 
3363
 
  memcpy(password_expected, password.data, PIPE_BUF);
 
3365
 
  bool password_is_read = false;
 
3366
 
  bool quit_now = false;
 
3367
 
  __attribute__((cleanup(cleanup_queue)))
 
3368
 
    task_queue *queue = create_queue();
 
3369
 
  g_assert_nonnull(queue);
 
3371
 
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
3372
 
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
3373
 
                             sizeof(dummy_test_password)),
 
3374
 
                  ==, (int)sizeof(dummy_test_password));
 
3376
 
  task_context task = {
 
3377
 
    .func=read_mandos_client_output,
 
3380
 
    .password=&password,
 
3381
 
    .password_is_read=&password_is_read,
 
3382
 
    .quit_now=&quit_now,
 
3384
 
  task.func(task, queue);
 
3386
 
  g_assert_false(password_is_read);
 
3387
 
  g_assert_cmpint((int)password.length, ==,
 
3388
 
                  PIPE_BUF + sizeof(dummy_test_password));
 
3389
 
  g_assert_nonnull(password.data);
 
3390
 
  g_assert_cmpint(memcmp(password_expected, password.data, PIPE_BUF),
 
3392
 
  g_assert_cmpint(memcmp(password.data + PIPE_BUF,
 
3393
 
                         dummy_test_password,
 
3394
 
                         sizeof(dummy_test_password)), ==, 0);
 
3395
 
  g_assert_false(quit_now);
 
3396
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
3398
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
3399
 
        .func=read_mandos_client_output,
 
3402
 
        .password=&password,
 
3403
 
        .password_is_read=&password_is_read,
 
3404
 
        .quit_now=&quit_now,
 
3407
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
3408
 
                                   EPOLLIN | EPOLLRDHUP));
 
3411
 
static char *make_temporary_directory(void);
 
3413
 
static void test_add_inotify_dir_watch(__attribute__((unused))
 
3414
 
                                       test_fixture *fixture,
 
3415
 
                                       __attribute__((unused))
 
3416
 
                                       gconstpointer user_data){
 
3417
 
  __attribute__((cleanup(cleanup_close)))
 
3418
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3419
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3420
 
  __attribute__((cleanup(cleanup_queue)))
 
3421
 
    task_queue *queue = create_queue();
 
3422
 
  g_assert_nonnull(queue);
 
3423
 
  __attribute__((cleanup(string_set_clear)))
 
3424
 
    string_set cancelled_filenames = {};
 
3425
 
  const mono_microsecs current_time = 0;
 
3427
 
  bool quit_now = false;
 
3428
 
  buffer password = {};
 
3429
 
  bool mandos_client_exited = false;
 
3430
 
  bool password_is_read = false;
 
3432
 
  __attribute__((cleanup(cleanup_string)))
 
3433
 
    char *tempdir = make_temporary_directory();
 
3434
 
  g_assert_nonnull(tempdir);
 
3436
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3438
 
                                      &cancelled_filenames,
 
3440
 
                                      &mandos_client_exited,
 
3441
 
                                      &password_is_read));
 
3443
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
3445
 
  const task_context *const added_read_task
 
3446
 
    = find_matching_task(queue, (task_context){
 
3447
 
        .func=read_inotify_event,
 
3449
 
        .quit_now=&quit_now,
 
3450
 
        .password=&password,
 
3452
 
        .cancelled_filenames=&cancelled_filenames,
 
3453
 
        .current_time=¤t_time,
 
3454
 
        .mandos_client_exited=&mandos_client_exited,
 
3455
 
        .password_is_read=&password_is_read,
 
3457
 
  g_assert_nonnull(added_read_task);
 
3459
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
3460
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
3461
 
  g_assert_true(epoll_set_contains(added_read_task->epoll_fd,
 
3462
 
                                   added_read_task->fd,
 
3463
 
                                   EPOLLIN | EPOLLRDHUP));
 
3465
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
3468
 
static char *make_temporary_directory(void){
 
3469
 
  char *name = strdup("/tmp/mandosXXXXXX");
 
3470
 
  g_assert_nonnull(name);
 
3471
 
  char *result = mkdtemp(name);
 
3478
 
static void test_add_inotify_dir_watch_fail(__attribute__((unused))
 
3479
 
                                            test_fixture *fixture,
 
3480
 
                                            __attribute__((unused))
 
3481
 
                                            gconstpointer user_data){
 
3482
 
  __attribute__((cleanup(cleanup_close)))
 
3483
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3484
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3485
 
  __attribute__((cleanup(cleanup_queue)))
 
3486
 
    task_queue *queue = create_queue();
 
3487
 
  g_assert_nonnull(queue);
 
3488
 
  __attribute__((cleanup(string_set_clear)))
 
3489
 
    string_set cancelled_filenames = {};
 
3490
 
  const mono_microsecs current_time = 0;
 
3492
 
  bool quit_now = false;
 
3493
 
  buffer password = {};
 
3494
 
  bool mandos_client_exited = false;
 
3495
 
  bool password_is_read = false;
 
3497
 
  const char nonexistent_dir[] = "/nonexistent";
 
3499
 
  FILE *real_stderr = stderr;
 
3500
 
  FILE *devnull = fopen("/dev/null", "we");
 
3501
 
  g_assert_nonnull(devnull);
 
3503
 
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3504
 
                                       &password, nonexistent_dir,
 
3505
 
                                       &cancelled_filenames,
 
3507
 
                                       &mandos_client_exited,
 
3508
 
                                       &password_is_read));
 
3509
 
  stderr = real_stderr;
 
3510
 
  g_assert_cmpint(fclose(devnull), ==, 0);
 
3512
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
3515
 
static void test_add_inotify_dir_watch_nondir(__attribute__((unused))
 
3516
 
                                              test_fixture *fixture,
 
3517
 
                                            __attribute__((unused))
 
3520
 
  __attribute__((cleanup(cleanup_close)))
 
3521
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3522
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3523
 
  __attribute__((cleanup(cleanup_queue)))
 
3524
 
    task_queue *queue = create_queue();
 
3525
 
  g_assert_nonnull(queue);
 
3526
 
  __attribute__((cleanup(string_set_clear)))
 
3527
 
    string_set cancelled_filenames = {};
 
3528
 
  const mono_microsecs current_time = 0;
 
3530
 
  bool quit_now = false;
 
3531
 
  buffer password = {};
 
3532
 
  bool mandos_client_exited = false;
 
3533
 
  bool password_is_read = false;
 
3535
 
  const char not_a_directory[] = "/dev/tty";
 
3537
 
  FILE *real_stderr = stderr;
 
3538
 
  FILE *devnull = fopen("/dev/null", "we");
 
3539
 
  g_assert_nonnull(devnull);
 
3541
 
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3542
 
                                       &password, not_a_directory,
 
3543
 
                                       &cancelled_filenames,
 
3545
 
                                       &mandos_client_exited,
 
3546
 
                                       &password_is_read));
 
3547
 
  stderr = real_stderr;
 
3548
 
  g_assert_cmpint(fclose(devnull), ==, 0);
 
3550
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
3553
 
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
 
3554
 
                                              test_fixture *fixture,
 
3555
 
                                              __attribute__((unused))
 
3558
 
  __attribute__((cleanup(cleanup_close)))
 
3559
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3560
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3561
 
  __attribute__((cleanup(cleanup_queue)))
 
3562
 
    task_queue *queue = create_queue();
 
3563
 
  g_assert_nonnull(queue);
 
3564
 
  __attribute__((cleanup(string_set_clear)))
 
3565
 
    string_set cancelled_filenames = {};
 
3566
 
  const mono_microsecs current_time = 0;
 
3568
 
  bool quit_now = false;
 
3569
 
  buffer password = {};
 
3570
 
  bool mandos_client_exited = false;
 
3571
 
  bool password_is_read = false;
 
3573
 
  __attribute__((cleanup(cleanup_string)))
 
3574
 
    char *tempdir = make_temporary_directory();
 
3575
 
  g_assert_nonnull(tempdir);
 
3577
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3579
 
                                      &cancelled_filenames,
 
3581
 
                                      &mandos_client_exited,
 
3582
 
                                      &password_is_read));
 
3584
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
3586
 
  const task_context *const added_read_task
 
3587
 
    = find_matching_task(queue,
 
3588
 
                         (task_context){ .func=read_inotify_event });
 
3589
 
  g_assert_nonnull(added_read_task);
 
3591
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
3592
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
3594
 
  /* "sufficient to read at least one event." - inotify(7) */
 
3595
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3597
 
  struct inotify_event *ievent = malloc(ievent_size);
 
3598
 
  g_assert_nonnull(ievent);
 
3600
 
  g_assert_cmpint(read(added_read_task->fd, ievent, ievent_size), ==,
 
3602
 
  g_assert_cmpint(errno, ==, EAGAIN);
 
3606
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
3609
 
static char *make_temporary_file_in_directory(const char
 
3613
 
void test_add_inotify_dir_watch_IN_CLOSE_WRITE(__attribute__((unused))
 
3614
 
                                               test_fixture *fixture,
 
3615
 
                                               __attribute__((unused))
 
3618
 
  __attribute__((cleanup(cleanup_close)))
 
3619
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3620
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3621
 
  __attribute__((cleanup(cleanup_queue)))
 
3622
 
    task_queue *queue = create_queue();
 
3623
 
  g_assert_nonnull(queue);
 
3624
 
  __attribute__((cleanup(string_set_clear)))
 
3625
 
    string_set cancelled_filenames = {};
 
3626
 
  const mono_microsecs current_time = 0;
 
3628
 
  bool quit_now = false;
 
3629
 
  buffer password = {};
 
3630
 
  bool mandos_client_exited = false;
 
3631
 
  bool password_is_read = false;
 
3633
 
  __attribute__((cleanup(cleanup_string)))
 
3634
 
    char *tempdir = make_temporary_directory();
 
3635
 
  g_assert_nonnull(tempdir);
 
3637
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3639
 
                                      &cancelled_filenames,
 
3641
 
                                      &mandos_client_exited,
 
3642
 
                                      &password_is_read));
 
3644
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
3646
 
  const task_context *const added_read_task
 
3647
 
    = find_matching_task(queue,
 
3648
 
                         (task_context){ .func=read_inotify_event });
 
3649
 
  g_assert_nonnull(added_read_task);
 
3651
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
3652
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
3654
 
  __attribute__((cleanup(cleanup_string)))
 
3655
 
    char *filename = make_temporary_file_in_directory(tempdir);
 
3656
 
  g_assert_nonnull(filename);
 
3658
 
  /* "sufficient to read at least one event." - inotify(7) */
 
3659
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3661
 
  struct inotify_event *ievent = malloc(ievent_size);
 
3662
 
  g_assert_nonnull(ievent);
 
3664
 
  ssize_t read_size = 0;
 
3665
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
3667
 
  g_assert_cmpint((int)read_size, >, 0);
 
3668
 
  g_assert_true(ievent->mask & IN_CLOSE_WRITE);
 
3669
 
  g_assert_cmpstr(ievent->name, ==, basename(filename));
 
3673
 
  g_assert_cmpint(unlink(filename), ==, 0);
 
3674
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
3677
 
static char *make_temporary_prefixed_file_in_directory(const char
 
3681
 
  char *filename = NULL;
 
3682
 
  g_assert_cmpint(asprintf(&filename, "%s/%sXXXXXX", dir, prefix),
 
3684
 
  g_assert_nonnull(filename);
 
3685
 
  const int fd = mkostemp(filename, O_CLOEXEC);
 
3686
 
  g_assert_cmpint(fd, >=, 0);
 
3687
 
  g_assert_cmpint(close(fd), ==, 0);
 
3691
 
static char *make_temporary_file_in_directory(const char
 
3693
 
  return make_temporary_prefixed_file_in_directory("temp", dir);
 
3697
 
void test_add_inotify_dir_watch_IN_MOVED_TO(__attribute__((unused))
 
3698
 
                                            test_fixture *fixture,
 
3699
 
                                            __attribute__((unused))
 
3700
 
                                            gconstpointer user_data){
 
3701
 
  __attribute__((cleanup(cleanup_close)))
 
3702
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3703
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3704
 
  __attribute__((cleanup(cleanup_queue)))
 
3705
 
    task_queue *queue = create_queue();
 
3706
 
  g_assert_nonnull(queue);
 
3707
 
  __attribute__((cleanup(string_set_clear)))
 
3708
 
    string_set cancelled_filenames = {};
 
3709
 
  const mono_microsecs current_time = 0;
 
3711
 
  bool quit_now = false;
 
3712
 
  buffer password = {};
 
3713
 
  bool mandos_client_exited = false;
 
3714
 
  bool password_is_read = false;
 
3716
 
  __attribute__((cleanup(cleanup_string)))
 
3717
 
    char *watchdir = make_temporary_directory();
 
3718
 
  g_assert_nonnull(watchdir);
 
3720
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3721
 
                                      &password, watchdir,
 
3722
 
                                      &cancelled_filenames,
 
3724
 
                                      &mandos_client_exited,
 
3725
 
                                      &password_is_read));
 
3727
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
3729
 
  const task_context *const added_read_task
 
3730
 
    = find_matching_task(queue,
 
3731
 
                         (task_context){ .func=read_inotify_event });
 
3732
 
  g_assert_nonnull(added_read_task);
 
3734
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
3735
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
3737
 
  char *sourcedir = make_temporary_directory();
 
3738
 
  g_assert_nonnull(sourcedir);
 
3740
 
  __attribute__((cleanup(cleanup_string)))
 
3741
 
    char *filename = make_temporary_file_in_directory(sourcedir);
 
3742
 
  g_assert_nonnull(filename);
 
3744
 
  __attribute__((cleanup(cleanup_string)))
 
3745
 
    char *targetfilename = NULL;
 
3746
 
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", watchdir,
 
3747
 
                           basename(filename)), >, 0);
 
3748
 
  g_assert_nonnull(targetfilename);
 
3750
 
  g_assert_cmpint(rename(filename, targetfilename), ==, 0);
 
3751
 
  g_assert_cmpint(rmdir(sourcedir), ==, 0);
 
3754
 
  /* "sufficient to read at least one event." - inotify(7) */
 
3755
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3757
 
  struct inotify_event *ievent = malloc(ievent_size);
 
3758
 
  g_assert_nonnull(ievent);
 
3760
 
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
 
3762
 
  g_assert_cmpint((int)read_size, >, 0);
 
3763
 
  g_assert_true(ievent->mask & IN_MOVED_TO);
 
3764
 
  g_assert_cmpstr(ievent->name, ==, basename(targetfilename));
 
3768
 
  g_assert_cmpint(unlink(targetfilename), ==, 0);
 
3769
 
  g_assert_cmpint(rmdir(watchdir), ==, 0);
 
3773
 
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
 
3774
 
                                              test_fixture *fixture,
 
3775
 
                                              __attribute__((unused))
 
3778
 
  __attribute__((cleanup(cleanup_close)))
 
3779
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3780
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3781
 
  __attribute__((cleanup(cleanup_queue)))
 
3782
 
    task_queue *queue = create_queue();
 
3783
 
  g_assert_nonnull(queue);
 
3784
 
  __attribute__((cleanup(string_set_clear)))
 
3785
 
    string_set cancelled_filenames = {};
 
3786
 
  const mono_microsecs current_time = 0;
 
3788
 
  bool quit_now = false;
 
3789
 
  buffer password = {};
 
3790
 
  bool mandos_client_exited = false;
 
3791
 
  bool password_is_read = false;
 
3793
 
  __attribute__((cleanup(cleanup_string)))
 
3794
 
    char *tempdir = make_temporary_directory();
 
3795
 
  g_assert_nonnull(tempdir);
 
3797
 
  __attribute__((cleanup(cleanup_string)))
 
3798
 
    char *tempfilename = make_temporary_file_in_directory(tempdir);
 
3799
 
  g_assert_nonnull(tempfilename);
 
3801
 
  __attribute__((cleanup(cleanup_string)))
 
3802
 
    char *targetdir = make_temporary_directory();
 
3803
 
  g_assert_nonnull(targetdir);
 
3805
 
  __attribute__((cleanup(cleanup_string)))
 
3806
 
    char *targetfilename = NULL;
 
3807
 
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
 
3808
 
                           basename(tempfilename)), >, 0);
 
3809
 
  g_assert_nonnull(targetfilename);
 
3811
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3813
 
                                      &cancelled_filenames,
 
3815
 
                                      &mandos_client_exited,
 
3816
 
                                      &password_is_read));
 
3818
 
  g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
 
3820
 
  const task_context *const added_read_task
 
3821
 
    = find_matching_task(queue,
 
3822
 
                         (task_context){ .func=read_inotify_event });
 
3823
 
  g_assert_nonnull(added_read_task);
 
3825
 
  /* "sufficient to read at least one event." - inotify(7) */
 
3826
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3828
 
  struct inotify_event *ievent = malloc(ievent_size);
 
3829
 
  g_assert_nonnull(ievent);
 
3831
 
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
 
3833
 
  g_assert_cmpint((int)read_size, >, 0);
 
3834
 
  g_assert_true(ievent->mask & IN_MOVED_FROM);
 
3835
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
 
3839
 
  g_assert_cmpint(unlink(targetfilename), ==, 0);
 
3840
 
  g_assert_cmpint(rmdir(targetdir), ==, 0);
 
3841
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
3845
 
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
 
3846
 
                                          test_fixture *fixture,
 
3847
 
                                          __attribute__((unused))
 
3848
 
                                          gconstpointer user_data){
 
3849
 
  __attribute__((cleanup(cleanup_close)))
 
3850
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3851
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3852
 
  __attribute__((cleanup(cleanup_queue)))
 
3853
 
    task_queue *queue = create_queue();
 
3854
 
  g_assert_nonnull(queue);
 
3855
 
  __attribute__((cleanup(string_set_clear)))
 
3856
 
    string_set cancelled_filenames = {};
 
3857
 
  const mono_microsecs current_time = 0;
 
3859
 
  bool quit_now = false;
 
3860
 
  buffer password = {};
 
3861
 
  bool mandos_client_exited = false;
 
3862
 
  bool password_is_read = false;
 
3864
 
  __attribute__((cleanup(cleanup_string)))
 
3865
 
    char *tempdir = make_temporary_directory();
 
3866
 
  g_assert_nonnull(tempdir);
 
3868
 
  __attribute__((cleanup(cleanup_string)))
 
3869
 
    char *tempfile = make_temporary_file_in_directory(tempdir);
 
3870
 
  g_assert_nonnull(tempfile);
 
3872
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3874
 
                                      &cancelled_filenames,
 
3876
 
                                      &mandos_client_exited,
 
3877
 
                                      &password_is_read));
 
3878
 
  g_assert_cmpint(unlink(tempfile), ==, 0);
 
3880
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
3882
 
  const task_context *const added_read_task
 
3883
 
    = find_matching_task(queue,
 
3884
 
                         (task_context){ .func=read_inotify_event });
 
3885
 
  g_assert_nonnull(added_read_task);
 
3887
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
3888
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
3890
 
  /* "sufficient to read at least one event." - inotify(7) */
 
3891
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3893
 
  struct inotify_event *ievent = malloc(ievent_size);
 
3894
 
  g_assert_nonnull(ievent);
 
3896
 
  ssize_t read_size = 0;
 
3897
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
3899
 
  g_assert_cmpint((int)read_size, >, 0);
 
3900
 
  g_assert_true(ievent->mask & IN_DELETE);
 
3901
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
 
3905
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
3909
 
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
 
3910
 
                                               test_fixture *fixture,
 
3911
 
                                               __attribute__((unused))
 
3914
 
  __attribute__((cleanup(cleanup_close)))
 
3915
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3916
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3917
 
  __attribute__((cleanup(cleanup_queue)))
 
3918
 
    task_queue *queue = create_queue();
 
3919
 
  g_assert_nonnull(queue);
 
3920
 
  __attribute__((cleanup(string_set_clear)))
 
3921
 
    string_set cancelled_filenames = {};
 
3922
 
  const mono_microsecs current_time = 0;
 
3924
 
  bool quit_now = false;
 
3925
 
  buffer password = {};
 
3926
 
  bool mandos_client_exited = false;
 
3927
 
  bool password_is_read = false;
 
3929
 
  __attribute__((cleanup(cleanup_string)))
 
3930
 
    char *tempdir = make_temporary_directory();
 
3931
 
  g_assert_nonnull(tempdir);
 
3933
 
  __attribute__((cleanup(cleanup_string)))
 
3934
 
    char *tempfile = make_temporary_file_in_directory(tempdir);
 
3935
 
  g_assert_nonnull(tempfile);
 
3936
 
  int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
 
3938
 
  g_assert_cmpint(tempfile_fd, >, 2);
 
3940
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3942
 
                                      &cancelled_filenames,
 
3944
 
                                      &mandos_client_exited,
 
3945
 
                                      &password_is_read));
 
3946
 
  g_assert_cmpint(unlink(tempfile), ==, 0);
 
3948
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
3950
 
  const task_context *const added_read_task
 
3951
 
    = find_matching_task(queue,
 
3952
 
                         (task_context){ .func=read_inotify_event });
 
3953
 
  g_assert_nonnull(added_read_task);
 
3955
 
  g_assert_cmpint(added_read_task->fd, >, 2);
 
3956
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
3958
 
  /* "sufficient to read at least one event." - inotify(7) */
 
3959
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3961
 
  struct inotify_event *ievent = malloc(ievent_size);
 
3962
 
  g_assert_nonnull(ievent);
 
3964
 
  ssize_t read_size = 0;
 
3965
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
3967
 
  g_assert_cmpint((int)read_size, >, 0);
 
3968
 
  g_assert_true(ievent->mask & IN_DELETE);
 
3969
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
 
3971
 
  g_assert_cmpint(close(tempfile_fd), ==, 0);
 
3973
 
  /* IN_EXCL_UNLINK should make the closing of the previously unlinked
 
3974
 
     file not appear as an ievent, so we should not see it now. */
 
3975
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
3976
 
  g_assert_cmpint((int)read_size, ==, -1);
 
3977
 
  g_assert_true(errno == EAGAIN);
 
3981
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
3984
 
static void test_read_inotify_event_readerror(__attribute__((unused))
 
3985
 
                                              test_fixture *fixture,
 
3986
 
                                              __attribute__((unused))
 
3989
 
  __attribute__((cleanup(cleanup_close)))
 
3990
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3991
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
3992
 
  const mono_microsecs current_time = 0;
 
3994
 
  /* Reading /proc/self/mem from offset 0 will always result in EIO */
 
3995
 
  const int fd = open("/proc/self/mem",
 
3996
 
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
 
3998
 
  bool quit_now = false;
 
3999
 
  __attribute__((cleanup(cleanup_queue)))
 
4000
 
    task_queue *queue = create_queue();
 
4001
 
  g_assert_nonnull(queue);
 
4003
 
  task_context task = {
 
4004
 
    .func=read_inotify_event,
 
4007
 
    .quit_now=&quit_now,
 
4008
 
    .filename=strdup("/nonexistent"),
 
4009
 
    .cancelled_filenames = &(string_set){},
 
4011
 
    .current_time=¤t_time,
 
4013
 
  g_assert_nonnull(task.filename);
 
4014
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4015
 
  g_assert_true(quit_now);
 
4016
 
  g_assert_true(queue->next_run == 0);
 
4017
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4019
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
 
4021
 
  g_assert_cmpint(close(fd), ==, -1);
 
4024
 
static void test_read_inotify_event_bad_epoll(__attribute__((unused))
 
4025
 
                                              test_fixture *fixture,
 
4026
 
                                              __attribute__((unused))
 
4029
 
  const mono_microsecs current_time = 17;
 
4032
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4033
 
  const int epoll_fd = pipefds[0]; /* This will obviously fail */
 
4035
 
  bool quit_now = false;
 
4036
 
  buffer password = {};
 
4037
 
  bool mandos_client_exited = false;
 
4038
 
  bool password_is_read = false;
 
4039
 
  __attribute__((cleanup(cleanup_queue)))
 
4040
 
    task_queue *queue = create_queue();
 
4041
 
  g_assert_nonnull(queue);
 
4043
 
  task_context task = {
 
4044
 
    .func=read_inotify_event,
 
4047
 
    .quit_now=&quit_now,
 
4048
 
    .password=&password,
 
4049
 
    .filename=strdup("/nonexistent"),
 
4050
 
    .cancelled_filenames = &(string_set){},
 
4052
 
    .current_time=¤t_time,
 
4053
 
    .mandos_client_exited=&mandos_client_exited,
 
4054
 
    .password_is_read=&password_is_read,
 
4056
 
  g_assert_nonnull(task.filename);
 
4057
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4059
 
  g_assert_nonnull(find_matching_task(queue, task));
 
4060
 
  g_assert_true(queue->next_run == 1000000 + current_time);
 
4062
 
  g_assert_cmpint(close(pipefds[0]), ==, 0);
 
4063
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4066
 
static void test_read_inotify_event_nodata(__attribute__((unused))
 
4067
 
                                           test_fixture *fixture,
 
4068
 
                                           __attribute__((unused))
 
4069
 
                                           gconstpointer user_data){
 
4070
 
  __attribute__((cleanup(cleanup_close)))
 
4071
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4072
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4073
 
  const mono_microsecs current_time = 0;
 
4076
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4078
 
  bool quit_now = false;
 
4079
 
  buffer password = {};
 
4080
 
  bool mandos_client_exited = false;
 
4081
 
  bool password_is_read = false;
 
4082
 
  __attribute__((cleanup(cleanup_queue)))
 
4083
 
    task_queue *queue = create_queue();
 
4084
 
  g_assert_nonnull(queue);
 
4086
 
  task_context task = {
 
4087
 
    .func=read_inotify_event,
 
4090
 
    .quit_now=&quit_now,
 
4091
 
    .password=&password,
 
4092
 
    .filename=strdup("/nonexistent"),
 
4093
 
    .cancelled_filenames = &(string_set){},
 
4095
 
    .current_time=¤t_time,
 
4096
 
    .mandos_client_exited=&mandos_client_exited,
 
4097
 
    .password_is_read=&password_is_read,
 
4099
 
  g_assert_nonnull(task.filename);
 
4100
 
  task.func(task, queue);
 
4101
 
  g_assert_false(quit_now);
 
4102
 
  g_assert_true(queue->next_run == 0);
 
4103
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4105
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4106
 
        .func=read_inotify_event,
 
4109
 
        .quit_now=&quit_now,
 
4110
 
        .password=&password,
 
4111
 
        .filename=task.filename,
 
4112
 
        .cancelled_filenames=task.cancelled_filenames,
 
4113
 
        .current_time=¤t_time,
 
4114
 
        .mandos_client_exited=&mandos_client_exited,
 
4115
 
        .password_is_read=&password_is_read,
 
4118
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4119
 
                                   EPOLLIN | EPOLLRDHUP));
 
4121
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4124
 
static void test_read_inotify_event_eof(__attribute__((unused))
 
4125
 
                                        test_fixture *fixture,
 
4126
 
                                        __attribute__((unused))
 
4127
 
                                        gconstpointer user_data){
 
4128
 
  __attribute__((cleanup(cleanup_close)))
 
4129
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4130
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4131
 
  const mono_microsecs current_time = 0;
 
4134
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4135
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4137
 
  bool quit_now = false;
 
4138
 
  buffer password = {};
 
4139
 
  __attribute__((cleanup(cleanup_queue)))
 
4140
 
    task_queue *queue = create_queue();
 
4141
 
  g_assert_nonnull(queue);
 
4143
 
  task_context task = {
 
4144
 
    .func=read_inotify_event,
 
4147
 
    .quit_now=&quit_now,
 
4148
 
    .password=&password,
 
4149
 
    .filename=strdup("/nonexistent"),
 
4150
 
    .cancelled_filenames = &(string_set){},
 
4152
 
    .current_time=¤t_time,
 
4154
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4155
 
  g_assert_true(quit_now);
 
4156
 
  g_assert_true(queue->next_run == 0);
 
4157
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4159
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
 
4161
 
  g_assert_cmpint(close(pipefds[0]), ==, -1);
 
4165
 
void test_read_inotify_event_IN_CLOSE_WRITE(__attribute__((unused))
 
4166
 
                                            test_fixture *fixture,
 
4167
 
                                            __attribute__((unused))
 
4168
 
                                            gconstpointer user_data){
 
4169
 
  __attribute__((cleanup(cleanup_close)))
 
4170
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4171
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4172
 
  const mono_microsecs current_time = 0;
 
4175
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4177
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4178
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4180
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4182
 
    struct inotify_event event;
 
4183
 
    char name_buffer[NAME_MAX + 1];
 
4185
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4187
 
  const char dummy_file_name[] = "ask.dummy_file_name";
 
4188
 
  ievent->mask = IN_CLOSE_WRITE;
 
4189
 
  ievent->len = sizeof(dummy_file_name);
 
4190
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4191
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4192
 
                              + sizeof(dummy_file_name));
 
4193
 
#if defined(__GNUC__) and __GNUC__ >= 11
 
4194
 
#pragma GCC diagnostic push
 
4195
 
  /* ievent is pointing into a struct which is of sufficient size */
 
4196
 
#pragma GCC diagnostic ignored "-Wstringop-overread"
 
4198
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4200
 
#if defined(__GNUC__) and __GNUC__ >= 11
 
4201
 
#pragma GCC diagnostic pop
 
4203
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4205
 
  bool quit_now = false;
 
4206
 
  buffer password = {};
 
4207
 
  bool mandos_client_exited = false;
 
4208
 
  bool password_is_read = false;
 
4209
 
  __attribute__((cleanup(cleanup_queue)))
 
4210
 
    task_queue *queue = create_queue();
 
4211
 
  g_assert_nonnull(queue);
 
4213
 
  task_context task = {
 
4214
 
    .func=read_inotify_event,
 
4217
 
    .quit_now=&quit_now,
 
4218
 
    .password=&password,
 
4219
 
    .filename=strdup("/nonexistent"),
 
4220
 
    .cancelled_filenames = &(string_set){},
 
4222
 
    .current_time=¤t_time,
 
4223
 
    .mandos_client_exited=&mandos_client_exited,
 
4224
 
    .password_is_read=&password_is_read,
 
4226
 
  task.func(task, queue);
 
4227
 
  g_assert_false(quit_now);
 
4228
 
  g_assert_true(queue->next_run != 0);
 
4229
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 1);
 
4231
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4232
 
        .func=read_inotify_event,
 
4235
 
        .quit_now=&quit_now,
 
4236
 
        .password=&password,
 
4237
 
        .filename=task.filename,
 
4238
 
        .cancelled_filenames=task.cancelled_filenames,
 
4239
 
        .current_time=¤t_time,
 
4240
 
        .mandos_client_exited=&mandos_client_exited,
 
4241
 
        .password_is_read=&password_is_read,
 
4244
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4245
 
                                   EPOLLIN | EPOLLRDHUP));
 
4247
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
4249
 
  __attribute__((cleanup(cleanup_string)))
 
4250
 
    char *filename = NULL;
 
4251
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
4252
 
                           dummy_file_name), >, 0);
 
4253
 
  g_assert_nonnull(filename);
 
4254
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4255
 
        .func=open_and_parse_question,
 
4258
 
        .question_filename=filename,
 
4259
 
        .password=&password,
 
4260
 
        .cancelled_filenames=task.cancelled_filenames,
 
4261
 
        .current_time=¤t_time,
 
4262
 
        .mandos_client_exited=&mandos_client_exited,
 
4263
 
        .password_is_read=&password_is_read,
 
4268
 
void test_read_inotify_event_IN_MOVED_TO(__attribute__((unused))
 
4269
 
                                         test_fixture *fixture,
 
4270
 
                                         __attribute__((unused))
 
4271
 
                                         gconstpointer user_data){
 
4272
 
  __attribute__((cleanup(cleanup_close)))
 
4273
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4274
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4275
 
  const mono_microsecs current_time = 0;
 
4278
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4280
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4281
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4283
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4285
 
    struct inotify_event event;
 
4286
 
    char name_buffer[NAME_MAX + 1];
 
4288
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4290
 
  const char dummy_file_name[] = "ask.dummy_file_name";
 
4291
 
  ievent->mask = IN_MOVED_TO;
 
4292
 
  ievent->len = sizeof(dummy_file_name);
 
4293
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4294
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4295
 
                              + sizeof(dummy_file_name));
 
4296
 
#if defined(__GNUC__) and __GNUC__ >= 11
 
4297
 
#pragma GCC diagnostic push
 
4298
 
  /* ievent is pointing into a struct which is of sufficient size */
 
4299
 
#pragma GCC diagnostic ignored "-Wstringop-overread"
 
4301
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4303
 
#if defined(__GNUC__) and __GNUC__ >= 11
 
4304
 
#pragma GCC diagnostic pop
 
4306
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4308
 
  bool quit_now = false;
 
4309
 
  buffer password = {};
 
4310
 
  bool mandos_client_exited = false;
 
4311
 
  bool password_is_read = false;
 
4312
 
  __attribute__((cleanup(cleanup_queue)))
 
4313
 
    task_queue *queue = create_queue();
 
4314
 
  g_assert_nonnull(queue);
 
4316
 
  task_context task = {
 
4317
 
    .func=read_inotify_event,
 
4320
 
    .quit_now=&quit_now,
 
4321
 
    .password=&password,
 
4322
 
    .filename=strdup("/nonexistent"),
 
4323
 
    .cancelled_filenames = &(string_set){},
 
4325
 
    .current_time=¤t_time,
 
4326
 
    .mandos_client_exited=&mandos_client_exited,
 
4327
 
    .password_is_read=&password_is_read,
 
4329
 
  task.func(task, queue);
 
4330
 
  g_assert_false(quit_now);
 
4331
 
  g_assert_true(queue->next_run != 0);
 
4332
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 1);
 
4334
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4335
 
        .func=read_inotify_event,
 
4338
 
        .quit_now=&quit_now,
 
4339
 
        .password=&password,
 
4340
 
        .filename=task.filename,
 
4341
 
        .cancelled_filenames=task.cancelled_filenames,
 
4342
 
        .current_time=¤t_time,
 
4343
 
        .mandos_client_exited=&mandos_client_exited,
 
4344
 
        .password_is_read=&password_is_read,
 
4347
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4348
 
                                   EPOLLIN | EPOLLRDHUP));
 
4350
 
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
4352
 
  __attribute__((cleanup(cleanup_string)))
 
4353
 
    char *filename = NULL;
 
4354
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
4355
 
                           dummy_file_name), >, 0);
 
4356
 
  g_assert_nonnull(filename);
 
4357
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4358
 
        .func=open_and_parse_question,
 
4361
 
        .question_filename=filename,
 
4362
 
        .password=&password,
 
4363
 
        .cancelled_filenames=task.cancelled_filenames,
 
4364
 
        .current_time=¤t_time,
 
4365
 
        .mandos_client_exited=&mandos_client_exited,
 
4366
 
        .password_is_read=&password_is_read,
 
4371
 
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
 
4372
 
                                           test_fixture *fixture,
 
4373
 
                                           __attribute__((unused))
 
4374
 
                                           gconstpointer user_data){
 
4375
 
  __attribute__((cleanup(cleanup_close)))
 
4376
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4377
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4378
 
  __attribute__((cleanup(string_set_clear)))
 
4379
 
    string_set cancelled_filenames = {};
 
4380
 
  const mono_microsecs current_time = 0;
 
4383
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4385
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4386
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4388
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4390
 
    struct inotify_event event;
 
4391
 
    char name_buffer[NAME_MAX + 1];
 
4393
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4395
 
  const char dummy_file_name[] = "ask.dummy_file_name";
 
4396
 
  ievent->mask = IN_MOVED_FROM;
 
4397
 
  ievent->len = sizeof(dummy_file_name);
 
4398
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4399
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4400
 
                              + sizeof(dummy_file_name));
 
4401
 
#if defined(__GNUC__) and __GNUC__ >= 11
 
4402
 
#pragma GCC diagnostic push
 
4403
 
  /* ievent is pointing into a struct which is of sufficient size */
 
4404
 
#pragma GCC diagnostic ignored "-Wstringop-overread"
 
4406
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4408
 
#if defined(__GNUC__) and __GNUC__ >= 11
 
4409
 
#pragma GCC diagnostic pop
 
4411
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4413
 
  bool quit_now = false;
 
4414
 
  buffer password = {};
 
4415
 
  bool mandos_client_exited = false;
 
4416
 
  bool password_is_read = false;
 
4417
 
  __attribute__((cleanup(cleanup_queue)))
 
4418
 
    task_queue *queue = create_queue();
 
4419
 
  g_assert_nonnull(queue);
 
4421
 
  task_context task = {
 
4422
 
    .func=read_inotify_event,
 
4425
 
    .quit_now=&quit_now,
 
4426
 
    .password=&password,
 
4427
 
    .filename=strdup("/nonexistent"),
 
4428
 
    .cancelled_filenames=&cancelled_filenames,
 
4429
 
    .current_time=¤t_time,
 
4430
 
    .mandos_client_exited=&mandos_client_exited,
 
4431
 
    .password_is_read=&password_is_read,
 
4433
 
  task.func(task, queue);
 
4434
 
  g_assert_false(quit_now);
 
4435
 
  g_assert_true(queue->next_run == 0);
 
4436
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4438
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4439
 
        .func=read_inotify_event,
 
4442
 
        .quit_now=&quit_now,
 
4443
 
        .password=&password,
 
4444
 
        .filename=task.filename,
 
4445
 
        .cancelled_filenames=&cancelled_filenames,
 
4446
 
        .current_time=¤t_time,
 
4447
 
        .mandos_client_exited=&mandos_client_exited,
 
4448
 
        .password_is_read=&password_is_read,
 
4451
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4452
 
                                   EPOLLIN | EPOLLRDHUP));
 
4454
 
  __attribute__((cleanup(cleanup_string)))
 
4455
 
    char *filename = NULL;
 
4456
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
4457
 
                           dummy_file_name), >, 0);
 
4458
 
  g_assert_nonnull(filename);
 
4459
 
  g_assert_true(string_set_contains(*task.cancelled_filenames,
 
4463
 
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
 
4464
 
                                              test_fixture *fixture,
 
4465
 
                                              __attribute__((unused))
 
4468
 
  __attribute__((cleanup(cleanup_close)))
 
4469
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4470
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4471
 
  __attribute__((cleanup(string_set_clear)))
 
4472
 
    string_set cancelled_filenames = {};
 
4473
 
  const mono_microsecs current_time = 0;
 
4476
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4478
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4479
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4481
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4483
 
    struct inotify_event event;
 
4484
 
    char name_buffer[NAME_MAX + 1];
 
4486
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4488
 
  const char dummy_file_name[] = "ask.dummy_file_name";
 
4489
 
  ievent->mask = IN_DELETE;
 
4490
 
  ievent->len = sizeof(dummy_file_name);
 
4491
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4492
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4493
 
                              + sizeof(dummy_file_name));
 
4494
 
#if defined(__GNUC__) and __GNUC__ >= 11
 
4495
 
#pragma GCC diagnostic push
 
4496
 
  /* ievent is pointing into a struct which is of sufficient size */
 
4497
 
#pragma GCC diagnostic ignored "-Wstringop-overread"
 
4499
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4501
 
#if defined(__GNUC__) and __GNUC__ >= 11
 
4502
 
#pragma GCC diagnostic pop
 
4504
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4506
 
  bool quit_now = false;
 
4507
 
  buffer password = {};
 
4508
 
  bool mandos_client_exited = false;
 
4509
 
  bool password_is_read = false;
 
4510
 
  __attribute__((cleanup(cleanup_queue)))
 
4511
 
    task_queue *queue = create_queue();
 
4512
 
  g_assert_nonnull(queue);
 
4514
 
  task_context task = {
 
4515
 
    .func=read_inotify_event,
 
4518
 
    .quit_now=&quit_now,
 
4519
 
    .password=&password,
 
4520
 
    .filename=strdup("/nonexistent"),
 
4521
 
    .cancelled_filenames=&cancelled_filenames,
 
4522
 
    .current_time=¤t_time,
 
4523
 
    .mandos_client_exited=&mandos_client_exited,
 
4524
 
    .password_is_read=&password_is_read,
 
4526
 
  task.func(task, queue);
 
4527
 
  g_assert_false(quit_now);
 
4528
 
  g_assert_true(queue->next_run == 0);
 
4529
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4531
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4532
 
        .func=read_inotify_event,
 
4535
 
        .quit_now=&quit_now,
 
4536
 
        .password=&password,
 
4537
 
        .filename=task.filename,
 
4538
 
        .cancelled_filenames=&cancelled_filenames,
 
4539
 
        .current_time=¤t_time,
 
4540
 
        .mandos_client_exited=&mandos_client_exited,
 
4541
 
        .password_is_read=&password_is_read,
 
4544
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4545
 
                                   EPOLLIN | EPOLLRDHUP));
 
4547
 
  __attribute__((cleanup(cleanup_string)))
 
4548
 
    char *filename = NULL;
 
4549
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
4550
 
                           dummy_file_name), >, 0);
 
4551
 
  g_assert_nonnull(filename);
 
4552
 
  g_assert_true(string_set_contains(*task.cancelled_filenames,
 
4557
 
test_read_inotify_event_IN_CLOSE_WRITE_badname(__attribute__((unused))
 
4558
 
                                               test_fixture *fixture,
 
4559
 
                                               __attribute__((unused))
 
4562
 
  __attribute__((cleanup(cleanup_close)))
 
4563
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4564
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4565
 
  const mono_microsecs current_time = 0;
 
4568
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4570
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4571
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4573
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4575
 
    struct inotify_event event;
 
4576
 
    char name_buffer[NAME_MAX + 1];
 
4578
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4580
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
4581
 
  ievent->mask = IN_CLOSE_WRITE;
 
4582
 
  ievent->len = sizeof(dummy_file_name);
 
4583
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4584
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4585
 
                              + sizeof(dummy_file_name));
 
4586
 
#if defined(__GNUC__) and __GNUC__ >= 11
 
4587
 
#pragma GCC diagnostic push
 
4588
 
  /* ievent is pointing into a struct which is of sufficient size */
 
4589
 
#pragma GCC diagnostic ignored "-Wstringop-overread"
 
4591
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4593
 
#if defined(__GNUC__) and __GNUC__ >= 11
 
4594
 
#pragma GCC diagnostic pop
 
4596
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4598
 
  bool quit_now = false;
 
4599
 
  buffer password = {};
 
4600
 
  bool mandos_client_exited = false;
 
4601
 
  bool password_is_read = false;
 
4602
 
  __attribute__((cleanup(cleanup_queue)))
 
4603
 
    task_queue *queue = create_queue();
 
4604
 
  g_assert_nonnull(queue);
 
4606
 
  task_context task = {
 
4607
 
    .func=read_inotify_event,
 
4610
 
    .quit_now=&quit_now,
 
4611
 
    .password=&password,
 
4612
 
    .filename=strdup("/nonexistent"),
 
4613
 
    .cancelled_filenames = &(string_set){},
 
4615
 
    .current_time=¤t_time,
 
4616
 
    .mandos_client_exited=&mandos_client_exited,
 
4617
 
    .password_is_read=&password_is_read,
 
4619
 
  task.func(task, queue);
 
4620
 
  g_assert_false(quit_now);
 
4621
 
  g_assert_true(queue->next_run == 0);
 
4622
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4624
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4625
 
        .func=read_inotify_event,
 
4628
 
        .quit_now=&quit_now,
 
4629
 
        .password=&password,
 
4630
 
        .filename=task.filename,
 
4631
 
        .cancelled_filenames=task.cancelled_filenames,
 
4632
 
        .current_time=¤t_time,
 
4633
 
        .mandos_client_exited=&mandos_client_exited,
 
4634
 
        .password_is_read=&password_is_read,
 
4637
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4638
 
                                   EPOLLIN | EPOLLRDHUP));
 
4642
 
test_read_inotify_event_IN_MOVED_TO_badname(__attribute__((unused))
 
4643
 
                                            test_fixture *fixture,
 
4644
 
                                            __attribute__((unused))
 
4645
 
                                            gconstpointer user_data){
 
4646
 
  __attribute__((cleanup(cleanup_close)))
 
4647
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4648
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4649
 
  const mono_microsecs current_time = 0;
 
4652
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4654
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4655
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4657
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4659
 
    struct inotify_event event;
 
4660
 
    char name_buffer[NAME_MAX + 1];
 
4662
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4664
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
4665
 
  ievent->mask = IN_MOVED_TO;
 
4666
 
  ievent->len = sizeof(dummy_file_name);
 
4667
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4668
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4669
 
                              + sizeof(dummy_file_name));
 
4670
 
#if defined(__GNUC__) and __GNUC__ >= 11
 
4671
 
#pragma GCC diagnostic push
 
4672
 
  /* ievent is pointing into a struct which is of sufficient size */
 
4673
 
#pragma GCC diagnostic ignored "-Wstringop-overread"
 
4675
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4677
 
#if defined(__GNUC__) and __GNUC__ >= 11
 
4678
 
#pragma GCC diagnostic pop
 
4680
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4682
 
  bool quit_now = false;
 
4683
 
  buffer password = {};
 
4684
 
  bool mandos_client_exited = false;
 
4685
 
  bool password_is_read = false;
 
4686
 
  __attribute__((cleanup(cleanup_queue)))
 
4687
 
    task_queue *queue = create_queue();
 
4688
 
  g_assert_nonnull(queue);
 
4690
 
  task_context task = {
 
4691
 
    .func=read_inotify_event,
 
4694
 
    .quit_now=&quit_now,
 
4695
 
    .password=&password,
 
4696
 
    .filename=strdup("/nonexistent"),
 
4697
 
    .cancelled_filenames = &(string_set){},
 
4699
 
    .current_time=¤t_time,
 
4700
 
    .mandos_client_exited=&mandos_client_exited,
 
4701
 
    .password_is_read=&password_is_read,
 
4703
 
  task.func(task, queue);
 
4704
 
  g_assert_false(quit_now);
 
4705
 
  g_assert_true(queue->next_run == 0);
 
4706
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4708
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4709
 
        .func=read_inotify_event,
 
4712
 
        .quit_now=&quit_now,
 
4713
 
        .password=&password,
 
4714
 
        .filename=task.filename,
 
4715
 
        .cancelled_filenames=task.cancelled_filenames,
 
4716
 
        .current_time=¤t_time,
 
4717
 
        .mandos_client_exited=&mandos_client_exited,
 
4718
 
        .password_is_read=&password_is_read,
 
4721
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4722
 
                                   EPOLLIN | EPOLLRDHUP));
 
4726
 
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
 
4727
 
                                              test_fixture *fixture,
 
4728
 
                                              __attribute__((unused))
 
4731
 
  __attribute__((cleanup(cleanup_close)))
 
4732
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4733
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4734
 
  __attribute__((cleanup(string_set_clear)))
 
4735
 
    string_set cancelled_filenames = {};
 
4736
 
  const mono_microsecs current_time = 0;
 
4739
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4741
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4742
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4744
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4746
 
    struct inotify_event event;
 
4747
 
    char name_buffer[NAME_MAX + 1];
 
4749
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4751
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
4752
 
  ievent->mask = IN_MOVED_FROM;
 
4753
 
  ievent->len = sizeof(dummy_file_name);
 
4754
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4755
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4756
 
                              + sizeof(dummy_file_name));
 
4757
 
#if defined(__GNUC__) and __GNUC__ >= 11
 
4758
 
#pragma GCC diagnostic push
 
4759
 
  /* ievent is pointing into a struct which is of sufficient size */
 
4760
 
#pragma GCC diagnostic ignored "-Wstringop-overread"
 
4762
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4764
 
#if defined(__GNUC__) and __GNUC__ >= 11
 
4765
 
#pragma GCC diagnostic pop
 
4767
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4769
 
  bool quit_now = false;
 
4770
 
  buffer password = {};
 
4771
 
  bool mandos_client_exited = false;
 
4772
 
  bool password_is_read = false;
 
4773
 
  __attribute__((cleanup(cleanup_queue)))
 
4774
 
    task_queue *queue = create_queue();
 
4775
 
  g_assert_nonnull(queue);
 
4777
 
  task_context task = {
 
4778
 
    .func=read_inotify_event,
 
4781
 
    .quit_now=&quit_now,
 
4782
 
    .password=&password,
 
4783
 
    .filename=strdup("/nonexistent"),
 
4784
 
    .cancelled_filenames=&cancelled_filenames,
 
4785
 
    .current_time=¤t_time,
 
4786
 
    .mandos_client_exited=&mandos_client_exited,
 
4787
 
    .password_is_read=&password_is_read,
 
4789
 
  task.func(task, queue);
 
4790
 
  g_assert_false(quit_now);
 
4791
 
  g_assert_true(queue->next_run == 0);
 
4792
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4794
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4795
 
        .func=read_inotify_event,
 
4798
 
        .quit_now=&quit_now,
 
4799
 
        .password=&password,
 
4800
 
        .filename=task.filename,
 
4801
 
        .cancelled_filenames=&cancelled_filenames,
 
4802
 
        .current_time=¤t_time,
 
4803
 
        .mandos_client_exited=&mandos_client_exited,
 
4804
 
        .password_is_read=&password_is_read,
 
4807
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4808
 
                                   EPOLLIN | EPOLLRDHUP));
 
4810
 
  __attribute__((cleanup(cleanup_string)))
 
4811
 
    char *filename = NULL;
 
4812
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
4813
 
                           dummy_file_name), >, 0);
 
4814
 
  g_assert_nonnull(filename);
 
4815
 
  g_assert_false(string_set_contains(cancelled_filenames, filename));
 
4819
 
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
 
4820
 
                                               test_fixture *fixture,
 
4821
 
                                               __attribute__((unused))
 
4824
 
  __attribute__((cleanup(cleanup_close)))
 
4825
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4826
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4827
 
  __attribute__((cleanup(string_set_clear)))
 
4828
 
    string_set cancelled_filenames = {};
 
4829
 
  const mono_microsecs current_time = 0;
 
4832
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4834
 
  /* "sufficient to read at least one event." - inotify(7) */
 
4835
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4837
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4839
 
    struct inotify_event event;
 
4840
 
    char name_buffer[NAME_MAX + 1];
 
4842
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4844
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
4845
 
  ievent->mask = IN_DELETE;
 
4846
 
  ievent->len = sizeof(dummy_file_name);
 
4847
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4848
 
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4849
 
                              + sizeof(dummy_file_name));
 
4850
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4852
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4854
 
  bool quit_now = false;
 
4855
 
  buffer password = {};
 
4856
 
  bool mandos_client_exited = false;
 
4857
 
  bool password_is_read = false;
 
4858
 
  __attribute__((cleanup(cleanup_queue)))
 
4859
 
    task_queue *queue = create_queue();
 
4860
 
  g_assert_nonnull(queue);
 
4862
 
  task_context task = {
 
4863
 
    .func=read_inotify_event,
 
4866
 
    .quit_now=&quit_now,
 
4867
 
    .password=&password,
 
4868
 
    .filename=strdup("/nonexistent"),
 
4869
 
    .cancelled_filenames=&cancelled_filenames,
 
4870
 
    .current_time=¤t_time,
 
4871
 
    .mandos_client_exited=&mandos_client_exited,
 
4872
 
    .password_is_read=&password_is_read,
 
4874
 
  task.func(task, queue);
 
4875
 
  g_assert_false(quit_now);
 
4876
 
  g_assert_true(queue->next_run == 0);
 
4877
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4879
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4880
 
        .func=read_inotify_event,
 
4883
 
        .quit_now=&quit_now,
 
4884
 
        .password=&password,
 
4885
 
        .filename=task.filename,
 
4886
 
        .cancelled_filenames=&cancelled_filenames,
 
4887
 
        .current_time=¤t_time,
 
4888
 
        .mandos_client_exited=&mandos_client_exited,
 
4889
 
        .password_is_read=&password_is_read,
 
4892
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4893
 
                                   EPOLLIN | EPOLLRDHUP));
 
4895
 
  __attribute__((cleanup(cleanup_string)))
 
4896
 
    char *filename = NULL;
 
4897
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
4898
 
                           dummy_file_name), >, 0);
 
4899
 
  g_assert_nonnull(filename);
 
4900
 
  g_assert_false(string_set_contains(cancelled_filenames, filename));
 
4904
 
void test_open_and_parse_question_ENOENT(__attribute__((unused))
 
4905
 
                                         test_fixture *fixture,
 
4906
 
                                         __attribute__((unused))
 
4907
 
                                         gconstpointer user_data){
 
4908
 
  __attribute__((cleanup(cleanup_close)))
 
4909
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4910
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4911
 
  __attribute__((cleanup(string_set_clear)))
 
4912
 
    string_set cancelled_filenames = {};
 
4913
 
  bool mandos_client_exited = false;
 
4914
 
  bool password_is_read = false;
 
4915
 
  __attribute__((cleanup(cleanup_queue)))
 
4916
 
    task_queue *queue = create_queue();
 
4917
 
  g_assert_nonnull(queue);
 
4919
 
  char *const filename = strdup("/nonexistent");
 
4920
 
  g_assert_nonnull(filename);
 
4921
 
  task_context task = {
 
4922
 
    .func=open_and_parse_question,
 
4923
 
    .question_filename=filename,
 
4925
 
    .password=(buffer[]){{}},
 
4927
 
    .cancelled_filenames=&cancelled_filenames,
 
4928
 
    .current_time=(mono_microsecs[]){0},
 
4929
 
    .mandos_client_exited=&mandos_client_exited,
 
4930
 
    .password_is_read=&password_is_read,
 
4932
 
  task.func(task, queue);
 
4933
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4936
 
static void test_open_and_parse_question_EIO(__attribute__((unused))
 
4937
 
                                             test_fixture *fixture,
 
4938
 
                                             __attribute__((unused))
 
4939
 
                                             gconstpointer user_data){
 
4940
 
  __attribute__((cleanup(cleanup_close)))
 
4941
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4942
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4943
 
  __attribute__((cleanup(string_set_clear)))
 
4944
 
    string_set cancelled_filenames = {};
 
4945
 
  buffer password = {};
 
4946
 
  bool mandos_client_exited = false;
 
4947
 
  bool password_is_read = false;
 
4948
 
  __attribute__((cleanup(cleanup_queue)))
 
4949
 
    task_queue *queue = create_queue();
 
4950
 
  g_assert_nonnull(queue);
 
4951
 
  const mono_microsecs current_time = 0;
 
4953
 
  char *filename = strdup("/proc/self/mem");
 
4954
 
  task_context task = {
 
4955
 
    .func=open_and_parse_question,
 
4956
 
    .question_filename=filename,
 
4958
 
    .password=&password,
 
4960
 
    .cancelled_filenames=&cancelled_filenames,
 
4961
 
    .current_time=¤t_time,
 
4962
 
    .mandos_client_exited=&mandos_client_exited,
 
4963
 
    .password_is_read=&password_is_read,
 
4965
 
  run_task_with_stderr_to_dev_null(task, queue);
 
4966
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
4970
 
test_open_and_parse_question_parse_error(__attribute__((unused))
 
4971
 
                                         test_fixture *fixture,
 
4972
 
                                         __attribute__((unused))
 
4973
 
                                         gconstpointer user_data){
 
4974
 
  __attribute__((cleanup(cleanup_close)))
 
4975
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4976
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
4977
 
  __attribute__((cleanup(string_set_clear)))
 
4978
 
    string_set cancelled_filenames = {};
 
4979
 
  __attribute__((cleanup(cleanup_queue)))
 
4980
 
    task_queue *queue = create_queue();
 
4981
 
  g_assert_nonnull(queue);
 
4983
 
  __attribute__((cleanup(cleanup_string)))
 
4984
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
4985
 
  g_assert_nonnull(tempfilename);
 
4986
 
  int tempfile = mkostemp(tempfilename, O_CLOEXEC);
 
4987
 
  g_assert_cmpint(tempfile, >, 0);
 
4988
 
  const char bad_data[] = "this is bad syntax\n";
 
4989
 
  g_assert_cmpint(write(tempfile, bad_data, sizeof(bad_data)),
 
4990
 
                  ==, sizeof(bad_data));
 
4991
 
  g_assert_cmpint(close(tempfile), ==, 0);
 
4993
 
  char *const filename = strdup(tempfilename);
 
4994
 
  g_assert_nonnull(filename);
 
4995
 
  task_context task = {
 
4996
 
    .func=open_and_parse_question,
 
4997
 
    .question_filename=filename,
 
4999
 
    .password=(buffer[]){{}},
 
5001
 
    .cancelled_filenames=&cancelled_filenames,
 
5002
 
    .current_time=(mono_microsecs[]){0},
 
5003
 
    .mandos_client_exited=(bool[]){false},
 
5004
 
    .password_is_read=(bool[]){false},
 
5006
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5008
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5010
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
5014
 
void test_open_and_parse_question_nosocket(__attribute__((unused))
 
5015
 
                                           test_fixture *fixture,
 
5016
 
                                           __attribute__((unused))
 
5017
 
                                           gconstpointer user_data){
 
5018
 
  __attribute__((cleanup(cleanup_close)))
 
5019
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5020
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5021
 
  __attribute__((cleanup(string_set_clear)))
 
5022
 
    string_set cancelled_filenames = {};
 
5023
 
  __attribute__((cleanup(cleanup_queue)))
 
5024
 
    task_queue *queue = create_queue();
 
5025
 
  g_assert_nonnull(queue);
 
5027
 
  __attribute__((cleanup(cleanup_string)))
 
5028
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
5029
 
  g_assert_nonnull(tempfilename);
 
5030
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
5031
 
  g_assert_cmpint(questionfile, >, 0);
 
5032
 
  FILE *qf = fdopen(questionfile, "w");
 
5033
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nPID=1\n"), >, 0);
 
5034
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
5036
 
  char *const filename = strdup(tempfilename);
 
5037
 
  g_assert_nonnull(filename);
 
5038
 
  task_context task = {
 
5039
 
    .func=open_and_parse_question,
 
5040
 
    .question_filename=filename,
 
5042
 
    .password=(buffer[]){{}},
 
5044
 
    .cancelled_filenames=&cancelled_filenames,
 
5045
 
    .current_time=(mono_microsecs[]){0},
 
5046
 
    .mandos_client_exited=(bool[]){false},
 
5047
 
    .password_is_read=(bool[]){false},
 
5049
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5050
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5052
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
5056
 
void test_open_and_parse_question_badsocket(__attribute__((unused))
 
5057
 
                                            test_fixture *fixture,
 
5058
 
                                            __attribute__((unused))
 
5059
 
                                            gconstpointer user_data){
 
5060
 
  __attribute__((cleanup(cleanup_close)))
 
5061
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5062
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5063
 
  __attribute__((cleanup(string_set_clear)))
 
5064
 
    string_set cancelled_filenames = {};
 
5065
 
  __attribute__((cleanup(cleanup_queue)))
 
5066
 
    task_queue *queue = create_queue();
 
5067
 
  g_assert_nonnull(queue);
 
5069
 
  __attribute__((cleanup(cleanup_string)))
 
5070
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
5071
 
  g_assert_nonnull(tempfilename);
 
5072
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
5073
 
  g_assert_cmpint(questionfile, >, 0);
 
5074
 
  FILE *qf = fdopen(questionfile, "w");
 
5075
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=\nPID=1\n"), >, 0);
 
5076
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
5078
 
  char *const filename = strdup(tempfilename);
 
5079
 
  g_assert_nonnull(filename);
 
5080
 
  task_context task = {
 
5081
 
    .func=open_and_parse_question,
 
5082
 
    .question_filename=filename,
 
5084
 
    .password=(buffer[]){{}},
 
5086
 
    .cancelled_filenames=&cancelled_filenames,
 
5087
 
    .current_time=(mono_microsecs[]){0},
 
5088
 
    .mandos_client_exited=(bool[]){false},
 
5089
 
    .password_is_read=(bool[]){false},
 
5091
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5092
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5094
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
5098
 
void test_open_and_parse_question_nopid(__attribute__((unused))
 
5099
 
                                        test_fixture *fixture,
 
5100
 
                                        __attribute__((unused))
 
5101
 
                                        gconstpointer user_data){
 
5102
 
  __attribute__((cleanup(cleanup_close)))
 
5103
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5104
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5105
 
  __attribute__((cleanup(string_set_clear)))
 
5106
 
    string_set cancelled_filenames = {};
 
5107
 
  __attribute__((cleanup(cleanup_queue)))
 
5108
 
    task_queue *queue = create_queue();
 
5109
 
  g_assert_nonnull(queue);
 
5111
 
  __attribute__((cleanup(cleanup_string)))
 
5112
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
5113
 
  g_assert_nonnull(tempfilename);
 
5114
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
5115
 
  g_assert_cmpint(questionfile, >, 0);
 
5116
 
  FILE *qf = fdopen(questionfile, "w");
 
5117
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\n"), >, 0);
 
5118
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
5120
 
  char *const filename = strdup(tempfilename);
 
5121
 
  g_assert_nonnull(filename);
 
5122
 
  task_context task = {
 
5123
 
    .func=open_and_parse_question,
 
5124
 
    .question_filename=filename,
 
5126
 
    .password=(buffer[]){{}},
 
5128
 
    .cancelled_filenames=&cancelled_filenames,
 
5129
 
    .current_time=(mono_microsecs[]){0},
 
5130
 
    .mandos_client_exited=(bool[]){false},
 
5131
 
    .password_is_read=(bool[]){false},
 
5133
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5134
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5136
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
5140
 
void test_open_and_parse_question_badpid(__attribute__((unused))
 
5141
 
                                         test_fixture *fixture,
 
5142
 
                                         __attribute__((unused))
 
5143
 
                                         gconstpointer user_data){
 
5144
 
  __attribute__((cleanup(cleanup_close)))
 
5145
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5146
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5147
 
  __attribute__((cleanup(string_set_clear)))
 
5148
 
    string_set cancelled_filenames = {};
 
5149
 
  __attribute__((cleanup(cleanup_queue)))
 
5150
 
    task_queue *queue = create_queue();
 
5151
 
  g_assert_nonnull(queue);
 
5153
 
  __attribute__((cleanup(cleanup_string)))
 
5154
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
5155
 
  g_assert_nonnull(tempfilename);
 
5156
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
5157
 
  g_assert_cmpint(questionfile, >, 0);
 
5158
 
  FILE *qf = fdopen(questionfile, "w");
 
5159
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=\n"),
 
5161
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
5163
 
  char *const filename = strdup(tempfilename);
 
5164
 
  g_assert_nonnull(filename);
 
5165
 
  task_context task = {
 
5166
 
    .func=open_and_parse_question,
 
5167
 
    .question_filename=filename,
 
5169
 
    .password=(buffer[]){{}},
 
5171
 
    .cancelled_filenames=&cancelled_filenames,
 
5172
 
    .current_time=(mono_microsecs[]){0},
 
5173
 
    .mandos_client_exited=(bool[]){false},
 
5174
 
    .password_is_read=(bool[]){false},
 
5176
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5177
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5179
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
5183
 
test_open_and_parse_question_noexist_pid(__attribute__((unused))
 
5184
 
                                         test_fixture *fixture,
 
5185
 
                                         __attribute__((unused))
 
5186
 
                                         gconstpointer user_data){
 
5187
 
  __attribute__((cleanup(cleanup_close)))
 
5188
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5189
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5190
 
  __attribute__((cleanup(string_set_clear)))
 
5191
 
    string_set cancelled_filenames = {};
 
5192
 
  buffer password = {};
 
5193
 
  bool mandos_client_exited = false;
 
5194
 
  bool password_is_read = false;
 
5195
 
  __attribute__((cleanup(cleanup_queue)))
 
5196
 
    task_queue *queue = create_queue();
 
5197
 
  g_assert_nonnull(queue);
 
5198
 
  const mono_microsecs current_time = 0;
 
5200
 
  /* Find value of sysctl kernel.pid_max */
 
5201
 
  uintmax_t pid_max = 0;
 
5202
 
  FILE *sysctl_pid_max = fopen("/proc/sys/kernel/pid_max", "r");
 
5203
 
  g_assert_nonnull(sysctl_pid_max);
 
5204
 
  g_assert_cmpint(fscanf(sysctl_pid_max, "%" PRIuMAX, &pid_max),
 
5206
 
  g_assert_cmpint(fclose(sysctl_pid_max), ==, 0);
 
5208
 
  pid_t nonexisting_pid = ((pid_t)pid_max)+1;
 
5209
 
  g_assert_true(nonexisting_pid > 0); /* Overflow check */
 
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"\n", (uintmax_t)nonexisting_pid),
 
5220
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
5222
 
  char *const question_filename = strdup(tempfilename);
 
5223
 
  g_assert_nonnull(question_filename);
 
5224
 
  task_context task = {
 
5225
 
    .func=open_and_parse_question,
 
5226
 
    .question_filename=question_filename,
 
5228
 
    .password=&password,
 
5229
 
    .filename=question_filename,
 
5230
 
    .cancelled_filenames=&cancelled_filenames,
 
5231
 
    .current_time=¤t_time,
 
5232
 
    .mandos_client_exited=&mandos_client_exited,
 
5233
 
    .password_is_read=&password_is_read,
 
5235
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5236
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5238
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
5242
 
test_open_and_parse_question_no_notafter(__attribute__((unused))
 
5243
 
                                         test_fixture *fixture,
 
5244
 
                                         __attribute__((unused))
 
5245
 
                                         gconstpointer user_data){
 
5246
 
  __attribute__((cleanup(cleanup_close)))
 
5247
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5248
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5249
 
  __attribute__((cleanup(string_set_clear)))
 
5250
 
    string_set cancelled_filenames = {};
 
5251
 
  buffer password = {};
 
5252
 
  bool mandos_client_exited = false;
 
5253
 
  bool password_is_read = false;
 
5254
 
  __attribute__((cleanup(cleanup_queue)))
 
5255
 
    task_queue *queue = create_queue();
 
5256
 
  g_assert_nonnull(queue);
 
5257
 
  const mono_microsecs current_time = 0;
 
5259
 
  __attribute__((cleanup(cleanup_string)))
 
5260
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
5261
 
  g_assert_nonnull(tempfilename);
 
5262
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
5263
 
  g_assert_cmpint(questionfile, >, 0);
 
5264
 
  FILE *qf = fdopen(questionfile, "w");
 
5265
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
5266
 
                          PRIuMAX "\n", (uintmax_t)getpid()), >, 0);
 
5267
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
5269
 
  char *const filename = strdup(tempfilename);
 
5270
 
  g_assert_nonnull(filename);
 
5271
 
  task_context task = {
 
5272
 
    .func=open_and_parse_question,
 
5273
 
    .question_filename=filename,
 
5275
 
    .password=&password,
 
5277
 
    .cancelled_filenames=&cancelled_filenames,
 
5278
 
    .current_time=¤t_time,
 
5279
 
    .mandos_client_exited=&mandos_client_exited,
 
5280
 
    .password_is_read=&password_is_read,
 
5282
 
  task.func(task, queue);
 
5283
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5285
 
  __attribute__((cleanup(cleanup_string)))
 
5286
 
    char *socket_filename = strdup("/nonexistent");
 
5287
 
  g_assert_nonnull(socket_filename);
 
5288
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
5289
 
        .func=connect_question_socket,
 
5290
 
        .question_filename=tempfilename,
 
5291
 
        .filename=socket_filename,
 
5293
 
        .password=&password,
 
5294
 
        .current_time=¤t_time,
 
5295
 
        .mandos_client_exited=&mandos_client_exited,
 
5296
 
        .password_is_read=&password_is_read,
 
5299
 
  g_assert_true(queue->next_run != 0);
 
5301
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
5305
 
test_open_and_parse_question_bad_notafter(__attribute__((unused))
 
5306
 
                                          test_fixture *fixture,
 
5307
 
                                          __attribute__((unused))
 
5308
 
                                          gconstpointer user_data){
 
5309
 
  __attribute__((cleanup(cleanup_close)))
 
5310
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5311
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5312
 
  __attribute__((cleanup(string_set_clear)))
 
5313
 
    string_set cancelled_filenames = {};
 
5314
 
  buffer password = {};
 
5315
 
  bool mandos_client_exited = false;
 
5316
 
  bool password_is_read = false;
 
5317
 
  __attribute__((cleanup(cleanup_queue)))
 
5318
 
    task_queue *queue = create_queue();
 
5319
 
  g_assert_nonnull(queue);
 
5320
 
  const mono_microsecs current_time = 0;
 
5322
 
  __attribute__((cleanup(cleanup_string)))
 
5323
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
5324
 
  g_assert_nonnull(tempfilename);
 
5325
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
5326
 
  g_assert_cmpint(questionfile, >, 0);
 
5327
 
  FILE *qf = fdopen(questionfile, "w");
 
5328
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
5329
 
                          PRIuMAX "\nNotAfter=\n",
 
5330
 
                          (uintmax_t)getpid()), >, 0);
 
5331
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
5333
 
  char *const filename = strdup(tempfilename);
 
5334
 
  g_assert_nonnull(filename);
 
5335
 
  task_context task = {
 
5336
 
    .func=open_and_parse_question,
 
5337
 
    .question_filename=filename,
 
5339
 
    .password=&password,
 
5341
 
    .cancelled_filenames=&cancelled_filenames,
 
5342
 
    .current_time=¤t_time,
 
5343
 
    .mandos_client_exited=&mandos_client_exited,
 
5344
 
    .password_is_read=&password_is_read,
 
5346
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5347
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5349
 
  __attribute__((cleanup(cleanup_string)))
 
5350
 
    char *socket_filename = strdup("/nonexistent");
 
5351
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
5352
 
        .func=connect_question_socket,
 
5353
 
        .question_filename=tempfilename,
 
5354
 
        .filename=socket_filename,
 
5356
 
        .password=&password,
 
5357
 
        .current_time=¤t_time,
 
5358
 
        .mandos_client_exited=&mandos_client_exited,
 
5359
 
        .password_is_read=&password_is_read,
 
5361
 
  g_assert_true(queue->next_run != 0);
 
5363
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
5367
 
void assert_open_and_parse_question_with_notafter(const mono_microsecs
 
5369
 
                                                  const mono_microsecs
 
5371
 
                                                  const mono_microsecs
 
5373
 
  __attribute__((cleanup(cleanup_close)))
 
5374
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5375
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5376
 
  __attribute__((cleanup(string_set_clear)))
 
5377
 
    string_set cancelled_filenames = {};
 
5378
 
  buffer password = {};
 
5379
 
  bool mandos_client_exited = false;
 
5380
 
  bool password_is_read = false;
 
5381
 
  __attribute__((cleanup(cleanup_queue)))
 
5382
 
    task_queue *queue = create_queue();
 
5383
 
  g_assert_nonnull(queue);
 
5384
 
  queue->next_run = next_queue_run;
 
5386
 
  __attribute__((cleanup(cleanup_string)))
 
5387
 
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
5388
 
  g_assert_nonnull(tempfilename);
 
5389
 
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
5390
 
  g_assert_cmpint(questionfile, >, 0);
 
5391
 
  FILE *qf = fdopen(questionfile, "w");
 
5392
 
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
5393
 
                          PRIuMAX "\nNotAfter=%" PRIuMAX "\n",
 
5394
 
                          (uintmax_t)getpid(), notafter), >, 0);
 
5395
 
  g_assert_cmpint(fclose(qf), ==, 0);
 
5397
 
  char *const filename = strdup(tempfilename);
 
5398
 
  g_assert_nonnull(filename);
 
5399
 
  task_context task = {
 
5400
 
    .func=open_and_parse_question,
 
5401
 
    .question_filename=filename,
 
5403
 
    .password=&password,
 
5405
 
    .cancelled_filenames=&cancelled_filenames,
 
5406
 
    .current_time=¤t_time,
 
5407
 
    .mandos_client_exited=&mandos_client_exited,
 
5408
 
    .password_is_read=&password_is_read,
 
5410
 
  task.func(task, queue);
 
5412
 
  if(queue->length >= 1){
 
5413
 
    __attribute__((cleanup(cleanup_string)))
 
5414
 
      char *socket_filename = strdup("/nonexistent");
 
5415
 
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
5416
 
          .func=connect_question_socket,
 
5417
 
          .filename=socket_filename,
 
5419
 
          .password=&password,
 
5420
 
          .current_time=¤t_time,
 
5421
 
          .cancelled_filenames=&cancelled_filenames,
 
5422
 
          .mandos_client_exited=&mandos_client_exited,
 
5423
 
          .password_is_read=&password_is_read,
 
5425
 
    g_assert_true(queue->next_run != 0);
 
5429
 
    g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5430
 
  } else if(current_time >= notafter) {
 
5431
 
    g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5433
 
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
5434
 
          .func=cancel_old_question,
 
5435
 
          .question_filename=tempfilename,
 
5436
 
          .filename=tempfilename,
 
5438
 
          .cancelled_filenames=&cancelled_filenames,
 
5439
 
          .current_time=¤t_time,
 
5442
 
  g_assert_true(queue->next_run == 1);
 
5444
 
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
5448
 
test_open_and_parse_question_notafter_0(__attribute__((unused))
 
5449
 
                                        test_fixture *fixture,
 
5450
 
                                        __attribute__((unused))
 
5451
 
                                        gconstpointer user_data){
 
5452
 
  /* current_time, notafter, next_queue_run */
 
5453
 
  assert_open_and_parse_question_with_notafter(0, 0, 0);
 
5457
 
test_open_and_parse_question_notafter_1(__attribute__((unused))
 
5458
 
                                        test_fixture *fixture,
 
5459
 
                                        __attribute__((unused))
 
5460
 
                                        gconstpointer user_data){
 
5461
 
  /* current_time, notafter, next_queue_run */
 
5462
 
  assert_open_and_parse_question_with_notafter(0, 1, 0);
 
5466
 
test_open_and_parse_question_notafter_1_1(__attribute__((unused))
 
5467
 
                                          test_fixture *fixture,
 
5468
 
                                          __attribute__((unused))
 
5469
 
                                          gconstpointer user_data){
 
5470
 
  /* current_time, notafter, next_queue_run */
 
5471
 
  assert_open_and_parse_question_with_notafter(0, 1, 1);
 
5475
 
test_open_and_parse_question_notafter_1_2(__attribute__((unused))
 
5476
 
                                          test_fixture *fixture,
 
5477
 
                                          __attribute__((unused))
 
5478
 
                                          gconstpointer user_data){
 
5479
 
  /* current_time, notafter, next_queue_run */
 
5480
 
  assert_open_and_parse_question_with_notafter(0, 1, 2);
 
5484
 
test_open_and_parse_question_equal_notafter(__attribute__((unused))
 
5485
 
                                            test_fixture *fixture,
 
5486
 
                                            __attribute__((unused))
 
5487
 
                                            gconstpointer user_data){
 
5488
 
  /* current_time, notafter, next_queue_run */
 
5489
 
  assert_open_and_parse_question_with_notafter(1, 1, 0);
 
5493
 
test_open_and_parse_question_late_notafter(__attribute__((unused))
 
5494
 
                                           test_fixture *fixture,
 
5495
 
                                           __attribute__((unused))
 
5496
 
                                           gconstpointer user_data){
 
5497
 
  /* current_time, notafter, next_queue_run */
 
5498
 
  assert_open_and_parse_question_with_notafter(2, 1, 0);
 
5501
 
static void assert_cancel_old_question_param(const mono_microsecs
 
5503
 
                                             const mono_microsecs
 
5505
 
                                             const mono_microsecs
 
5507
 
                                             const mono_microsecs
 
5509
 
  __attribute__((cleanup(string_set_clear)))
 
5510
 
    string_set cancelled_filenames = {};
 
5511
 
  __attribute__((cleanup(cleanup_queue)))
 
5512
 
    task_queue *queue = create_queue();
 
5513
 
  g_assert_nonnull(queue);
 
5514
 
  queue->next_run = next_queue_run;
 
5516
 
  char *const question_filename = strdup("/nonexistent");
 
5517
 
  g_assert_nonnull(question_filename);
 
5518
 
  task_context task = {
 
5519
 
    .func=cancel_old_question,
 
5520
 
    .question_filename=question_filename,
 
5521
 
    .filename=question_filename,
 
5523
 
    .cancelled_filenames=&cancelled_filenames,
 
5524
 
    .current_time=¤t_time,
 
5526
 
  task.func(task, queue);
 
5528
 
  if(current_time >= notafter){
 
5529
 
    g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5530
 
    g_assert_true(string_set_contains(cancelled_filenames,
 
5533
 
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
5534
 
          .func=cancel_old_question,
 
5535
 
          .question_filename=question_filename,
 
5536
 
          .filename=question_filename,
 
5538
 
          .cancelled_filenames=&cancelled_filenames,
 
5539
 
          .current_time=¤t_time,
 
5542
 
    g_assert_false(string_set_contains(cancelled_filenames,
 
5543
 
                                       question_filename));
 
5545
 
  g_assert_cmpuint((unsigned int)queue->next_run, ==,
 
5546
 
                   (unsigned int)next_set_to);
 
5549
 
static void test_cancel_old_question_0_1_2(__attribute__((unused))
 
5550
 
                                           test_fixture *fixture,
 
5551
 
                                           __attribute__((unused))
 
5552
 
                                           gconstpointer user_data){
 
5553
 
  /* next_queue_run unset,
 
5554
 
     cancellation should happen because time has come,
 
5555
 
     next_queue_run should be unchanged */
 
5556
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5557
 
  assert_cancel_old_question_param(0, 1, 2, 0);
 
5560
 
static void test_cancel_old_question_0_2_1(__attribute__((unused))
 
5561
 
                                           test_fixture *fixture,
 
5562
 
                                           __attribute__((unused))
 
5563
 
                                           gconstpointer user_data){
 
5564
 
  /* If next_queue_run is 0, meaning unset, and notafter is 2,
 
5565
 
     and current_time is not yet notafter or greater,
 
5566
 
     update value of next_queue_run to value of notafter */
 
5567
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5568
 
  assert_cancel_old_question_param(0, 2, 1, 2);
 
5571
 
static void test_cancel_old_question_1_2_3(__attribute__((unused))
 
5572
 
                                           test_fixture *fixture,
 
5573
 
                                           __attribute__((unused))
 
5574
 
                                           gconstpointer user_data){
 
5575
 
  /* next_queue_run 1,
 
5576
 
     cancellation should happen because time has come,
 
5577
 
     next_queue_run should be unchanged */
 
5578
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5579
 
  assert_cancel_old_question_param(1, 2, 3, 1);
 
5582
 
static void test_cancel_old_question_1_3_2(__attribute__((unused))
 
5583
 
                                           test_fixture *fixture,
 
5584
 
                                           __attribute__((unused))
 
5585
 
                                           gconstpointer user_data){
 
5586
 
  /* If next_queue_run is set,
 
5587
 
     and current_time is not yet notafter or greater,
 
5588
 
     and notafter is larger than next_queue_run
 
5589
 
     next_queue_run should be unchanged */
 
5590
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5591
 
  assert_cancel_old_question_param(1, 3, 2, 1);
 
5594
 
static void test_cancel_old_question_2_1_3(__attribute__((unused))
 
5595
 
                                           test_fixture *fixture,
 
5596
 
                                           __attribute__((unused))
 
5597
 
                                           gconstpointer user_data){
 
5598
 
  /* next_queue_run 2,
 
5599
 
     cancellation should happen because time has come,
 
5600
 
     next_queue_run should be unchanged */
 
5601
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5602
 
  assert_cancel_old_question_param(2, 1, 3, 2);
 
5605
 
static void test_cancel_old_question_2_3_1(__attribute__((unused))
 
5606
 
                                           test_fixture *fixture,
 
5607
 
                                           __attribute__((unused))
 
5608
 
                                           gconstpointer user_data){
 
5609
 
  /* If next_queue_run is set,
 
5610
 
     and current_time is not yet notafter or greater,
 
5611
 
     and notafter is larger than next_queue_run
 
5612
 
     next_queue_run should be unchanged */
 
5613
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5614
 
  assert_cancel_old_question_param(2, 3, 1, 2);
 
5617
 
static void test_cancel_old_question_3_1_2(__attribute__((unused))
 
5618
 
                                           test_fixture *fixture,
 
5619
 
                                           __attribute__((unused))
 
5620
 
                                           gconstpointer user_data){
 
5621
 
  /* next_queue_run 3,
 
5622
 
     cancellation should happen because time has come,
 
5623
 
     next_queue_run should be unchanged */
 
5624
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5625
 
  assert_cancel_old_question_param(3, 1, 2, 3);
 
5628
 
static void test_cancel_old_question_3_2_1(__attribute__((unused))
 
5629
 
                                           test_fixture *fixture,
 
5630
 
                                           __attribute__((unused))
 
5631
 
                                           gconstpointer user_data){
 
5632
 
  /* If next_queue_run is set,
 
5633
 
     and current_time is not yet notafter or greater,
 
5634
 
     and notafter is smaller than next_queue_run
 
5635
 
     update value of next_queue_run to value of notafter */
 
5636
 
  /* next_queue_run, notafter, current_time, next_set_to */
 
5637
 
  assert_cancel_old_question_param(3, 2, 1, 2);
 
5641
 
test_connect_question_socket_name_too_long(__attribute__((unused))
 
5642
 
                                           test_fixture *fixture,
 
5643
 
                                           __attribute__((unused))
 
5644
 
                                           gconstpointer user_data){
 
5645
 
  __attribute__((cleanup(cleanup_close)))
 
5646
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5647
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5648
 
  const char question_filename[] = "/nonexistent/question";
 
5649
 
  __attribute__((cleanup(string_set_clear)))
 
5650
 
    string_set cancelled_filenames = {};
 
5651
 
  __attribute__((cleanup(cleanup_queue)))
 
5652
 
    task_queue *queue = create_queue();
 
5653
 
  g_assert_nonnull(queue);
 
5654
 
  __attribute__((cleanup(cleanup_string)))
 
5655
 
    char *tempdir = make_temporary_directory();
 
5656
 
  g_assert_nonnull(tempdir);
 
5657
 
  struct sockaddr_un unix_socket = { .sun_family=AF_LOCAL };
 
5658
 
  char socket_name[sizeof(unix_socket.sun_path)];
 
5659
 
  memset(socket_name, 'x', sizeof(socket_name));
 
5660
 
  socket_name[sizeof(socket_name)-1] = '\0';
 
5661
 
  char *filename = NULL;
 
5662
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
5664
 
  g_assert_nonnull(filename);
 
5666
 
  task_context task = {
 
5667
 
    .func=connect_question_socket,
 
5668
 
    .question_filename=strdup(question_filename),
 
5670
 
    .password=(buffer[]){{}},
 
5672
 
    .cancelled_filenames=&cancelled_filenames,
 
5673
 
    .mandos_client_exited=(bool[]){false},
 
5674
 
    .password_is_read=(bool[]){false},
 
5675
 
    .current_time=(mono_microsecs[]){0},
 
5677
 
  g_assert_nonnull(task.question_filename);
 
5678
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5680
 
  g_assert_true(string_set_contains(cancelled_filenames,
 
5681
 
                                    question_filename));
 
5682
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
5683
 
  g_assert_true(queue->next_run == 0);
 
5685
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
5689
 
void test_connect_question_socket_connect_fail(__attribute__((unused))
 
5690
 
                                               test_fixture *fixture,
 
5691
 
                                               __attribute__((unused))
 
5694
 
  __attribute__((cleanup(cleanup_close)))
 
5695
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5696
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5697
 
  const char question_filename[] = "/nonexistent/question";
 
5698
 
  __attribute__((cleanup(string_set_clear)))
 
5699
 
    string_set cancelled_filenames = {};
 
5700
 
  const mono_microsecs current_time = 3;
 
5701
 
  __attribute__((cleanup(cleanup_queue)))
 
5702
 
    task_queue *queue = create_queue();
 
5703
 
  g_assert_nonnull(queue);
 
5704
 
  __attribute__((cleanup(cleanup_string)))
 
5705
 
    char *tempdir = make_temporary_directory();
 
5706
 
  g_assert_nonnull(tempdir);
 
5707
 
  char socket_name[] = "nonexistent";
 
5708
 
  char *filename = NULL;
 
5709
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
5711
 
  g_assert_nonnull(filename);
 
5713
 
  task_context task = {
 
5714
 
    .func=connect_question_socket,
 
5715
 
    .question_filename=strdup(question_filename),
 
5717
 
    .password=(buffer[]){{}},
 
5719
 
    .cancelled_filenames=&cancelled_filenames,
 
5720
 
    .mandos_client_exited=(bool[]){false},
 
5721
 
    .password_is_read=(bool[]){false},
 
5722
 
    .current_time=¤t_time,
 
5724
 
  g_assert_nonnull(task.question_filename);
 
5725
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5727
 
  g_assert_nonnull(find_matching_task(queue, task));
 
5729
 
  g_assert_false(string_set_contains(cancelled_filenames,
 
5730
 
                                     question_filename));
 
5731
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5732
 
  g_assert_true(queue->next_run == 1000000 + current_time);
 
5734
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
5738
 
void test_connect_question_socket_bad_epoll(__attribute__((unused))
 
5739
 
                                            test_fixture *fixture,
 
5740
 
                                            __attribute__((unused))
 
5741
 
                                            gconstpointer user_data){
 
5742
 
  __attribute__((cleanup(cleanup_close)))
 
5743
 
    const int epoll_fd = open("/dev/null",
 
5744
 
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
5745
 
  __attribute__((cleanup(cleanup_string)))
 
5746
 
    char *const question_filename = strdup("/nonexistent/question");
 
5747
 
  g_assert_nonnull(question_filename);
 
5748
 
  __attribute__((cleanup(string_set_clear)))
 
5749
 
    string_set cancelled_filenames = {};
 
5750
 
  const mono_microsecs current_time = 5;
 
5751
 
  __attribute__((cleanup(cleanup_queue)))
 
5752
 
    task_queue *queue = create_queue();
 
5753
 
  g_assert_nonnull(queue);
 
5754
 
  __attribute__((cleanup(cleanup_string)))
 
5755
 
    char *tempdir = make_temporary_directory();
 
5756
 
  g_assert_nonnull(tempdir);
 
5757
 
  __attribute__((cleanup(cleanup_close)))
 
5758
 
    const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
 
5759
 
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 
5760
 
  g_assert_cmpint(sock_fd, >=, 0);
 
5761
 
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
 
5762
 
  const char socket_name[] = "socket_name";
 
5763
 
  __attribute__((cleanup(cleanup_string)))
 
5764
 
    char *filename = NULL;
 
5765
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
5767
 
  g_assert_nonnull(filename);
 
5768
 
  g_assert_cmpint((int)strlen(filename), <,
 
5769
 
                  (int)sizeof(sock_name.sun_path));
 
5770
 
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
 
5771
 
  sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
 
5772
 
  g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
 
5773
 
                            (socklen_t)SUN_LEN(&sock_name)), >=, 0);
 
5774
 
  task_context task = {
 
5775
 
    .func=connect_question_socket,
 
5776
 
    .question_filename=strdup(question_filename),
 
5778
 
    .password=(buffer[]){{}},
 
5779
 
    .filename=strdup(filename),
 
5780
 
    .cancelled_filenames=&cancelled_filenames,
 
5781
 
    .mandos_client_exited=(bool[]){false},
 
5782
 
    .password_is_read=(bool[]){false},
 
5783
 
    .current_time=¤t_time,
 
5785
 
  g_assert_nonnull(task.question_filename);
 
5786
 
  run_task_with_stderr_to_dev_null(task, queue);
 
5788
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5789
 
  const task_context *const added_task
 
5790
 
    = find_matching_task(queue, task);
 
5791
 
  g_assert_nonnull(added_task);
 
5792
 
  g_assert_true(queue->next_run == 1000000 + current_time);
 
5794
 
  g_assert_cmpint(unlink(filename), ==, 0);
 
5795
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
5799
 
void test_connect_question_socket_usable(__attribute__((unused))
 
5800
 
                                         test_fixture *fixture,
 
5801
 
                                         __attribute__((unused))
 
5802
 
                                         gconstpointer user_data){
 
5803
 
  __attribute__((cleanup(cleanup_close)))
 
5804
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5805
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5806
 
  __attribute__((cleanup(cleanup_string)))
 
5807
 
    char *const question_filename = strdup("/nonexistent/question");
 
5808
 
  g_assert_nonnull(question_filename);
 
5809
 
  __attribute__((cleanup(string_set_clear)))
 
5810
 
    string_set cancelled_filenames = {};
 
5811
 
  buffer password = {};
 
5812
 
  bool mandos_client_exited = false;
 
5813
 
  bool password_is_read = false;
 
5814
 
  const mono_microsecs current_time = 0;
 
5815
 
  __attribute__((cleanup(cleanup_queue)))
 
5816
 
    task_queue *queue = create_queue();
 
5817
 
  g_assert_nonnull(queue);
 
5818
 
  __attribute__((cleanup(cleanup_string)))
 
5819
 
    char *tempdir = make_temporary_directory();
 
5820
 
  g_assert_nonnull(tempdir);
 
5821
 
  __attribute__((cleanup(cleanup_close)))
 
5822
 
    const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
 
5823
 
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 
5824
 
  g_assert_cmpint(sock_fd, >=, 0);
 
5825
 
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
 
5826
 
  const char socket_name[] = "socket_name";
 
5827
 
  __attribute__((cleanup(cleanup_string)))
 
5828
 
    char *filename = NULL;
 
5829
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
5831
 
  g_assert_nonnull(filename);
 
5832
 
  g_assert_cmpint((int)strlen(filename), <,
 
5833
 
                  (int)sizeof(sock_name.sun_path));
 
5834
 
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
 
5835
 
  sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
 
5836
 
  g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
 
5837
 
                            (socklen_t)SUN_LEN(&sock_name)), >=, 0);
 
5838
 
  task_context task = {
 
5839
 
    .func=connect_question_socket,
 
5840
 
    .question_filename=strdup(question_filename),
 
5842
 
    .password=&password,
 
5843
 
    .filename=strdup(filename),
 
5844
 
    .cancelled_filenames=&cancelled_filenames,
 
5845
 
    .mandos_client_exited=&mandos_client_exited,
 
5846
 
    .password_is_read=&password_is_read,
 
5847
 
    .current_time=¤t_time,
 
5849
 
  g_assert_nonnull(task.question_filename);
 
5850
 
  task.func(task, queue);
 
5852
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5853
 
  const task_context *const added_task
 
5854
 
    = find_matching_task(queue, (task_context){
 
5855
 
        .func=send_password_to_socket,
 
5856
 
        .question_filename=question_filename,
 
5859
 
        .password=&password,
 
5860
 
        .cancelled_filenames=&cancelled_filenames,
 
5861
 
        .mandos_client_exited=&mandos_client_exited,
 
5862
 
        .password_is_read=&password_is_read,
 
5863
 
        .current_time=¤t_time,
 
5865
 
  g_assert_nonnull(added_task);
 
5866
 
  g_assert_cmpint(added_task->fd, >, 0);
 
5868
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
5871
 
  const int fd = added_task->fd;
 
5872
 
  g_assert_cmpint(fd, >, 0);
 
5873
 
  g_assert_true(fd_has_cloexec_and_nonblock(fd));
 
5876
 
  char write_data[PIPE_BUF];
 
5878
 
    /* Construct test password buffer */
 
5879
 
    /* Start with + since that is what the real protocol uses */
 
5880
 
    write_data[0] = '+';
 
5881
 
    /* Set a special character at string end just to mark the end */
 
5882
 
    write_data[sizeof(write_data)-2] = 'y';
 
5883
 
    /* Set NUL at buffer end, as suggested by the protocol */
 
5884
 
    write_data[sizeof(write_data)-1] = '\0';
 
5885
 
    /* Fill rest of password with 'x' */
 
5886
 
    memset(write_data+1, 'x', sizeof(write_data)-3);
 
5887
 
    g_assert_cmpint((int)send(fd, write_data, sizeof(write_data),
 
5888
 
                              MSG_NOSIGNAL), ==, sizeof(write_data));
 
5891
 
  /* read from sock_fd */
 
5892
 
  char read_data[sizeof(write_data)];
 
5893
 
  g_assert_cmpint((int)read(sock_fd, read_data, sizeof(read_data)),
 
5894
 
                  ==, sizeof(read_data));
 
5896
 
  g_assert_true(memcmp(write_data, read_data, sizeof(write_data))
 
5899
 
  /* writing to sock_fd should fail */
 
5900
 
  g_assert_cmpint(send(sock_fd, write_data, sizeof(write_data),
 
5901
 
                       MSG_NOSIGNAL), <, 0);
 
5903
 
  /* reading from fd should fail */
 
5904
 
  g_assert_cmpint((int)recv(fd, read_data, sizeof(read_data),
 
5905
 
                            MSG_NOSIGNAL), <, 0);
 
5907
 
  g_assert_cmpint(unlink(filename), ==, 0);
 
5908
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
5912
 
test_send_password_to_socket_client_not_exited(__attribute__((unused))
 
5913
 
                                               test_fixture *fixture,
 
5914
 
                                               __attribute__((unused))
 
5917
 
  __attribute__((cleanup(cleanup_close)))
 
5918
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5919
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5920
 
  __attribute__((cleanup(cleanup_string)))
 
5921
 
    char *const question_filename = strdup("/nonexistent/question");
 
5922
 
  g_assert_nonnull(question_filename);
 
5923
 
  __attribute__((cleanup(cleanup_string)))
 
5924
 
    char *const filename = strdup("/nonexistent/socket");
 
5925
 
  g_assert_nonnull(filename);
 
5926
 
  __attribute__((cleanup(string_set_clear)))
 
5927
 
    string_set cancelled_filenames = {};
 
5928
 
  buffer password = {};
 
5929
 
  bool password_is_read = true;
 
5930
 
  __attribute__((cleanup(cleanup_queue)))
 
5931
 
    task_queue *queue = create_queue();
 
5932
 
  g_assert_nonnull(queue);
 
5934
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
5935
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
5937
 
  __attribute__((cleanup(cleanup_close)))
 
5938
 
    const int read_socket = socketfds[0];
 
5939
 
  const int write_socket = socketfds[1];
 
5940
 
  task_context task = {
 
5941
 
    .func=send_password_to_socket,
 
5942
 
    .question_filename=strdup(question_filename),
 
5943
 
    .filename=strdup(filename),
 
5946
 
    .password=&password,
 
5947
 
    .cancelled_filenames=&cancelled_filenames,
 
5948
 
    .mandos_client_exited=(bool[]){false},
 
5949
 
    .password_is_read=&password_is_read,
 
5950
 
    .current_time=(mono_microsecs[]){0},
 
5952
 
  g_assert_nonnull(task.question_filename);
 
5954
 
  task.func(task, queue);
 
5956
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
5958
 
  const task_context *const added_task
 
5959
 
    = find_matching_task(queue, task);
 
5960
 
  g_assert_nonnull(added_task);
 
5961
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
5962
 
  g_assert_true(password_is_read);
 
5964
 
  g_assert_cmpint(added_task->fd, >, 0);
 
5965
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
5970
 
test_send_password_to_socket_password_not_read(__attribute__((unused))
 
5971
 
                                               test_fixture *fixture,
 
5972
 
                                               __attribute__((unused))
 
5975
 
  __attribute__((cleanup(cleanup_close)))
 
5976
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
5977
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
5978
 
  __attribute__((cleanup(cleanup_string)))
 
5979
 
    char *const question_filename = strdup("/nonexistent/question");
 
5980
 
  g_assert_nonnull(question_filename);
 
5981
 
  __attribute__((cleanup(cleanup_string)))
 
5982
 
    char *const filename = strdup("/nonexistent/socket");
 
5983
 
  __attribute__((cleanup(string_set_clear)))
 
5984
 
    string_set cancelled_filenames = {};
 
5985
 
  buffer password = {};
 
5986
 
  __attribute__((cleanup(cleanup_queue)))
 
5987
 
    task_queue *queue = create_queue();
 
5988
 
  g_assert_nonnull(queue);
 
5990
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
5991
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
5993
 
  __attribute__((cleanup(cleanup_close)))
 
5994
 
    const int read_socket = socketfds[0];
 
5995
 
  const int write_socket = socketfds[1];
 
5996
 
  task_context task = {
 
5997
 
    .func=send_password_to_socket,
 
5998
 
    .question_filename=strdup(question_filename),
 
5999
 
    .filename=strdup(filename),
 
6002
 
    .password=&password,
 
6003
 
    .cancelled_filenames=&cancelled_filenames,
 
6004
 
    .mandos_client_exited=(bool[]){false},
 
6005
 
    .password_is_read=(bool[]){false},
 
6006
 
    .current_time=(mono_microsecs[]){0},
 
6008
 
  g_assert_nonnull(task.question_filename);
 
6010
 
  task.func(task, queue);
 
6012
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
6014
 
  const task_context *const added_task = find_matching_task(queue,
 
6016
 
  g_assert_nonnull(added_task);
 
6017
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
6018
 
  g_assert_true(queue->next_run == 0);
 
6020
 
  g_assert_cmpint(added_task->fd, >, 0);
 
6021
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
6026
 
void test_send_password_to_socket_EMSGSIZE(__attribute__((unused))
 
6027
 
                                           test_fixture *fixture,
 
6028
 
                                           __attribute__((unused))
 
6029
 
                                           gconstpointer user_data){
 
6030
 
  __attribute__((cleanup(cleanup_close)))
 
6031
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6032
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6033
 
  const char question_filename[] = "/nonexistent/question";
 
6034
 
  char *const filename = strdup("/nonexistent/socket");
 
6035
 
  __attribute__((cleanup(string_set_clear)))
 
6036
 
    string_set cancelled_filenames = {};
 
6039
 
  /* Find a message size which triggers EMSGSIZE */
 
6040
 
  __attribute__((cleanup(cleanup_string)))
 
6041
 
    char *message_buffer = NULL;
 
6042
 
  size_t message_size = PIPE_BUF + 1;
 
6043
 
  for(ssize_t ssret = 0; ssret >= 0; message_size += 1024){
 
6044
 
    if(message_size >= 1024*1024*1024){ /* 1 GiB */
 
6045
 
      g_test_skip("Skipping EMSGSIZE test: Will not try 1GiB");
 
6048
 
    message_buffer = realloc(message_buffer, message_size);
 
6049
 
    if(message_buffer == NULL){
 
6050
 
      g_test_skip("Skipping EMSGSIZE test");
 
6051
 
      g_test_message("Failed to malloc() %" PRIuMAX " bytes",
 
6052
 
                     (uintmax_t)message_size);
 
6055
 
    /* Fill buffer with 'x' */
 
6056
 
    memset(message_buffer, 'x', message_size);
 
6057
 
    /* Create a new socketpair for each message size to avoid having
 
6058
 
       to empty the pipe by reading the message to a separate buffer
 
6060
 
    g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
6061
 
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
6063
 
    ssret = send(socketfds[1], message_buffer, message_size,
 
6065
 
    error_t saved_errno = errno;
 
6066
 
    g_assert_cmpint(close(socketfds[0]), ==, 0);
 
6067
 
    g_assert_cmpint(close(socketfds[1]), ==, 0);
 
6070
 
      if(saved_errno != EMSGSIZE) {
 
6071
 
        g_test_skip("Skipping EMSGSIZE test");
 
6072
 
        g_test_message("Error on send(%" PRIuMAX " bytes): %s",
 
6073
 
                       (uintmax_t)message_size,
 
6074
 
                       strerror(saved_errno));
 
6078
 
    } else if(ssret != (ssize_t)message_size){
 
6079
 
      g_test_skip("Skipping EMSGSIZE test");
 
6080
 
      g_test_message("Partial send(): %" PRIuMAX " of %" PRIdMAX
 
6081
 
                     " bytes", (uintmax_t)ssret,
 
6082
 
                     (intmax_t)message_size);
 
6086
 
  g_test_message("EMSGSIZE triggered by %" PRIdMAX " bytes",
 
6087
 
                 (intmax_t)message_size);
 
6090
 
    .data=message_buffer,
 
6091
 
    .length=message_size - 2,   /* Compensate for added '+' and NUL */
 
6092
 
    .allocated=message_size,
 
6094
 
  if(mlock(password.data, password.allocated) != 0){
 
6095
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
6098
 
  __attribute__((cleanup(cleanup_queue)))
 
6099
 
    task_queue *queue = create_queue();
 
6100
 
  g_assert_nonnull(queue);
 
6101
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
6102
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
6104
 
  __attribute__((cleanup(cleanup_close)))
 
6105
 
    const int read_socket = socketfds[0];
 
6106
 
  __attribute__((cleanup(cleanup_close)))
 
6107
 
    const int write_socket = socketfds[1];
 
6108
 
  task_context task = {
 
6109
 
    .func=send_password_to_socket,
 
6110
 
    .question_filename=strdup(question_filename),
 
6114
 
    .password=&password,
 
6115
 
    .cancelled_filenames=&cancelled_filenames,
 
6116
 
    .mandos_client_exited=(bool[]){true},
 
6117
 
    .password_is_read=(bool[]){true},
 
6118
 
    .current_time=(mono_microsecs[]){0},
 
6120
 
  g_assert_nonnull(task.question_filename);
 
6122
 
  run_task_with_stderr_to_dev_null(task, queue);
 
6124
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
6125
 
  g_assert_true(string_set_contains(cancelled_filenames,
 
6126
 
                                    question_filename));
 
6129
 
static void test_send_password_to_socket_retry(__attribute__((unused))
 
6130
 
                                               test_fixture *fixture,
 
6131
 
                                               __attribute__((unused))
 
6134
 
  __attribute__((cleanup(cleanup_close)))
 
6135
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6136
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6137
 
  __attribute__((cleanup(cleanup_string)))
 
6138
 
    char *const question_filename = strdup("/nonexistent/question");
 
6139
 
  g_assert_nonnull(question_filename);
 
6140
 
  __attribute__((cleanup(cleanup_string)))
 
6141
 
    char *const filename = strdup("/nonexistent/socket");
 
6142
 
  g_assert_nonnull(filename);
 
6143
 
  __attribute__((cleanup(string_set_clear)))
 
6144
 
    string_set cancelled_filenames = {};
 
6145
 
  __attribute__((cleanup(cleanup_buffer)))
 
6146
 
    buffer password = {};
 
6148
 
  __attribute__((cleanup(cleanup_queue)))
 
6149
 
    task_queue *queue = create_queue();
 
6150
 
  g_assert_nonnull(queue);
 
6152
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
6153
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
6155
 
  __attribute__((cleanup(cleanup_close)))
 
6156
 
    const int read_socket = socketfds[0];
 
6157
 
  const int write_socket = socketfds[1];
 
6158
 
  /* Close the server side socket to force ECONNRESET on client */
 
6159
 
  g_assert_cmpint(close(read_socket), ==, 0);
 
6160
 
  task_context task = {
 
6161
 
    .func=send_password_to_socket,
 
6162
 
    .question_filename=strdup(question_filename),
 
6163
 
    .filename=strdup(filename),
 
6166
 
    .password=&password,
 
6167
 
    .cancelled_filenames=&cancelled_filenames,
 
6168
 
    .mandos_client_exited=(bool[]){true},
 
6169
 
    .password_is_read=(bool[]){true},
 
6170
 
    .current_time=(mono_microsecs[]){0},
 
6172
 
  g_assert_nonnull(task.question_filename);
 
6174
 
  task.func(task, queue);
 
6176
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
6178
 
  const task_context *const added_task = find_matching_task(queue,
 
6180
 
  g_assert_nonnull(added_task);
 
6181
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
6183
 
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
6188
 
void test_send_password_to_socket_bad_epoll(__attribute__((unused))
 
6189
 
                                            test_fixture *fixture,
 
6190
 
                                            __attribute__((unused))
 
6191
 
                                            gconstpointer user_data){
 
6192
 
  __attribute__((cleanup(cleanup_close)))
 
6193
 
    const int epoll_fd = open("/dev/null",
 
6194
 
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
6195
 
  __attribute__((cleanup(cleanup_string)))
 
6196
 
    char *const question_filename = strdup("/nonexistent/question");
 
6197
 
  g_assert_nonnull(question_filename);
 
6198
 
  __attribute__((cleanup(cleanup_string)))
 
6199
 
    char *const filename = strdup("/nonexistent/socket");
 
6200
 
  g_assert_nonnull(filename);
 
6201
 
  __attribute__((cleanup(string_set_clear)))
 
6202
 
    string_set cancelled_filenames = {};
 
6203
 
  __attribute__((cleanup(cleanup_buffer)))
 
6204
 
    buffer password = {};
 
6206
 
  const mono_microsecs current_time = 11;
 
6207
 
  __attribute__((cleanup(cleanup_queue)))
 
6208
 
    task_queue *queue = create_queue();
 
6209
 
  g_assert_nonnull(queue);
 
6211
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
6212
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
6214
 
  __attribute__((cleanup(cleanup_close)))
 
6215
 
    const int read_socket = socketfds[0];
 
6216
 
  const int write_socket = socketfds[1];
 
6217
 
  /* Close the server side socket to force ECONNRESET on client */
 
6218
 
  g_assert_cmpint(close(read_socket), ==, 0);
 
6219
 
  task_context task = {
 
6220
 
    .func=send_password_to_socket,
 
6221
 
    .question_filename=strdup(question_filename),
 
6222
 
    .filename=strdup(filename),
 
6225
 
    .password=&password,
 
6226
 
    .cancelled_filenames=&cancelled_filenames,
 
6227
 
    .mandos_client_exited=(bool[]){true},
 
6228
 
    .password_is_read=(bool[]){true},
 
6229
 
    .current_time=¤t_time,
 
6231
 
  g_assert_nonnull(task.question_filename);
 
6233
 
  run_task_with_stderr_to_dev_null(task, queue);
 
6235
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
6237
 
  const task_context *const added_task = find_matching_task(queue,
 
6239
 
  g_assert_nonnull(added_task);
 
6240
 
  g_assert_true(queue->next_run == current_time + 1000000);
 
6241
 
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
6244
 
static void assert_send_password_to_socket_password(buffer password){
 
6245
 
  __attribute__((cleanup(cleanup_close)))
 
6246
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6247
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6248
 
  char *const question_filename = strdup("/nonexistent/question");
 
6249
 
  g_assert_nonnull(question_filename);
 
6250
 
  char *const filename = strdup("/nonexistent/socket");
 
6251
 
  g_assert_nonnull(filename);
 
6252
 
  __attribute__((cleanup(string_set_clear)))
 
6253
 
    string_set cancelled_filenames = {};
 
6255
 
  __attribute__((cleanup(cleanup_queue)))
 
6256
 
    task_queue *queue = create_queue();
 
6257
 
  g_assert_nonnull(queue);
 
6259
 
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
6260
 
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
6262
 
  __attribute__((cleanup(cleanup_close)))
 
6263
 
    const int read_socket = socketfds[0];
 
6264
 
  const int write_socket = socketfds[1];
 
6265
 
  task_context task = {
 
6266
 
    .func=send_password_to_socket,
 
6267
 
    .question_filename=question_filename,
 
6271
 
    .password=&password,
 
6272
 
    .cancelled_filenames=&cancelled_filenames,
 
6273
 
    .mandos_client_exited=(bool[]){true},
 
6274
 
    .password_is_read=(bool[]){true},
 
6275
 
    .current_time=(mono_microsecs[]){0},
 
6278
 
  char *expected_written_data = malloc(password.length + 2);
 
6279
 
  g_assert_nonnull(expected_written_data);
 
6280
 
  expected_written_data[0] = '+';
 
6281
 
  expected_written_data[password.length + 1] = '\0';
 
6282
 
  if(password.length > 0){
 
6283
 
    g_assert_nonnull(password.data);
 
6284
 
    memcpy(expected_written_data + 1, password.data, password.length);
 
6287
 
  task.func(task, queue);
 
6290
 
  g_assert_cmpint((int)read(read_socket, buf, PIPE_BUF), ==,
 
6291
 
                  (int)(password.length + 2));
 
6292
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
6294
 
  g_assert_true(memcmp(expected_written_data, buf,
 
6295
 
                       password.length + 2) == 0);
 
6297
 
  g_assert_true(epoll_set_does_not_contain(epoll_fd, write_socket));
 
6299
 
  free(expected_written_data);
 
6303
 
test_send_password_to_socket_null_password(__attribute__((unused))
 
6304
 
                                           test_fixture *fixture,
 
6305
 
                                           __attribute__((unused))
 
6306
 
                                           gconstpointer user_data){
 
6307
 
  __attribute__((cleanup(cleanup_buffer)))
 
6308
 
    buffer password = {};
 
6309
 
  assert_send_password_to_socket_password(password);
 
6313
 
test_send_password_to_socket_empty_password(__attribute__((unused))
 
6314
 
                                            test_fixture *fixture,
 
6315
 
                                            __attribute__((unused))
 
6316
 
                                            gconstpointer user_data){
 
6317
 
  __attribute__((cleanup(cleanup_buffer)))
 
6319
 
    .data=malloc(1),           /* because malloc(0) may return NULL */
 
6321
 
    .allocated=0,               /* deliberate lie */
 
6323
 
  g_assert_nonnull(password.data);
 
6324
 
  assert_send_password_to_socket_password(password);
 
6328
 
test_send_password_to_socket_empty_str_pass(__attribute__((unused))
 
6329
 
                                            test_fixture *fixture,
 
6330
 
                                            __attribute__((unused))
 
6331
 
                                            gconstpointer user_data){
 
6332
 
  __attribute__((cleanup(cleanup_buffer)))
 
6338
 
  if(mlock(password.data, password.allocated) != 0){
 
6339
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
6341
 
  assert_send_password_to_socket_password(password);
 
6345
 
test_send_password_to_socket_text_password(__attribute__((unused))
 
6346
 
                                           test_fixture *fixture,
 
6347
 
                                           __attribute__((unused))
 
6348
 
                                           gconstpointer user_data){
 
6349
 
  const char dummy_test_password[] = "dummy test password";
 
6350
 
  __attribute__((cleanup(cleanup_buffer)))
 
6352
 
    .data = strdup(dummy_test_password),
 
6353
 
    .length = strlen(dummy_test_password),
 
6354
 
    .allocated = sizeof(dummy_test_password),
 
6356
 
  if(mlock(password.data, password.allocated) != 0){
 
6357
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
6359
 
  assert_send_password_to_socket_password(password);
 
6363
 
test_send_password_to_socket_binary_password(__attribute__((unused))
 
6364
 
                                             test_fixture *fixture,
 
6365
 
                                             __attribute__((unused))
 
6366
 
                                             gconstpointer user_data){
 
6367
 
  __attribute__((cleanup(cleanup_buffer)))
 
6373
 
  g_assert_nonnull(password.data);
 
6374
 
  if(mlock(password.data, password.allocated) != 0){
 
6375
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
6377
 
  char c = 1;                   /* Start at 1, avoiding NUL */
 
6378
 
  for(int i=0; i < 255; i++){
 
6379
 
    password.data[i] = c++;
 
6381
 
  assert_send_password_to_socket_password(password);
 
6385
 
test_send_password_to_socket_nuls_in_password(__attribute__((unused))
 
6386
 
                                              test_fixture *fixture,
 
6387
 
                                              __attribute__((unused))
 
6390
 
  char test_password[] = {'\0', 'a', '\0', 'b', '\0', 'c', '\0'};
 
6391
 
  __attribute__((cleanup(cleanup_buffer)))
 
6393
 
    .data=malloc(sizeof(test_password)),
 
6394
 
    .length=sizeof(test_password),
 
6395
 
    .allocated=sizeof(test_password),
 
6397
 
  g_assert_nonnull(password.data);
 
6398
 
  if(mlock(password.data, password.allocated) !=0){
 
6399
 
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
6401
 
  memcpy(password.data, test_password, password.allocated);
 
6402
 
  assert_send_password_to_socket_password(password);
 
6405
 
static bool assert_add_existing_questions_to_devnull(task_queue
 
6418
 
static void test_add_existing_questions_ENOENT(__attribute__((unused))
 
6419
 
                                               test_fixture *fixture,
 
6420
 
                                               __attribute__((unused))
 
6423
 
  __attribute__((cleanup(cleanup_queue)))
 
6424
 
    task_queue *queue = create_queue();
 
6425
 
  g_assert_nonnull(queue);
 
6426
 
  __attribute__((cleanup(cleanup_close)))
 
6427
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6428
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6429
 
  __attribute__((cleanup(string_set_clear)))
 
6430
 
    string_set cancelled_filenames = {};
 
6432
 
  g_assert_false(assert_add_existing_questions_to_devnull
 
6435
 
                  (buffer[]){{}}, /* password */
 
6436
 
                  &cancelled_filenames,
 
6437
 
                  (mono_microsecs[]){0}, /* current_time */
 
6438
 
                  (bool[]){false},       /* mandos_client_exited */
 
6439
 
                  (bool[]){false},       /* password_is_read */
 
6440
 
                  "/nonexistent"));      /* dirname */
 
6442
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
6446
 
bool assert_add_existing_questions_to_devnull(task_queue
 
6453
 
                                              *cancelled_filenames,
 
6454
 
                                              const mono_microsecs
 
6455
 
                                              *const current_time,
 
6457
 
                                              mandos_client_exited,
 
6462
 
  __attribute__((cleanup(cleanup_close)))
 
6463
 
    const int devnull_fd = open("/dev/null",
 
6464
 
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
6465
 
  g_assert_cmpint(devnull_fd, >=, 0);
 
6466
 
  __attribute__((cleanup(cleanup_close)))
 
6467
 
    const int real_stderr_fd = dup(STDERR_FILENO);
 
6468
 
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
6469
 
  dup2(devnull_fd, STDERR_FILENO);
 
6470
 
  const bool ret = add_existing_questions(queue, epoll_fd, password,
 
6471
 
                                          cancelled_filenames,
 
6473
 
                                          mandos_client_exited,
 
6474
 
                                          password_is_read, dirname);
 
6475
 
  dup2(real_stderr_fd, STDERR_FILENO);
 
6480
 
void test_add_existing_questions_no_questions(__attribute__((unused))
 
6481
 
                                              test_fixture *fixture,
 
6482
 
                                              __attribute__((unused))
 
6485
 
  __attribute__((cleanup(cleanup_queue)))
 
6486
 
    task_queue *queue = create_queue();
 
6487
 
  g_assert_nonnull(queue);
 
6488
 
  __attribute__((cleanup(cleanup_close)))
 
6489
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6490
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6491
 
  __attribute__((cleanup(string_set_clear)))
 
6492
 
    string_set cancelled_filenames = {};
 
6493
 
  __attribute__((cleanup(cleanup_string)))
 
6494
 
    char *tempdir = make_temporary_directory();
 
6495
 
  g_assert_nonnull(tempdir);
 
6497
 
  g_assert_false(assert_add_existing_questions_to_devnull
 
6500
 
                  (buffer[]){{}}, /* password */
 
6501
 
                  &cancelled_filenames,
 
6502
 
                  (mono_microsecs[]){0}, /* current_time */
 
6503
 
                  (bool[]){false},       /* mandos_client_exited */
 
6504
 
                  (bool[]){false},       /* password_is_read */
 
6507
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
6509
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
6512
 
static char *make_question_file_in_directory(const char *const);
 
6515
 
void test_add_existing_questions_one_question(__attribute__((unused))
 
6516
 
                                              test_fixture *fixture,
 
6517
 
                                              __attribute__((unused))
 
6520
 
  __attribute__((cleanup(cleanup_queue)))
 
6521
 
    task_queue *queue = create_queue();
 
6522
 
  g_assert_nonnull(queue);
 
6523
 
  __attribute__((cleanup(cleanup_close)))
 
6524
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6525
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6526
 
  __attribute__((cleanup(cleanup_buffer)))
 
6527
 
    buffer password = {};
 
6528
 
  __attribute__((cleanup(string_set_clear)))
 
6529
 
    string_set cancelled_filenames = {};
 
6530
 
  const mono_microsecs current_time = 0;
 
6531
 
  bool mandos_client_exited = false;
 
6532
 
  bool password_is_read = false;
 
6533
 
  __attribute__((cleanup(cleanup_string)))
 
6534
 
    char *tempdir = make_temporary_directory();
 
6535
 
  g_assert_nonnull(tempdir);
 
6536
 
  __attribute__((cleanup(cleanup_string)))
 
6537
 
    char *question_filename
 
6538
 
    = make_question_file_in_directory(tempdir);
 
6539
 
  g_assert_nonnull(question_filename);
 
6541
 
  g_assert_true(assert_add_existing_questions_to_devnull
 
6545
 
                 &cancelled_filenames,
 
6547
 
                 &mandos_client_exited,
 
6551
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
6553
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
6554
 
        .func=open_and_parse_question,
 
6556
 
        .filename=question_filename,
 
6557
 
        .question_filename=question_filename,
 
6558
 
        .password=&password,
 
6559
 
        .cancelled_filenames=&cancelled_filenames,
 
6560
 
        .current_time=¤t_time,
 
6561
 
        .mandos_client_exited=&mandos_client_exited,
 
6562
 
        .password_is_read=&password_is_read,
 
6565
 
  g_assert_true(queue->next_run == 1);
 
6567
 
  g_assert_cmpint(unlink(question_filename), ==, 0);
 
6568
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
6571
 
static char *make_question_file_in_directory(const char
 
6573
 
  return make_temporary_prefixed_file_in_directory("ask.", dir);
 
6577
 
void test_add_existing_questions_two_questions(__attribute__((unused))
 
6578
 
                                               test_fixture *fixture,
 
6579
 
                                               __attribute__((unused))
 
6582
 
  __attribute__((cleanup(cleanup_queue)))
 
6583
 
    task_queue *queue = create_queue();
 
6584
 
  g_assert_nonnull(queue);
 
6585
 
  __attribute__((cleanup(cleanup_close)))
 
6586
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6587
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6588
 
  __attribute__((cleanup(cleanup_buffer)))
 
6589
 
    buffer password = {};
 
6590
 
  __attribute__((cleanup(string_set_clear)))
 
6591
 
    string_set cancelled_filenames = {};
 
6592
 
  const mono_microsecs current_time = 0;
 
6593
 
  bool mandos_client_exited = false;
 
6594
 
  bool password_is_read = false;
 
6595
 
  __attribute__((cleanup(cleanup_string)))
 
6596
 
    char *tempdir = make_temporary_directory();
 
6597
 
  g_assert_nonnull(tempdir);
 
6598
 
  __attribute__((cleanup(cleanup_string)))
 
6599
 
    char *question_filename1
 
6600
 
    = make_question_file_in_directory(tempdir);
 
6601
 
  g_assert_nonnull(question_filename1);
 
6602
 
  __attribute__((cleanup(cleanup_string)))
 
6603
 
    char *question_filename2
 
6604
 
    = make_question_file_in_directory(tempdir);
 
6605
 
  g_assert_nonnull(question_filename2);
 
6607
 
  g_assert_true(assert_add_existing_questions_to_devnull
 
6611
 
                 &cancelled_filenames,
 
6613
 
                 &mandos_client_exited,
 
6617
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 2);
 
6619
 
  g_assert_true(queue->next_run == 1);
 
6621
 
  __attribute__((cleanup(string_set_clear)))
 
6622
 
    string_set seen_questions = {};
 
6624
 
  bool queue_contains_question_opener(char *const question_filename){
 
6625
 
    return(find_matching_task(queue, (task_context){
 
6626
 
          .func=open_and_parse_question,
 
6628
 
          .question_filename=question_filename,
 
6629
 
          .password=&password,
 
6630
 
          .cancelled_filenames=&cancelled_filenames,
 
6631
 
          .current_time=¤t_time,
 
6632
 
          .mandos_client_exited=&mandos_client_exited,
 
6633
 
          .password_is_read=&password_is_read,
 
6637
 
  g_assert_true(queue_contains_question_opener(question_filename1));
 
6638
 
  g_assert_true(queue_contains_question_opener(question_filename2));
 
6640
 
  g_assert_true(queue->next_run == 1);
 
6642
 
  g_assert_cmpint(unlink(question_filename1), ==, 0);
 
6643
 
  g_assert_cmpint(unlink(question_filename2), ==, 0);
 
6644
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
6648
 
test_add_existing_questions_non_questions(__attribute__((unused))
 
6649
 
                                          test_fixture *fixture,
 
6650
 
                                          __attribute__((unused))
 
6651
 
                                          gconstpointer user_data){
 
6652
 
  __attribute__((cleanup(cleanup_queue)))
 
6653
 
    task_queue *queue = create_queue();
 
6654
 
  g_assert_nonnull(queue);
 
6655
 
  __attribute__((cleanup(cleanup_close)))
 
6656
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6657
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6658
 
  __attribute__((cleanup(string_set_clear)))
 
6659
 
    string_set cancelled_filenames = {};
 
6660
 
  __attribute__((cleanup(cleanup_string)))
 
6661
 
    char *tempdir = make_temporary_directory();
 
6662
 
  g_assert_nonnull(tempdir);
 
6663
 
  __attribute__((cleanup(cleanup_string)))
 
6664
 
    char *question_filename1
 
6665
 
    = make_temporary_file_in_directory(tempdir);
 
6666
 
  g_assert_nonnull(question_filename1);
 
6667
 
  __attribute__((cleanup(cleanup_string)))
 
6668
 
    char *question_filename2
 
6669
 
    = make_temporary_file_in_directory(tempdir);
 
6670
 
  g_assert_nonnull(question_filename2);
 
6672
 
  g_assert_false(assert_add_existing_questions_to_devnull
 
6675
 
                  (buffer[]){{}}, /* password */
 
6676
 
                  &cancelled_filenames,
 
6677
 
                  (mono_microsecs[]){0}, /* current_time */
 
6678
 
                  (bool[]){false},       /* mandos_client_exited */
 
6679
 
                  (bool[]){false},       /* password_is_read */
 
6682
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
6684
 
  g_assert_cmpint(unlink(question_filename1), ==, 0);
 
6685
 
  g_assert_cmpint(unlink(question_filename2), ==, 0);
 
6686
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
6690
 
test_add_existing_questions_both_types(__attribute__((unused))
 
6691
 
                                       test_fixture *fixture,
 
6692
 
                                       __attribute__((unused))
 
6693
 
                                       gconstpointer user_data){
 
6694
 
  __attribute__((cleanup(cleanup_queue)))
 
6695
 
    task_queue *queue = create_queue();
 
6696
 
  g_assert_nonnull(queue);
 
6697
 
  __attribute__((cleanup(cleanup_close)))
 
6698
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6699
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6700
 
  __attribute__((cleanup(cleanup_buffer)))
 
6701
 
    buffer password = {};
 
6702
 
  __attribute__((cleanup(string_set_clear)))
 
6703
 
    string_set cancelled_filenames = {};
 
6704
 
  const mono_microsecs current_time = 0;
 
6705
 
  bool mandos_client_exited = false;
 
6706
 
  bool password_is_read = false;
 
6707
 
  __attribute__((cleanup(cleanup_string)))
 
6708
 
    char *tempdir = make_temporary_directory();
 
6709
 
  g_assert_nonnull(tempdir);
 
6710
 
  __attribute__((cleanup(cleanup_string)))
 
6711
 
    char *tempfilename1 = make_temporary_file_in_directory(tempdir);
 
6712
 
  g_assert_nonnull(tempfilename1);
 
6713
 
  __attribute__((cleanup(cleanup_string)))
 
6714
 
    char *tempfilename2 = make_temporary_file_in_directory(tempdir);
 
6715
 
  g_assert_nonnull(tempfilename2);
 
6716
 
  __attribute__((cleanup(cleanup_string)))
 
6717
 
    char *question_filename
 
6718
 
    = make_question_file_in_directory(tempdir);
 
6719
 
  g_assert_nonnull(question_filename);
 
6721
 
  g_assert_true(assert_add_existing_questions_to_devnull
 
6725
 
                 &cancelled_filenames,
 
6727
 
                 &mandos_client_exited,
 
6731
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
6733
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
6734
 
        .func=open_and_parse_question,
 
6736
 
        .filename=question_filename,
 
6737
 
        .question_filename=question_filename,
 
6738
 
        .password=&password,
 
6739
 
        .cancelled_filenames=&cancelled_filenames,
 
6740
 
        .current_time=¤t_time,
 
6741
 
        .mandos_client_exited=&mandos_client_exited,
 
6742
 
        .password_is_read=&password_is_read,
 
6745
 
  g_assert_true(queue->next_run == 1);
 
6747
 
  g_assert_cmpint(unlink(tempfilename1), ==, 0);
 
6748
 
  g_assert_cmpint(unlink(tempfilename2), ==, 0);
 
6749
 
  g_assert_cmpint(unlink(question_filename), ==, 0);
 
6750
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
6753
 
static void test_wait_for_event_timeout(__attribute__((unused))
 
6754
 
                                        test_fixture *fixture,
 
6755
 
                                        __attribute__((unused))
 
6756
 
                                        gconstpointer user_data){
 
6757
 
  __attribute__((cleanup(cleanup_close)))
 
6758
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6759
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6761
 
  g_assert_true(wait_for_event(epoll_fd, 1, 0));
 
6764
 
static void test_wait_for_event_event(__attribute__((unused))
 
6765
 
                                      test_fixture *fixture,
 
6766
 
                                      __attribute__((unused))
 
6767
 
                                      gconstpointer user_data){
 
6768
 
  __attribute__((cleanup(cleanup_close)))
 
6769
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6770
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6772
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
6773
 
  __attribute__((cleanup(cleanup_close)))
 
6774
 
    const int read_pipe = pipefds[0];
 
6775
 
  __attribute__((cleanup(cleanup_close)))
 
6776
 
    const int write_pipe = pipefds[1];
 
6777
 
  g_assert_cmpint(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, read_pipe,
 
6778
 
                            &(struct epoll_event)
 
6779
 
                            { .events=EPOLLIN | EPOLLRDHUP }), ==, 0);
 
6780
 
  g_assert_cmpint((int)write(write_pipe, "x", 1), ==, 1);
 
6782
 
  g_assert_true(wait_for_event(epoll_fd, 0, 0));
 
6785
 
static void test_wait_for_event_sigchld(test_fixture *fixture,
 
6786
 
                                        __attribute__((unused))
 
6787
 
                                        gconstpointer user_data){
 
6788
 
  const pid_t pid = fork();
 
6789
 
  if(pid == 0){         /* Child */
 
6790
 
    if(not restore_signal_handler(&fixture->orig_sigaction)){
 
6791
 
      _exit(EXIT_FAILURE);
 
6793
 
    if(not restore_sigmask(&fixture->orig_sigmask)){
 
6794
 
      _exit(EXIT_FAILURE);
 
6798
 
  g_assert_true(pid != -1);
 
6799
 
  __attribute__((cleanup(cleanup_close)))
 
6800
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6801
 
  g_assert_cmpint(epoll_fd, >=, 0);
 
6803
 
  g_assert_true(wait_for_event(epoll_fd, 0, 0));
 
6806
 
  g_assert_true(waitpid(pid, &status, 0) == pid);
 
6807
 
  g_assert_true(WIFEXITED(status));
 
6808
 
  g_assert_cmpint(WEXITSTATUS(status), ==, EXIT_SUCCESS);
 
6811
 
static void test_run_queue_zeroes_next_run(__attribute__((unused))
 
6812
 
                                           test_fixture *fixture,
 
6813
 
                                           __attribute__((unused))
 
6814
 
                                           gconstpointer user_data){
 
6815
 
  __attribute__((cleanup(cleanup_queue)))
 
6816
 
    task_queue *queue = create_queue();
 
6817
 
  g_assert_nonnull(queue);
 
6818
 
  queue->next_run = 1;
 
6819
 
  __attribute__((cleanup(cleanup_close)))
 
6820
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
6821
 
  __attribute__((cleanup(string_set_clear)))
 
6822
 
    string_set cancelled_filenames = {};
 
6823
 
  bool quit_now = false;
 
6825
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6826
 
  g_assert_false(quit_now);
 
6827
 
  g_assert_cmpuint((unsigned int)queue->next_run, ==, 0);
 
6831
 
void test_run_queue_clears_cancelled_filenames(__attribute__((unused))
 
6832
 
                                               test_fixture *fixture,
 
6833
 
                                               __attribute__((unused))
 
6836
 
  __attribute__((cleanup(cleanup_queue)))
 
6837
 
    task_queue *queue = create_queue();
 
6838
 
  g_assert_nonnull(queue);
 
6839
 
  __attribute__((cleanup(string_set_clear)))
 
6840
 
    string_set cancelled_filenames = {};
 
6841
 
  bool quit_now = false;
 
6842
 
  const char question_filename[] = "/nonexistent/question_filename";
 
6843
 
  g_assert_true(string_set_add(&cancelled_filenames,
 
6844
 
                               question_filename));
 
6846
 
  g_assert_true(add_to_queue(queue,
 
6847
 
                             (task_context){ .func=dummy_func }));
 
6849
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6850
 
  g_assert_false(quit_now);
 
6851
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
6852
 
  g_assert_false(string_set_contains(cancelled_filenames,
 
6853
 
                                     question_filename));
 
6857
 
void test_run_queue_skips_cancelled_filenames(__attribute__((unused))
 
6858
 
                                              test_fixture *fixture,
 
6859
 
                                              __attribute__((unused))
 
6862
 
  __attribute__((cleanup(cleanup_queue)))
 
6863
 
    task_queue *queue = create_queue();
 
6864
 
  g_assert_nonnull(queue);
 
6865
 
  __attribute__((cleanup(string_set_clear)))
 
6866
 
    string_set cancelled_filenames = {};
 
6867
 
  bool quit_now = false;
 
6869
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
6870
 
  __attribute__((cleanup(cleanup_close)))
 
6871
 
    const int read_pipe = pipefds[0];
 
6872
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
6873
 
  const char question_filename[] = "/nonexistent/question_filename";
 
6874
 
  g_assert_true(string_set_add(&cancelled_filenames,
 
6875
 
                               question_filename));
 
6876
 
  __attribute__((nonnull))
 
6877
 
    void quit_func(const task_context task,
 
6878
 
                   __attribute__((unused)) task_queue *const q){
 
6879
 
    g_assert_nonnull(task.quit_now);
 
6880
 
    *task.quit_now = true;
 
6882
 
  task_context task = {
 
6884
 
    .question_filename=strdup(question_filename),
 
6885
 
    .quit_now=&quit_now,
 
6888
 
  g_assert_nonnull(task.question_filename);
 
6890
 
  g_assert_true(add_to_queue(queue, task));
 
6892
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6893
 
  g_assert_false(quit_now);
 
6895
 
  /* read_pipe should be closed already */
 
6897
 
  bool read_pipe_closed = (close(read_pipe) == -1);
 
6898
 
  read_pipe_closed &= (errno == EBADF);
 
6899
 
  g_assert_true(read_pipe_closed);
 
6902
 
static void test_run_queue_one_task(__attribute__((unused))
 
6903
 
                                    test_fixture *fixture,
 
6904
 
                                    __attribute__((unused))
 
6905
 
                                    gconstpointer user_data){
 
6906
 
  __attribute__((cleanup(cleanup_queue)))
 
6907
 
    task_queue *queue = create_queue();
 
6908
 
  g_assert_nonnull(queue);
 
6909
 
  __attribute__((cleanup(string_set_clear)))
 
6910
 
    string_set cancelled_filenames = {};
 
6911
 
  bool quit_now = false;
 
6913
 
  __attribute__((nonnull))
 
6914
 
    void next_run_func(__attribute__((unused))
 
6915
 
                       const task_context task,
 
6916
 
                       task_queue *const q){
 
6920
 
  task_context task = {
 
6921
 
    .func=next_run_func,
 
6923
 
  g_assert_true(add_to_queue(queue, task));
 
6925
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6926
 
  g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
 
6927
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
6930
 
static void test_run_queue_two_tasks(__attribute__((unused))
 
6931
 
                                     test_fixture *fixture,
 
6932
 
                                     __attribute__((unused))
 
6933
 
                                     gconstpointer user_data){
 
6934
 
  __attribute__((cleanup(cleanup_queue)))
 
6935
 
    task_queue *queue = create_queue();
 
6936
 
  g_assert_nonnull(queue);
 
6937
 
  queue->next_run = 1;
 
6938
 
  __attribute__((cleanup(string_set_clear)))
 
6939
 
    string_set cancelled_filenames = {};
 
6940
 
  bool quit_now = false;
 
6941
 
  bool mandos_client_exited = false;
 
6943
 
  __attribute__((nonnull))
 
6944
 
    void next_run_func(__attribute__((unused))
 
6945
 
                       const task_context task,
 
6946
 
                       task_queue *const q){
 
6950
 
  __attribute__((nonnull))
 
6951
 
    void exited_func(const task_context task,
 
6952
 
                     __attribute__((unused)) task_queue *const q){
 
6953
 
    *task.mandos_client_exited = true;
 
6956
 
  task_context task1 = {
 
6957
 
    .func=next_run_func,
 
6959
 
  g_assert_true(add_to_queue(queue, task1));
 
6961
 
  task_context task2 = {
 
6963
 
    .mandos_client_exited=&mandos_client_exited,
 
6965
 
  g_assert_true(add_to_queue(queue, task2));
 
6967
 
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
6968
 
  g_assert_false(quit_now);
 
6969
 
  g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
 
6970
 
  g_assert_true(mandos_client_exited);
 
6971
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
6974
 
static void test_run_queue_two_tasks_quit(__attribute__((unused))
 
6975
 
                                          test_fixture *fixture,
 
6976
 
                                          __attribute__((unused))
 
6977
 
                                          gconstpointer user_data){
 
6978
 
  __attribute__((cleanup(cleanup_queue)))
 
6979
 
    task_queue *queue = create_queue();
 
6980
 
  g_assert_nonnull(queue);
 
6981
 
  __attribute__((cleanup(string_set_clear)))
 
6982
 
    string_set cancelled_filenames = {};
 
6983
 
  bool quit_now = false;
 
6984
 
  bool mandos_client_exited = false;
 
6985
 
  bool password_is_read = false;
 
6987
 
  __attribute__((nonnull))
 
6988
 
    void set_exited_func(const task_context task,
 
6989
 
                         __attribute__((unused)) task_queue *const q){
 
6990
 
    *task.mandos_client_exited = true;
 
6991
 
    *task.quit_now = true;
 
6993
 
  task_context task1 = {
 
6994
 
    .func=set_exited_func,
 
6995
 
    .quit_now=&quit_now,
 
6996
 
    .mandos_client_exited=&mandos_client_exited,
 
6998
 
  g_assert_true(add_to_queue(queue, task1));
 
7000
 
  __attribute__((nonnull))
 
7001
 
    void set_read_func(const task_context task,
 
7002
 
                       __attribute__((unused)) task_queue *const q){
 
7003
 
    *task.quit_now = true;
 
7004
 
    *task.password_is_read = true;
 
7006
 
  task_context task2 = {
 
7007
 
    .func=set_read_func,
 
7008
 
    .quit_now=&quit_now,
 
7009
 
    .password_is_read=&password_is_read,
 
7011
 
  g_assert_true(add_to_queue(queue, task2));
 
7013
 
  g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
 
7014
 
  g_assert_true(quit_now);
 
7015
 
  g_assert_true(mandos_client_exited xor password_is_read);
 
7016
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
7019
 
static void test_run_queue_two_tasks_cleanup(__attribute__((unused))
 
7020
 
                                             test_fixture *fixture,
 
7021
 
                                             __attribute__((unused))
 
7022
 
                                             gconstpointer user_data){
 
7023
 
  __attribute__((cleanup(cleanup_queue)))
 
7024
 
    task_queue *queue = create_queue();
 
7025
 
  g_assert_nonnull(queue);
 
7026
 
  __attribute__((cleanup(string_set_clear)))
 
7027
 
    string_set cancelled_filenames = {};
 
7029
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
7030
 
  __attribute__((cleanup(cleanup_close)))
 
7031
 
    const int read_pipe = pipefds[0];
 
7032
 
  __attribute__((cleanup(cleanup_close)))
 
7033
 
    const int write_pipe = pipefds[1];
 
7034
 
  bool quit_now = false;
 
7036
 
  __attribute__((nonnull))
 
7037
 
    void read_func(const task_context task,
 
7038
 
                   __attribute__((unused)) task_queue *const q){
 
7039
 
    *task.quit_now = true;
 
7041
 
  task_context task1 = {
 
7043
 
    .quit_now=&quit_now,
 
7046
 
  g_assert_true(add_to_queue(queue, task1));
 
7048
 
  __attribute__((nonnull))
 
7049
 
    void write_func(const task_context task,
 
7050
 
                    __attribute__((unused)) task_queue *const q){
 
7051
 
    *task.quit_now = true;
 
7053
 
  task_context task2 = {
 
7055
 
    .quit_now=&quit_now,
 
7058
 
  g_assert_true(add_to_queue(queue, task2));
 
7060
 
  g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
 
7061
 
  g_assert_true(quit_now);
 
7063
 
  /* Either read_pipe or write_pipe should be closed already */
 
7065
 
  bool close_read_pipe = (close(read_pipe) == -1);
 
7066
 
  close_read_pipe &= (errno == EBADF);
 
7068
 
  bool close_write_pipe = (close(write_pipe) == -1);
 
7069
 
  close_write_pipe &= (errno == EBADF);
 
7070
 
  g_assert_true(close_read_pipe xor close_write_pipe);
 
7071
 
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
7074
 
static void test_setup_signal_handler(__attribute__((unused))
 
7075
 
                                      test_fixture *fixture,
 
7076
 
                                      __attribute__((unused))
 
7077
 
                                      gconstpointer user_data){
 
7078
 
  /* Save current SIGCHLD action, whatever it is */
 
7079
 
  struct sigaction expected_sigchld_action;
 
7080
 
  g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
 
7083
 
  /* Act; i.e. run the setup_signal_handler() function */
 
7084
 
  struct sigaction actual_old_sigchld_action;
 
7085
 
  g_assert_true(setup_signal_handler(&actual_old_sigchld_action));
 
7087
 
  /* Check that the function correctly set "actual_old_sigchld_action"
 
7088
 
     to the same values as the previously saved
 
7089
 
     "expected_sigchld_action" */
 
7090
 
  /* Check member sa_handler */
 
7091
 
  g_assert_true(actual_old_sigchld_action.sa_handler
 
7092
 
                == expected_sigchld_action.sa_handler);
 
7093
 
  /* Check member sa_mask */
 
7094
 
  for(int signum = 1; signum < NSIG; signum++){
 
7095
 
    const int expected_old_block_state
 
7096
 
      = sigismember(&expected_sigchld_action.sa_mask, signum);
 
7097
 
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
7098
 
    const int actual_old_block_state
 
7099
 
      = sigismember(&actual_old_sigchld_action.sa_mask, signum);
 
7100
 
    g_assert_cmpint(actual_old_block_state, >=, 0);
 
7101
 
    g_assert_cmpint(actual_old_block_state,
 
7102
 
                    ==, expected_old_block_state);
 
7104
 
  /* Check member sa_flags */
 
7105
 
  g_assert_true((actual_old_sigchld_action.sa_flags
 
7106
 
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
7107
 
                == (expected_sigchld_action.sa_flags
 
7108
 
                    & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
 
7110
 
  /* Retrieve the current signal handler for SIGCHLD as set by
 
7111
 
     setup_signal_handler() */
 
7112
 
  struct sigaction actual_new_sigchld_action;
 
7113
 
  g_assert_cmpint(sigaction(SIGCHLD, NULL,
 
7114
 
                            &actual_new_sigchld_action), ==, 0);
 
7115
 
  /* Check that the signal handler (member sa_handler) is correctly
 
7116
 
     set to the "handle_sigchld" function */
 
7117
 
  g_assert_true(actual_new_sigchld_action.sa_handler != SIG_DFL);
 
7118
 
  g_assert_true(actual_new_sigchld_action.sa_handler != SIG_IGN);
 
7119
 
  g_assert_true(actual_new_sigchld_action.sa_handler
 
7121
 
  /* Check (in member sa_mask) that at least a handful of signals are
 
7122
 
     actually blocked during the signal handler */
 
7123
 
  for(int signum = 1; signum < NSIG; signum++){
 
7124
 
    int actual_new_block_state;
 
7130
 
      actual_new_block_state
 
7131
 
        = sigismember(&actual_new_sigchld_action.sa_mask, signum);
 
7132
 
      g_assert_cmpint(actual_new_block_state, ==, 1);
 
7134
 
    case SIGKILL:               /* non-blockable */
 
7135
 
    case SIGSTOP:               /* non-blockable */
 
7136
 
    case SIGCHLD:               /* always blocked */
 
7141
 
  /* Check member sa_flags */
 
7142
 
  g_assert_true((actual_new_sigchld_action.sa_flags
 
7143
 
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
7144
 
                == (SA_NOCLDSTOP | SA_RESTART));
 
7146
 
  /* Restore signal handler */
 
7147
 
  g_assert_cmpint(sigaction(SIGCHLD, &expected_sigchld_action, NULL),
 
7151
 
static void test_restore_signal_handler(__attribute__((unused))
 
7152
 
                                        test_fixture *fixture,
 
7153
 
                                        __attribute__((unused))
 
7154
 
                                        gconstpointer user_data){
 
7155
 
  /* Save current SIGCHLD action, whatever it is */
 
7156
 
  struct sigaction expected_sigchld_action;
 
7157
 
  g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
 
7159
 
  /* Since we haven't established a signal handler yet, there should
 
7160
 
     not be one established.  But another test may have relied on
 
7161
 
     restore_signal_handler() to restore the signal handler, and if
 
7162
 
     restore_signal_handler() is buggy (which we should be prepared
 
7163
 
     for in this test) the signal handler may not have been restored
 
7164
 
     properly; check for this: */
 
7165
 
  g_assert_true(expected_sigchld_action.sa_handler != handle_sigchld);
 
7167
 
  /* Establish a signal handler */
 
7168
 
  struct sigaction sigchld_action = {
 
7169
 
    .sa_handler=handle_sigchld,
 
7170
 
    .sa_flags=SA_RESTART | SA_NOCLDSTOP,
 
7172
 
  g_assert_cmpint(sigfillset(&sigchld_action.sa_mask), ==, 0);
 
7173
 
  g_assert_cmpint(sigaction(SIGCHLD, &sigchld_action, NULL), ==, 0);
 
7175
 
  /* Act; i.e. run the restore_signal_handler() function */
 
7176
 
  g_assert_true(restore_signal_handler(&expected_sigchld_action));
 
7178
 
  /* Retrieve the restored signal handler data */
 
7179
 
  struct sigaction actual_restored_sigchld_action;
 
7180
 
  g_assert_cmpint(sigaction(SIGCHLD, NULL,
 
7181
 
                            &actual_restored_sigchld_action), ==, 0);
 
7183
 
  /* Check that the function correctly restored the signal action, as
 
7184
 
     saved in "actual_restored_sigchld_action", to the same values as
 
7185
 
     the previously saved "expected_sigchld_action" */
 
7186
 
  /* Check member sa_handler */
 
7187
 
  g_assert_true(actual_restored_sigchld_action.sa_handler
 
7188
 
                == expected_sigchld_action.sa_handler);
 
7189
 
  /* Check member sa_mask */
 
7190
 
  for(int signum = 1; signum < NSIG; signum++){
 
7191
 
    const int expected_old_block_state
 
7192
 
      = sigismember(&expected_sigchld_action.sa_mask, signum);
 
7193
 
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
7194
 
    const int actual_restored_block_state
 
7195
 
      = sigismember(&actual_restored_sigchld_action.sa_mask, signum);
 
7196
 
    g_assert_cmpint(actual_restored_block_state, >=, 0);
 
7197
 
    g_assert_cmpint(actual_restored_block_state,
 
7198
 
                    ==, expected_old_block_state);
 
7200
 
  /* Check member sa_flags */
 
7201
 
  g_assert_true((actual_restored_sigchld_action.sa_flags
 
7202
 
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
7203
 
                == (expected_sigchld_action.sa_flags
 
7204
 
                    & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
 
7207
 
static void test_block_sigchld(__attribute__((unused))
 
7208
 
                               test_fixture *fixture,
 
7209
 
                               __attribute__((unused))
 
7210
 
                               gconstpointer user_data){
 
7211
 
  /* Save original signal mask */
 
7212
 
  sigset_t expected_sigmask;
 
7213
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &expected_sigmask),
 
7216
 
  /* Make sure SIGCHLD is unblocked for this test */
 
7217
 
  sigset_t sigchld_sigmask;
 
7218
 
  g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
 
7219
 
  g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
 
7220
 
  g_assert_cmpint(pthread_sigmask(SIG_UNBLOCK, &sigchld_sigmask,
 
7223
 
  /* Act; i.e. run the block_sigchld() function */
 
7224
 
  sigset_t actual_old_sigmask;
 
7225
 
  g_assert_true(block_sigchld(&actual_old_sigmask));
 
7227
 
  /* Check the actual_old_sigmask; it should be the same as the
 
7228
 
     previously saved signal mask "expected_sigmask". */
 
7229
 
  for(int signum = 1; signum < NSIG; signum++){
 
7230
 
    const int expected_old_block_state
 
7231
 
      = sigismember(&expected_sigmask, signum);
 
7232
 
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
7233
 
    const int actual_old_block_state
 
7234
 
      = sigismember(&actual_old_sigmask, signum);
 
7235
 
    g_assert_cmpint(actual_old_block_state, >=, 0);
 
7236
 
    g_assert_cmpint(actual_old_block_state,
 
7237
 
                    ==, expected_old_block_state);
 
7240
 
  /* Retrieve the newly set signal mask */
 
7241
 
  sigset_t actual_sigmask;
 
7242
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &actual_sigmask), ==, 0);
 
7244
 
  /* SIGCHLD should be blocked */
 
7245
 
  g_assert_cmpint(sigismember(&actual_sigmask, SIGCHLD), ==, 1);
 
7247
 
  /* Restore signal mask */
 
7248
 
  g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &expected_sigmask,
 
7252
 
static void test_restore_sigmask(__attribute__((unused))
 
7253
 
                                 test_fixture *fixture,
 
7254
 
                                 __attribute__((unused))
 
7255
 
                                 gconstpointer user_data){
 
7256
 
  /* Save original signal mask */
 
7257
 
  sigset_t orig_sigmask;
 
7258
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &orig_sigmask), ==, 0);
 
7260
 
  /* Make sure SIGCHLD is blocked for this test */
 
7261
 
  sigset_t sigchld_sigmask;
 
7262
 
  g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
 
7263
 
  g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
 
7264
 
  g_assert_cmpint(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask,
 
7267
 
  /* Act; i.e. run the restore_sigmask() function */
 
7268
 
  g_assert_true(restore_sigmask(&orig_sigmask));
 
7270
 
  /* Retrieve the newly restored signal mask */
 
7271
 
  sigset_t restored_sigmask;
 
7272
 
  g_assert_cmpint(pthread_sigmask(-1, NULL, &restored_sigmask),
 
7275
 
  /* Check the restored_sigmask; it should be the same as the
 
7276
 
     previously saved signal mask "orig_sigmask". */
 
7277
 
  for(int signum = 1; signum < NSIG; signum++){
 
7278
 
    const int orig_block_state = sigismember(&orig_sigmask, signum);
 
7279
 
    g_assert_cmpint(orig_block_state, >=, 0);
 
7280
 
    const int restored_block_state = sigismember(&restored_sigmask,
 
7282
 
    g_assert_cmpint(restored_block_state, >=, 0);
 
7283
 
    g_assert_cmpint(restored_block_state, ==, orig_block_state);
 
7286
 
  /* Restore signal mask */
 
7287
 
  g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &orig_sigmask,
 
7291
 
static void test_parse_arguments_noargs(__attribute__((unused))
 
7292
 
                                        test_fixture *fixture,
 
7293
 
                                        __attribute__((unused))
 
7294
 
                                        gconstpointer user_data){
 
7298
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7300
 
  char *agent_directory = NULL;
 
7301
 
  char *helper_directory = NULL;
 
7304
 
  char *mandos_argz = NULL;
 
7305
 
  size_t mandos_argz_length = 0;
 
7307
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7308
 
                                &helper_directory, &user, &group,
 
7309
 
                                &mandos_argz, &mandos_argz_length));
 
7310
 
  g_assert_null(agent_directory);
 
7311
 
  g_assert_null(helper_directory);
 
7312
 
  g_assert_true(user == 0);
 
7313
 
  g_assert_true(group == 0);
 
7314
 
  g_assert_null(mandos_argz);
 
7315
 
  g_assert_true(mandos_argz_length == 0);
 
7317
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7322
 
__attribute__((nonnull))
 
7323
 
static bool parse_arguments_devnull(int argc, char *argv[],
 
7324
 
                                    const bool exit_failure,
 
7325
 
                                    char **agent_directory,
 
7326
 
                                    char **helper_directory,
 
7330
 
                                    size_t *mandos_argz_length){
 
7332
 
  FILE *real_stderr = stderr;
 
7333
 
  FILE *devnull = fopen("/dev/null", "we");
 
7334
 
  g_assert_nonnull(devnull);
 
7337
 
  const bool ret = parse_arguments(argc, argv, exit_failure,
 
7339
 
                                   helper_directory, user, group,
 
7340
 
                                   mandos_argz, mandos_argz_length);
 
7341
 
  const error_t saved_errno = errno;
 
7343
 
  stderr = real_stderr;
 
7344
 
  g_assert_cmpint(fclose(devnull), ==, 0);
 
7346
 
  errno = saved_errno;
 
7351
 
static void test_parse_arguments_invalid(__attribute__((unused))
 
7352
 
                                         test_fixture *fixture,
 
7353
 
                                         __attribute__((unused))
 
7354
 
                                         gconstpointer user_data){
 
7357
 
    strdup("--invalid"),
 
7359
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7361
 
  char *agent_directory = NULL;
 
7362
 
  char *helper_directory = NULL;
 
7365
 
  char *mandos_argz = NULL;
 
7366
 
  size_t mandos_argz_length = 0;
 
7368
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
7370
 
                                         &helper_directory, &user,
 
7371
 
                                         &group, &mandos_argz,
 
7372
 
                                         &mandos_argz_length));
 
7374
 
  g_assert_true(errno == EINVAL);
 
7375
 
  g_assert_null(agent_directory);
 
7376
 
  g_assert_null(helper_directory);
 
7377
 
  g_assert_null(mandos_argz);
 
7378
 
  g_assert_true(mandos_argz_length == 0);
 
7380
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7385
 
static void test_parse_arguments_long_dir(__attribute__((unused))
 
7386
 
                                          test_fixture *fixture,
 
7387
 
                                          __attribute__((unused))
 
7388
 
                                          gconstpointer user_data){
 
7391
 
    strdup("--agent-directory"),
 
7394
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7396
 
  __attribute__((cleanup(cleanup_string)))
 
7397
 
    char *agent_directory = NULL;
 
7398
 
  char *helper_directory = NULL;
 
7401
 
  __attribute__((cleanup(cleanup_string)))
 
7402
 
    char *mandos_argz = NULL;
 
7403
 
  size_t mandos_argz_length = 0;
 
7405
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7406
 
                                &helper_directory, &user, &group,
 
7407
 
                                &mandos_argz, &mandos_argz_length));
 
7409
 
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
7410
 
  g_assert_null(helper_directory);
 
7411
 
  g_assert_true(user == 0);
 
7412
 
  g_assert_true(group == 0);
 
7413
 
  g_assert_null(mandos_argz);
 
7414
 
  g_assert_true(mandos_argz_length == 0);
 
7416
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7421
 
static void test_parse_arguments_short_dir(__attribute__((unused))
 
7422
 
                                           test_fixture *fixture,
 
7423
 
                                           __attribute__((unused))
 
7424
 
                                           gconstpointer user_data){
 
7430
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7432
 
  __attribute__((cleanup(cleanup_string)))
 
7433
 
    char *agent_directory = NULL;
 
7434
 
  char *helper_directory = NULL;
 
7437
 
  __attribute__((cleanup(cleanup_string)))
 
7438
 
    char *mandos_argz = NULL;
 
7439
 
  size_t mandos_argz_length = 0;
 
7441
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7442
 
                                &helper_directory, &user, &group,
 
7443
 
                                &mandos_argz, &mandos_argz_length));
 
7445
 
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
7446
 
  g_assert_null(helper_directory);
 
7447
 
  g_assert_true(user == 0);
 
7448
 
  g_assert_true(group == 0);
 
7449
 
  g_assert_null(mandos_argz);
 
7450
 
  g_assert_true(mandos_argz_length == 0);
 
7452
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7458
 
void test_parse_arguments_helper_directory(__attribute__((unused))
 
7459
 
                                           test_fixture *fixture,
 
7460
 
                                           __attribute__((unused))
 
7461
 
                                           gconstpointer user_data){
 
7464
 
    strdup("--helper-directory"),
 
7467
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7469
 
  char *agent_directory = NULL;
 
7470
 
  __attribute__((cleanup(cleanup_string)))
 
7471
 
    char *helper_directory = NULL;
 
7474
 
  __attribute__((cleanup(cleanup_string)))
 
7475
 
    char *mandos_argz = NULL;
 
7476
 
  size_t mandos_argz_length = 0;
 
7478
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7479
 
                                &helper_directory, &user, &group,
 
7480
 
                                &mandos_argz, &mandos_argz_length));
 
7482
 
  g_assert_cmpstr(helper_directory, ==, "/tmp");
 
7483
 
  g_assert_null(agent_directory);
 
7484
 
  g_assert_true(user == 0);
 
7485
 
  g_assert_true(group == 0);
 
7486
 
  g_assert_null(mandos_argz);
 
7487
 
  g_assert_true(mandos_argz_length == 0);
 
7489
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7495
 
void test_parse_arguments_plugin_helper_dir(__attribute__((unused))
 
7496
 
                                            test_fixture *fixture,
 
7497
 
                                            __attribute__((unused))
 
7498
 
                                            gconstpointer user_data){
 
7501
 
    strdup("--plugin-helper-dir"),
 
7504
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7506
 
  char *agent_directory = NULL;
 
7507
 
  __attribute__((cleanup(cleanup_string)))
 
7508
 
    char *helper_directory = NULL;
 
7511
 
  __attribute__((cleanup(cleanup_string)))
 
7512
 
    char *mandos_argz = NULL;
 
7513
 
  size_t mandos_argz_length = 0;
 
7515
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7516
 
                                &helper_directory, &user, &group,
 
7517
 
                                &mandos_argz, &mandos_argz_length));
 
7519
 
  g_assert_cmpstr(helper_directory, ==, "/tmp");
 
7520
 
  g_assert_null(agent_directory);
 
7521
 
  g_assert_true(user == 0);
 
7522
 
  g_assert_true(group == 0);
 
7523
 
  g_assert_null(mandos_argz);
 
7524
 
  g_assert_true(mandos_argz_length == 0);
 
7526
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7531
 
static void test_parse_arguments_user(__attribute__((unused))
 
7532
 
                                      test_fixture *fixture,
 
7533
 
                                      __attribute__((unused))
 
7534
 
                                      gconstpointer user_data){
 
7540
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7542
 
  char *agent_directory = NULL;
 
7543
 
  __attribute__((cleanup(cleanup_string)))
 
7544
 
    char *helper_directory = NULL;
 
7547
 
  __attribute__((cleanup(cleanup_string)))
 
7548
 
    char *mandos_argz = NULL;
 
7549
 
  size_t mandos_argz_length = 0;
 
7551
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7552
 
                                &helper_directory, &user, &group,
 
7553
 
                                &mandos_argz, &mandos_argz_length));
 
7555
 
  g_assert_null(helper_directory);
 
7556
 
  g_assert_null(agent_directory);
 
7557
 
  g_assert_cmpuint((unsigned int)user, ==, 1000);
 
7558
 
  g_assert_true(group == 0);
 
7559
 
  g_assert_null(mandos_argz);
 
7560
 
  g_assert_true(mandos_argz_length == 0);
 
7562
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7567
 
static void test_parse_arguments_user_invalid(__attribute__((unused))
 
7568
 
                                              test_fixture *fixture,
 
7569
 
                                              __attribute__((unused))
 
7577
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7579
 
  char *agent_directory = NULL;
 
7580
 
  __attribute__((cleanup(cleanup_string)))
 
7581
 
    char *helper_directory = NULL;
 
7584
 
  __attribute__((cleanup(cleanup_string)))
 
7585
 
    char *mandos_argz = NULL;
 
7586
 
  size_t mandos_argz_length = 0;
 
7588
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
7590
 
                                         &helper_directory, &user,
 
7591
 
                                         &group, &mandos_argz,
 
7592
 
                                         &mandos_argz_length));
 
7594
 
  g_assert_null(helper_directory);
 
7595
 
  g_assert_null(agent_directory);
 
7596
 
  g_assert_cmpuint((unsigned int)user, ==, 0);
 
7597
 
  g_assert_true(group == 0);
 
7598
 
  g_assert_null(mandos_argz);
 
7599
 
  g_assert_true(mandos_argz_length == 0);
 
7601
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7607
 
void test_parse_arguments_user_zero_invalid(__attribute__((unused))
 
7608
 
                                            test_fixture *fixture,
 
7609
 
                                            __attribute__((unused))
 
7610
 
                                            gconstpointer user_data){
 
7616
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7618
 
  char *agent_directory = NULL;
 
7619
 
  __attribute__((cleanup(cleanup_string)))
 
7620
 
    char *helper_directory = NULL;
 
7623
 
  __attribute__((cleanup(cleanup_string)))
 
7624
 
    char *mandos_argz = NULL;
 
7625
 
  size_t mandos_argz_length = 0;
 
7627
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
7629
 
                                         &helper_directory, &user,
 
7630
 
                                         &group, &mandos_argz,
 
7631
 
                                         &mandos_argz_length));
 
7633
 
  g_assert_null(helper_directory);
 
7634
 
  g_assert_null(agent_directory);
 
7635
 
  g_assert_cmpuint((unsigned int)user, ==, 0);
 
7636
 
  g_assert_true(group == 0);
 
7637
 
  g_assert_null(mandos_argz);
 
7638
 
  g_assert_true(mandos_argz_length == 0);
 
7640
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7645
 
static void test_parse_arguments_group(__attribute__((unused))
 
7646
 
                                       test_fixture *fixture,
 
7647
 
                                       __attribute__((unused))
 
7648
 
                                       gconstpointer user_data){
 
7654
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7656
 
  char *agent_directory = NULL;
 
7657
 
  __attribute__((cleanup(cleanup_string)))
 
7658
 
    char *helper_directory = NULL;
 
7661
 
  __attribute__((cleanup(cleanup_string)))
 
7662
 
    char *mandos_argz = NULL;
 
7663
 
  size_t mandos_argz_length = 0;
 
7665
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7666
 
                                &helper_directory, &user, &group,
 
7667
 
                                &mandos_argz, &mandos_argz_length));
 
7669
 
  g_assert_null(helper_directory);
 
7670
 
  g_assert_null(agent_directory);
 
7671
 
  g_assert_true(user == 0);
 
7672
 
  g_assert_cmpuint((unsigned int)group, ==, 1000);
 
7673
 
  g_assert_null(mandos_argz);
 
7674
 
  g_assert_true(mandos_argz_length == 0);
 
7676
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7681
 
static void test_parse_arguments_group_invalid(__attribute__((unused))
 
7682
 
                                               test_fixture *fixture,
 
7683
 
                                               __attribute__((unused))
 
7691
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7693
 
  char *agent_directory = NULL;
 
7694
 
  __attribute__((cleanup(cleanup_string)))
 
7695
 
    char *helper_directory = NULL;
 
7698
 
  __attribute__((cleanup(cleanup_string)))
 
7699
 
    char *mandos_argz = NULL;
 
7700
 
  size_t mandos_argz_length = 0;
 
7702
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
7704
 
                                         &helper_directory, &user,
 
7705
 
                                         &group, &mandos_argz,
 
7706
 
                                         &mandos_argz_length));
 
7708
 
  g_assert_null(helper_directory);
 
7709
 
  g_assert_null(agent_directory);
 
7710
 
  g_assert_true(user == 0);
 
7711
 
  g_assert_true(group == 0);
 
7712
 
  g_assert_null(mandos_argz);
 
7713
 
  g_assert_true(mandos_argz_length == 0);
 
7715
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7721
 
void test_parse_arguments_group_zero_invalid(__attribute__((unused))
 
7722
 
                                             test_fixture *fixture,
 
7723
 
                                             __attribute__((unused))
 
7724
 
                                             gconstpointer user_data){
 
7730
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7732
 
  char *agent_directory = NULL;
 
7733
 
  __attribute__((cleanup(cleanup_string)))
 
7734
 
    char *helper_directory = NULL;
 
7737
 
  __attribute__((cleanup(cleanup_string)))
 
7738
 
    char *mandos_argz = NULL;
 
7739
 
  size_t mandos_argz_length = 0;
 
7741
 
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
7743
 
                                         &helper_directory, &user,
 
7744
 
                                         &group, &mandos_argz,
 
7745
 
                                         &mandos_argz_length));
 
7747
 
  g_assert_null(helper_directory);
 
7748
 
  g_assert_null(agent_directory);
 
7749
 
  g_assert_cmpuint((unsigned int)group, ==, 0);
 
7750
 
  g_assert_true(group == 0);
 
7751
 
  g_assert_null(mandos_argz);
 
7752
 
  g_assert_true(mandos_argz_length == 0);
 
7754
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7759
 
static void test_parse_arguments_mandos_noargs(__attribute__((unused))
 
7760
 
                                               test_fixture *fixture,
 
7761
 
                                               __attribute__((unused))
 
7766
 
    strdup("mandos-client"),
 
7768
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7770
 
  __attribute__((cleanup(cleanup_string)))
 
7771
 
    char *agent_directory = NULL;
 
7772
 
  __attribute__((cleanup(cleanup_string)))
 
7773
 
    char *helper_directory = NULL;
 
7776
 
  __attribute__((cleanup(cleanup_string)))
 
7777
 
    char *mandos_argz = NULL;
 
7778
 
  size_t mandos_argz_length = 0;
 
7780
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7781
 
                                &helper_directory, &user, &group,
 
7782
 
                                &mandos_argz, &mandos_argz_length));
 
7784
 
  g_assert_null(agent_directory);
 
7785
 
  g_assert_null(helper_directory);
 
7786
 
  g_assert_true(user == 0);
 
7787
 
  g_assert_true(group == 0);
 
7788
 
  g_assert_cmpstr(mandos_argz, ==, "mandos-client");
 
7789
 
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
7790
 
                                            mandos_argz_length),
 
7793
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7798
 
static void test_parse_arguments_mandos_args(__attribute__((unused))
 
7799
 
                                             test_fixture *fixture,
 
7800
 
                                             __attribute__((unused))
 
7801
 
                                             gconstpointer user_data){
 
7804
 
    strdup("mandos-client"),
 
7809
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7811
 
  __attribute__((cleanup(cleanup_string)))
 
7812
 
    char *agent_directory = NULL;
 
7813
 
  __attribute__((cleanup(cleanup_string)))
 
7814
 
    char *helper_directory = NULL;
 
7817
 
  __attribute__((cleanup(cleanup_string)))
 
7818
 
    char *mandos_argz = NULL;
 
7819
 
  size_t mandos_argz_length = 0;
 
7821
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7822
 
                                &helper_directory, &user, &group,
 
7823
 
                                &mandos_argz, &mandos_argz_length));
 
7825
 
  g_assert_null(agent_directory);
 
7826
 
  g_assert_null(helper_directory);
 
7827
 
  g_assert_true(user == 0);
 
7828
 
  g_assert_true(group == 0);
 
7829
 
  char *marg = mandos_argz;
 
7830
 
  g_assert_cmpstr(marg, ==, "mandos-client");
 
7831
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7832
 
  g_assert_cmpstr(marg, ==, "one");
 
7833
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7834
 
  g_assert_cmpstr(marg, ==, "two");
 
7835
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7836
 
  g_assert_cmpstr(marg, ==, "three");
 
7837
 
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
7838
 
                                            mandos_argz_length),
 
7841
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7846
 
static void test_parse_arguments_all_args(__attribute__((unused))
 
7847
 
                                          test_fixture *fixture,
 
7848
 
                                          __attribute__((unused))
 
7849
 
                                          gconstpointer user_data){
 
7852
 
    strdup("--agent-directory"),
 
7854
 
    strdup("--helper-directory"),
 
7860
 
    strdup("mandos-client"),
 
7865
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7867
 
  __attribute__((cleanup(cleanup_string)))
 
7868
 
    char *agent_directory = NULL;
 
7869
 
  __attribute__((cleanup(cleanup_string)))
 
7870
 
    char *helper_directory = NULL;
 
7873
 
  __attribute__((cleanup(cleanup_string)))
 
7874
 
    char *mandos_argz = NULL;
 
7875
 
  size_t mandos_argz_length = 0;
 
7877
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7878
 
                                &helper_directory, &user, &group,
 
7879
 
                                &mandos_argz, &mandos_argz_length));
 
7881
 
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
7882
 
  g_assert_cmpstr(helper_directory, ==, "/var/tmp");
 
7883
 
  g_assert_true(user == 1);
 
7884
 
  g_assert_true(group == 2);
 
7885
 
  char *marg = mandos_argz;
 
7886
 
  g_assert_cmpstr(marg, ==, "mandos-client");
 
7887
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7888
 
  g_assert_cmpstr(marg, ==, "one");
 
7889
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7890
 
  g_assert_cmpstr(marg, ==, "two");
 
7891
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7892
 
  g_assert_cmpstr(marg, ==, "three");
 
7893
 
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
7894
 
                                            mandos_argz_length),
 
7897
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7902
 
static void test_parse_arguments_mixed(__attribute__((unused))
 
7903
 
                                       test_fixture *fixture,
 
7904
 
                                       __attribute__((unused))
 
7905
 
                                       gconstpointer user_data){
 
7908
 
    strdup("mandos-client"),
 
7912
 
    strdup("--agent-directory"),
 
7916
 
    strdup("--helper-directory=/var/tmp"),
 
7918
 
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
7920
 
  __attribute__((cleanup(cleanup_string)))
 
7921
 
    char *agent_directory = NULL;
 
7922
 
  __attribute__((cleanup(cleanup_string)))
 
7923
 
    char *helper_directory = NULL;
 
7926
 
  __attribute__((cleanup(cleanup_string)))
 
7927
 
    char *mandos_argz = NULL;
 
7928
 
  size_t mandos_argz_length = 0;
 
7930
 
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
7931
 
                                &helper_directory, &user, &group,
 
7932
 
                                &mandos_argz, &mandos_argz_length));
 
7934
 
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
7935
 
  g_assert_cmpstr(helper_directory, ==, "/var/tmp");
 
7936
 
  g_assert_true(user == 1);
 
7937
 
  g_assert_true(group == 0);
 
7938
 
  char *marg = mandos_argz;
 
7939
 
  g_assert_cmpstr(marg, ==, "mandos-client");
 
7940
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7941
 
  g_assert_cmpstr(marg, ==, "one");
 
7942
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7943
 
  g_assert_cmpstr(marg, ==, "two");
 
7944
 
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
7945
 
  g_assert_cmpstr(marg, ==, "three");
 
7946
 
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
7947
 
                                            mandos_argz_length),
 
7950
 
  for(char **arg = argv; *arg != NULL; arg++){
 
7955
 
/* End of tests section */
 
7957
 
/* Test boilerplate section; New tests should be added to the test
 
7958
 
   suite definition here, in the "run_tests" function.
 
7960
 
   Finally, this section also contains the should_only_run_tests()
 
7961
 
   function used by main() for deciding if tests should be run or to
 
7964
 
__attribute__((cold))
 
7965
 
static bool run_tests(int argc, char *argv[]){
 
7966
 
  g_test_init(&argc, &argv, NULL);
 
7968
 
  /* A macro to add a test with no setup or teardown functions */
 
7969
 
#define test_add(testpath, testfunc)                    \
 
7971
 
    g_test_add((testpath), test_fixture, NULL, NULL,    \
 
7972
 
               (testfunc), NULL);                       \
 
7975
 
  /* Test the signal-related functions first, since some other tests
 
7976
 
     depend on these functions in their setups and teardowns */
 
7977
 
  test_add("/signal-handling/setup", test_setup_signal_handler);
 
7978
 
  test_add("/signal-handling/restore", test_restore_signal_handler);
 
7979
 
  test_add("/signal-handling/block", test_block_sigchld);
 
7980
 
  test_add("/signal-handling/restore-sigmask", test_restore_sigmask);
 
7982
 
  /* Regular non-signal-related tests; these use no setups or
 
7984
 
  test_add("/parse_arguments/noargs", test_parse_arguments_noargs);
 
7985
 
  test_add("/parse_arguments/invalid", test_parse_arguments_invalid);
 
7986
 
  test_add("/parse_arguments/long-dir",
 
7987
 
           test_parse_arguments_long_dir);
 
7988
 
  test_add("/parse_arguments/short-dir",
 
7989
 
           test_parse_arguments_short_dir);
 
7990
 
  test_add("/parse_arguments/helper-directory",
 
7991
 
           test_parse_arguments_helper_directory);
 
7992
 
  test_add("/parse_arguments/plugin-helper-dir",
 
7993
 
           test_parse_arguments_plugin_helper_dir);
 
7994
 
  test_add("/parse_arguments/user", test_parse_arguments_user);
 
7995
 
  test_add("/parse_arguments/user-invalid",
 
7996
 
           test_parse_arguments_user_invalid);
 
7997
 
  test_add("/parse_arguments/user-zero-invalid",
 
7998
 
           test_parse_arguments_user_zero_invalid);
 
7999
 
  test_add("/parse_arguments/group", test_parse_arguments_group);
 
8000
 
  test_add("/parse_arguments/group-invalid",
 
8001
 
           test_parse_arguments_group_invalid);
 
8002
 
  test_add("/parse_arguments/group-zero-invalid",
 
8003
 
           test_parse_arguments_group_zero_invalid);
 
8004
 
  test_add("/parse_arguments/mandos-noargs",
 
8005
 
           test_parse_arguments_mandos_noargs);
 
8006
 
  test_add("/parse_arguments/mandos-args",
 
8007
 
           test_parse_arguments_mandos_args);
 
8008
 
  test_add("/parse_arguments/all-args",
 
8009
 
           test_parse_arguments_all_args);
 
8010
 
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
 
8011
 
  test_add("/queue/create", test_create_queue);
 
8012
 
  test_add("/queue/add", test_add_to_queue);
 
8013
 
  test_add("/queue/add/overflow", test_add_to_queue_overflow);
 
8014
 
  test_add("/queue/has_question/empty",
 
8015
 
           test_queue_has_question_empty);
 
8016
 
  test_add("/queue/has_question/false",
 
8017
 
           test_queue_has_question_false);
 
8018
 
  test_add("/queue/has_question/true", test_queue_has_question_true);
 
8019
 
  test_add("/queue/has_question/false2",
 
8020
 
           test_queue_has_question_false2);
 
8021
 
  test_add("/queue/has_question/true2",
 
8022
 
           test_queue_has_question_true2);
 
8023
 
  test_add("/buffer/cleanup", test_cleanup_buffer);
 
8024
 
  test_add("/string_set/net-set-contains-nothing",
 
8025
 
           test_string_set_new_set_contains_nothing);
 
8026
 
  test_add("/string_set/with-added-string-contains-it",
 
8027
 
           test_string_set_with_added_string_contains_it);
 
8028
 
  test_add("/string_set/cleared-does-not-contain-string",
 
8029
 
           test_string_set_cleared_does_not_contain_str);
 
8030
 
  test_add("/string_set/swap/one-with-empty",
 
8031
 
           test_string_set_swap_one_with_empty);
 
8032
 
  test_add("/string_set/swap/empty-with-one",
 
8033
 
           test_string_set_swap_empty_with_one);
 
8034
 
  test_add("/string_set/swap/one-with-one",
 
8035
 
           test_string_set_swap_one_with_one);
 
8037
 
  /* A macro to add a test using the setup and teardown functions */
 
8038
 
#define test_add_st(path, func)                                 \
 
8040
 
    g_test_add((path), test_fixture, NULL, test_setup, (func),  \
 
8044
 
  /* Signal-related tests; these use setups and teardowns which
 
8045
 
     establish, during each test run, a signal handler for, and a
 
8046
 
     signal mask blocking, the SIGCHLD signal, just like main() */
 
8047
 
  test_add_st("/wait_for_event/timeout", test_wait_for_event_timeout);
 
8048
 
  test_add_st("/wait_for_event/event", test_wait_for_event_event);
 
8049
 
  test_add_st("/wait_for_event/sigchld", test_wait_for_event_sigchld);
 
8050
 
  test_add_st("/run_queue/zeroes-next-run",
 
8051
 
              test_run_queue_zeroes_next_run);
 
8052
 
  test_add_st("/run_queue/clears-cancelled_filenames",
 
8053
 
              test_run_queue_clears_cancelled_filenames);
 
8054
 
  test_add_st("/run_queue/skips-cancelled-filenames",
 
8055
 
              test_run_queue_skips_cancelled_filenames);
 
8056
 
  test_add_st("/run_queue/one-task", test_run_queue_one_task);
 
8057
 
  test_add_st("/run_queue/two-tasks", test_run_queue_two_tasks);
 
8058
 
  test_add_st("/run_queue/two-tasks/quit",
 
8059
 
              test_run_queue_two_tasks_quit);
 
8060
 
  test_add_st("/run_queue/two-tasks-cleanup",
 
8061
 
              test_run_queue_two_tasks_cleanup);
 
8062
 
  test_add_st("/task-creators/start_mandos_client",
 
8063
 
              test_start_mandos_client);
 
8064
 
  test_add_st("/task-creators/start_mandos_client/execv",
 
8065
 
              test_start_mandos_client_execv);
 
8066
 
  test_add_st("/task-creators/start_mandos_client/suid/euid",
 
8067
 
              test_start_mandos_client_suid_euid);
 
8068
 
  test_add_st("/task-creators/start_mandos_client/suid/egid",
 
8069
 
              test_start_mandos_client_suid_egid);
 
8070
 
  test_add_st("/task-creators/start_mandos_client/suid/ruid",
 
8071
 
              test_start_mandos_client_suid_ruid);
 
8072
 
  test_add_st("/task-creators/start_mandos_client/suid/rgid",
 
8073
 
              test_start_mandos_client_suid_rgid);
 
8074
 
  test_add_st("/task-creators/start_mandos_client/read",
 
8075
 
              test_start_mandos_client_read);
 
8076
 
  test_add_st("/task-creators/start_mandos_client/helper-directory",
 
8077
 
              test_start_mandos_client_helper_directory);
 
8078
 
  test_add_st("/task-creators/start_mandos_client/sigmask",
 
8079
 
              test_start_mandos_client_sigmask);
 
8080
 
  test_add_st("/task/wait_for_mandos_client_exit/badpid",
 
8081
 
              test_wait_for_mandos_client_exit_badpid);
 
8082
 
  test_add_st("/task/wait_for_mandos_client_exit/noexit",
 
8083
 
              test_wait_for_mandos_client_exit_noexit);
 
8084
 
  test_add_st("/task/wait_for_mandos_client_exit/success",
 
8085
 
              test_wait_for_mandos_client_exit_success);
 
8086
 
  test_add_st("/task/wait_for_mandos_client_exit/failure",
 
8087
 
              test_wait_for_mandos_client_exit_failure);
 
8088
 
  test_add_st("/task/wait_for_mandos_client_exit/killed",
 
8089
 
              test_wait_for_mandos_client_exit_killed);
 
8090
 
  test_add_st("/task/read_mandos_client_output/readerror",
 
8091
 
              test_read_mandos_client_output_readerror);
 
8092
 
  test_add_st("/task/read_mandos_client_output/nodata",
 
8093
 
              test_read_mandos_client_output_nodata);
 
8094
 
  test_add_st("/task/read_mandos_client_output/eof",
 
8095
 
              test_read_mandos_client_output_eof);
 
8096
 
  test_add_st("/task/read_mandos_client_output/once",
 
8097
 
              test_read_mandos_client_output_once);
 
8098
 
  test_add_st("/task/read_mandos_client_output/malloc",
 
8099
 
              test_read_mandos_client_output_malloc);
 
8100
 
  test_add_st("/task/read_mandos_client_output/append",
 
8101
 
              test_read_mandos_client_output_append);
 
8102
 
  test_add_st("/task-creators/add_inotify_dir_watch",
 
8103
 
              test_add_inotify_dir_watch);
 
8104
 
  test_add_st("/task-creators/add_inotify_dir_watch/fail",
 
8105
 
              test_add_inotify_dir_watch_fail);
 
8106
 
  test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory",
 
8107
 
              test_add_inotify_dir_watch_nondir);
 
8108
 
  test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
 
8109
 
              test_add_inotify_dir_watch_EAGAIN);
 
8110
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
 
8111
 
              test_add_inotify_dir_watch_IN_CLOSE_WRITE);
 
8112
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
 
8113
 
              test_add_inotify_dir_watch_IN_MOVED_TO);
 
8114
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
 
8115
 
              test_add_inotify_dir_watch_IN_MOVED_FROM);
 
8116
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
 
8117
 
              test_add_inotify_dir_watch_IN_EXCL_UNLINK);
 
8118
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
 
8119
 
              test_add_inotify_dir_watch_IN_DELETE);
 
8120
 
  test_add_st("/task/read_inotify_event/readerror",
 
8121
 
              test_read_inotify_event_readerror);
 
8122
 
  test_add_st("/task/read_inotify_event/bad-epoll",
 
8123
 
              test_read_inotify_event_bad_epoll);
 
8124
 
  test_add_st("/task/read_inotify_event/nodata",
 
8125
 
              test_read_inotify_event_nodata);
 
8126
 
  test_add_st("/task/read_inotify_event/eof",
 
8127
 
              test_read_inotify_event_eof);
 
8128
 
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE",
 
8129
 
              test_read_inotify_event_IN_CLOSE_WRITE);
 
8130
 
  test_add_st("/task/read_inotify_event/IN_MOVED_TO",
 
8131
 
              test_read_inotify_event_IN_MOVED_TO);
 
8132
 
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
 
8133
 
              test_read_inotify_event_IN_MOVED_FROM);
 
8134
 
  test_add_st("/task/read_inotify_event/IN_DELETE",
 
8135
 
              test_read_inotify_event_IN_DELETE);
 
8136
 
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
 
8137
 
              test_read_inotify_event_IN_CLOSE_WRITE_badname);
 
8138
 
  test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
 
8139
 
              test_read_inotify_event_IN_MOVED_TO_badname);
 
8140
 
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
 
8141
 
              test_read_inotify_event_IN_MOVED_FROM_badname);
 
8142
 
  test_add_st("/task/read_inotify_event/IN_DELETE/badname",
 
8143
 
              test_read_inotify_event_IN_DELETE_badname);
 
8144
 
  test_add_st("/task/open_and_parse_question/ENOENT",
 
8145
 
              test_open_and_parse_question_ENOENT);
 
8146
 
  test_add_st("/task/open_and_parse_question/EIO",
 
8147
 
              test_open_and_parse_question_EIO);
 
8148
 
  test_add_st("/task/open_and_parse_question/parse-error",
 
8149
 
              test_open_and_parse_question_parse_error);
 
8150
 
  test_add_st("/task/open_and_parse_question/nosocket",
 
8151
 
              test_open_and_parse_question_nosocket);
 
8152
 
  test_add_st("/task/open_and_parse_question/badsocket",
 
8153
 
              test_open_and_parse_question_badsocket);
 
8154
 
  test_add_st("/task/open_and_parse_question/nopid",
 
8155
 
              test_open_and_parse_question_nopid);
 
8156
 
  test_add_st("/task/open_and_parse_question/badpid",
 
8157
 
              test_open_and_parse_question_badpid);
 
8158
 
  test_add_st("/task/open_and_parse_question/noexist_pid",
 
8159
 
              test_open_and_parse_question_noexist_pid);
 
8160
 
  test_add_st("/task/open_and_parse_question/no-notafter",
 
8161
 
              test_open_and_parse_question_no_notafter);
 
8162
 
  test_add_st("/task/open_and_parse_question/bad-notafter",
 
8163
 
              test_open_and_parse_question_bad_notafter);
 
8164
 
  test_add_st("/task/open_and_parse_question/notafter-0",
 
8165
 
              test_open_and_parse_question_notafter_0);
 
8166
 
  test_add_st("/task/open_and_parse_question/notafter-1",
 
8167
 
              test_open_and_parse_question_notafter_1);
 
8168
 
  test_add_st("/task/open_and_parse_question/notafter-1-1",
 
8169
 
              test_open_and_parse_question_notafter_1_1);
 
8170
 
  test_add_st("/task/open_and_parse_question/notafter-1-2",
 
8171
 
              test_open_and_parse_question_notafter_1_2);
 
8172
 
  test_add_st("/task/open_and_parse_question/equal-notafter",
 
8173
 
              test_open_and_parse_question_equal_notafter);
 
8174
 
  test_add_st("/task/open_and_parse_question/late-notafter",
 
8175
 
              test_open_and_parse_question_late_notafter);
 
8176
 
  test_add_st("/task/cancel_old_question/0-1-2",
 
8177
 
              test_cancel_old_question_0_1_2);
 
8178
 
  test_add_st("/task/cancel_old_question/0-2-1",
 
8179
 
              test_cancel_old_question_0_2_1);
 
8180
 
  test_add_st("/task/cancel_old_question/1-2-3",
 
8181
 
              test_cancel_old_question_1_2_3);
 
8182
 
  test_add_st("/task/cancel_old_question/1-3-2",
 
8183
 
              test_cancel_old_question_1_3_2);
 
8184
 
  test_add_st("/task/cancel_old_question/2-1-3",
 
8185
 
              test_cancel_old_question_2_1_3);
 
8186
 
  test_add_st("/task/cancel_old_question/2-3-1",
 
8187
 
              test_cancel_old_question_2_3_1);
 
8188
 
  test_add_st("/task/cancel_old_question/3-1-2",
 
8189
 
              test_cancel_old_question_3_1_2);
 
8190
 
  test_add_st("/task/cancel_old_question/3-2-1",
 
8191
 
              test_cancel_old_question_3_2_1);
 
8192
 
  test_add_st("/task/connect_question_socket/name-too-long",
 
8193
 
              test_connect_question_socket_name_too_long);
 
8194
 
  test_add_st("/task/connect_question_socket/connect-fail",
 
8195
 
              test_connect_question_socket_connect_fail);
 
8196
 
  test_add_st("/task/connect_question_socket/bad-epoll",
 
8197
 
              test_connect_question_socket_bad_epoll);
 
8198
 
  test_add_st("/task/connect_question_socket/usable",
 
8199
 
              test_connect_question_socket_usable);
 
8200
 
  test_add_st("/task/send_password_to_socket/client-not-exited",
 
8201
 
              test_send_password_to_socket_client_not_exited);
 
8202
 
  test_add_st("/task/send_password_to_socket/password-not-read",
 
8203
 
              test_send_password_to_socket_password_not_read);
 
8204
 
  test_add_st("/task/send_password_to_socket/EMSGSIZE",
 
8205
 
              test_send_password_to_socket_EMSGSIZE);
 
8206
 
  test_add_st("/task/send_password_to_socket/retry",
 
8207
 
              test_send_password_to_socket_retry);
 
8208
 
  test_add_st("/task/send_password_to_socket/bad-epoll",
 
8209
 
              test_send_password_to_socket_bad_epoll);
 
8210
 
  test_add_st("/task/send_password_to_socket/null-password",
 
8211
 
              test_send_password_to_socket_null_password);
 
8212
 
  test_add_st("/task/send_password_to_socket/empty-password",
 
8213
 
              test_send_password_to_socket_empty_password);
 
8214
 
  test_add_st("/task/send_password_to_socket/empty-str-password",
 
8215
 
              test_send_password_to_socket_empty_str_pass);
 
8216
 
  test_add_st("/task/send_password_to_socket/text-password",
 
8217
 
              test_send_password_to_socket_text_password);
 
8218
 
  test_add_st("/task/send_password_to_socket/binary-password",
 
8219
 
              test_send_password_to_socket_binary_password);
 
8220
 
  test_add_st("/task/send_password_to_socket/nuls-in-password",
 
8221
 
              test_send_password_to_socket_nuls_in_password);
 
8222
 
  test_add_st("/task-creators/add_existing_questions/ENOENT",
 
8223
 
              test_add_existing_questions_ENOENT);
 
8224
 
  test_add_st("/task-creators/add_existing_questions/no-questions",
 
8225
 
              test_add_existing_questions_no_questions);
 
8226
 
  test_add_st("/task-creators/add_existing_questions/one-question",
 
8227
 
              test_add_existing_questions_one_question);
 
8228
 
  test_add_st("/task-creators/add_existing_questions/two-questions",
 
8229
 
              test_add_existing_questions_two_questions);
 
8230
 
  test_add_st("/task-creators/add_existing_questions/non-questions",
 
8231
 
              test_add_existing_questions_non_questions);
 
8232
 
  test_add_st("/task-creators/add_existing_questions/both-types",
 
8233
 
              test_add_existing_questions_both_types);
 
8235
 
  return g_test_run() == 0;
 
8238
 
static bool should_only_run_tests(int *argc_p, char **argv_p[]){
 
8239
 
  GOptionContext *context = g_option_context_new("");
 
8241
 
  g_option_context_set_help_enabled(context, FALSE);
 
8242
 
  g_option_context_set_ignore_unknown_options(context, TRUE);
 
8244
 
  gboolean should_run_tests = FALSE;
 
8245
 
  GOptionEntry entries[] = {
 
8246
 
    { "test", 0, 0, G_OPTION_ARG_NONE,
 
8247
 
      &should_run_tests, "Run tests", NULL },
 
8250
 
  g_option_context_add_main_entries(context, entries, NULL);
 
8252
 
  GError *error = NULL;
 
8254
 
  if(g_option_context_parse(context, argc_p, argv_p, &error) != TRUE){
 
8255
 
    g_option_context_free(context);
 
8256
 
    g_error("Failed to parse options: %s", error->message);
 
8259
 
  g_option_context_free(context);
 
8260
 
  return should_run_tests != FALSE;
 
8267
 
  (if (not (funcall run-tests-in-test-buffer default-directory))
 
8268
 
      (funcall show-test-buffer-in-test-window)
 
8269
 
    (funcall remove-test-window)))
 
8270
 
run-tests-in-test-buffer:
 
8272
 
  (with-current-buffer (get-buffer-create "*Test*")
 
8273
 
    (setq buffer-read-only nil
 
8274
 
          default-directory dir)
 
8277
 
  (let ((process-result
 
8278
 
         (let ((inhibit-read-only t))
 
8279
 
           (process-file-shell-command
 
8280
 
            (funcall get-command-line) nil "*Test*"))))
 
8281
 
    (and (numberp process-result)
 
8282
 
         (= process-result 0))))
 
8287
 
        (funcall find-build-directory (buffer-file-name)))
 
8288
 
       (local-build-directory
 
8289
 
        (if (fboundp 'file-local-name)
 
8290
 
            (file-local-name build-directory)
 
8291
 
          (or (file-remote-p build-directory 'localname)
 
8294
 
        (file-relative-name (file-name-sans-extension
 
8295
 
                             (buffer-file-name)) build-directory))
 
8296
 
       (qbdir (shell-quote-argument local-build-directory))
 
8297
 
       (qcmd (shell-quote-argument command)))
 
8298
 
    (format (concat "cd %s && CFLAGS=-Werror make --silent %s"
 
8299
 
             " && %s --test --verbose") qbdir qcmd qcmd)))
 
8300
 
find-build-directory:
 
8301
 
(lambda (try-directory &optional base-directory)
 
8302
 
  (let ((base-directory (or base-directory try-directory)))
 
8303
 
    (cond ((equal try-directory "/") base-directory)
 
8305
 
            (concat (file-name-as-directory try-directory)
 
8306
 
                    "Makefile")) try-directory)
 
8307
 
          ((funcall find-build-directory
 
8308
 
                    (directory-file-name (file-name-directory
 
8311
 
show-test-buffer-in-test-window:
 
8313
 
  (when (not (get-buffer-window-list "*Test*"))
 
8314
 
    (setq next-error-last-buffer (get-buffer "*Test*"))
 
8315
 
    (let* ((side (if (>= (window-width) 146) 'right 'bottom))
 
8316
 
           (display-buffer-overriding-action
 
8317
 
            `((display-buffer-in-side-window) (side . ,side)
 
8318
 
              (window-height . fit-window-to-buffer)
 
8319
 
              (window-width . fit-window-to-buffer))))
 
8320
 
      (display-buffer "*Test*"))))
 
8323
 
  (let ((test-window (get-buffer-window "*Test*")))
 
8324
 
    (if test-window (delete-window test-window))))
 
8325
 
eval: (add-hook 'after-save-hook run-tests 90 t)