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 defined(__GNUC__) and __GNUC__ >= 5
 
 
1492
#pragma GCC diagnostic push
 
 
1493
  /* mlock() does not access the memory */
 
 
1494
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
 
 
1496
      if(mlock(send_buffer, send_buffer_length) != 0){
 
 
1497
#if defined(__GNUC__) and __GNUC__ >= 5
 
 
1498
#pragma GCC diagnostic pop
 
 
1500
        /* Warn but do not treat as fatal error */
 
 
1501
        if(errno != EPERM and errno != ENOMEM){
 
 
1502
          error(0, errno, "Failed to lock memory for password"
 
 
1506
      /* “[…] send a single datagram to the socket consisting of the
 
 
1507
         password string either prefixed with "+" or with "-"
 
 
1508
         depending on whether the password entry was successful or
 
 
1509
         not. You may but don't have to include a final NUL byte in
 
 
1512
         — <https://systemd.io/PASSWORD_AGENTS/> (Tue, 15 Sep 2020
 
 
1515
      send_buffer[0] = '+';     /* Prefix with "+" */
 
 
1516
      /* Always add an extra NUL */
 
 
1517
      send_buffer[password->length + 1] = '\0';
 
 
1518
      if(password->length > 0){
 
 
1519
        memcpy(send_buffer + 1, password->data, password->length);
 
 
1522
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
 
 
1524
      const error_t saved_errno = (ssret < 0) ? errno : 0;
 
 
1525
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
 
 
1526
      explicit_bzero(send_buffer, send_buffer_length);
 
 
1528
      memset(send_buffer, '\0', send_buffer_length);
 
 
1530
      if(munlock(send_buffer, send_buffer_length) != 0){
 
 
1531
        error(0, errno, "Failed to unlock memory of send buffer");
 
 
1534
      if(ssret < 0 or ssret < (ssize_t)send_buffer_length){
 
 
1535
        switch(saved_errno){
 
 
1548
          error(0, saved_errno, "Password of size %" PRIuMAX
 
 
1549
                " is too big", (uintmax_t)password->length);
 
 
1553
          __attribute__((fallthrough));
 
 
1556
          if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
 
 
1557
            error(0, 0, "Password only partially sent to socket %s: %"
 
 
1558
                  PRIuMAX " out of %" PRIuMAX " bytes sent", filename,
 
 
1559
                  (uintmax_t)ssret, (uintmax_t)send_buffer_length);
 
 
1564
          __attribute__((fallthrough));
 
 
1567
          error(0, saved_errno, "Failed to send() to socket %s",
 
 
1569
          if(not string_set_add(cancelled_filenames,
 
 
1570
                                question_filename)){
 
 
1571
            error(0, errno, "Failed to cancel question for file %s",
 
 
1574
          cleanup_task(&task);
 
 
1579
        cleanup_task(&task);
 
 
1585
  /* We failed or are not ready yet; retry later */
 
 
1587
  if(not add_to_queue(queue, task)){
 
 
1588
    error(0, errno, "Failed to add send_password_to_socket for"
 
 
1589
          " file %s and socket %s to queue", question_filename,
 
 
1591
    cleanup_task(&task);
 
 
1594
  /* Add the fd to the epoll set */
 
 
1595
  if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd,
 
 
1596
               &(struct epoll_event){ .events=EPOLLOUT })
 
 
1598
    error(0, errno, "Failed to add socket file descriptor %d for"
 
 
1599
          " socket %s to epoll set", fd, filename);
 
 
1600
    /* Force the added task (send_password_to_socket) to run again, at
 
 
1601
       most one second from now */
 
 
1602
    if((queue->next_run == 0)
 
 
1603
       or (queue->next_run > (*current_time + 1000000))){
 
 
1604
      queue->next_run = *current_time + 1000000;
 
 
1609
__attribute__((warn_unused_result))
 
 
1610
bool add_existing_questions(task_queue *const queue,
 
 
1612
                            buffer *const password,
 
 
1613
                            string_set *cancelled_filenames,
 
 
1614
                            const mono_microsecs *const current_time,
 
 
1615
                            bool *const mandos_client_exited,
 
 
1616
                            bool *const password_is_read,
 
 
1617
                            const char *const dirname){
 
 
1618
  __attribute__((cleanup(cleanup_string)))
 
 
1619
    char *dir_pattern = NULL;
 
 
1620
  const int ret = asprintf(&dir_pattern, "%s/ask.*", dirname);
 
 
1621
  if(ret < 0 or dir_pattern == NULL){
 
 
1622
    error(0, errno, "Could not create glob pattern for directory %s",
 
 
1626
  __attribute__((cleanup(globfree)))
 
 
1627
    glob_t question_filenames = {};
 
 
1628
  switch(glob(dir_pattern, GLOB_ERR | GLOB_NOSORT | GLOB_MARK,
 
 
1629
              NULL, &question_filenames)){
 
 
1632
    error(0, errno, "Failed to open directory %s", dirname);
 
 
1635
    error(0, errno, "There are no question files in %s", dirname);
 
 
1638
    error(0, errno, "Could not allocate memory for question file"
 
 
1639
          " names in %s", dirname);
 
 
1643
    __attribute__((fallthrough));
 
 
1646
    for(size_t i = 0; i < question_filenames.gl_pathc; i++){
 
 
1647
      char *const question_filename = strdup(question_filenames
 
 
1649
      const task_context task = {
 
 
1650
        .func=open_and_parse_question,
 
 
1652
        .question_filename=question_filename,
 
 
1653
        .filename=question_filename,
 
 
1655
        .cancelled_filenames=cancelled_filenames,
 
 
1656
        .current_time=current_time,
 
 
1657
        .mandos_client_exited=mandos_client_exited,
 
 
1658
        .password_is_read=password_is_read,
 
 
1661
      if(question_filename == NULL
 
 
1662
         or not add_to_queue(queue, task)){
 
 
1663
        error(0, errno, "Failed to add open_and_parse_question for"
 
 
1664
              " file %s to queue",
 
 
1665
              question_filenames.gl_pathv[i]);
 
 
1666
        free(question_filename);
 
 
1668
        queue->next_run = 1;
 
 
1675
__attribute__((nonnull, warn_unused_result))
 
 
1676
bool wait_for_event(const int epoll_fd,
 
 
1677
                    const mono_microsecs queue_next_run,
 
 
1678
                    const mono_microsecs current_time){
 
 
1679
  __attribute__((const))
 
 
1680
    int milliseconds_to_wait(const mono_microsecs currtime,
 
 
1681
                             const mono_microsecs nextrun){
 
 
1682
    if(currtime >= nextrun){
 
 
1685
    const uintmax_t wait_time_ms = (nextrun - currtime) / 1000;
 
 
1686
    if(wait_time_ms > (uintmax_t)INT_MAX){
 
 
1689
    return (int)wait_time_ms;
 
 
1692
  const int wait_time_ms = milliseconds_to_wait(current_time,
 
 
1695
  /* Prepare unblocking of SIGCHLD during epoll_pwait */
 
 
1696
  sigset_t temporary_unblocked_sigmask;
 
 
1697
  /* Get current signal mask */
 
 
1698
  if(pthread_sigmask(-1, NULL, &temporary_unblocked_sigmask) != 0){
 
 
1701
  /* Remove SIGCHLD from the signal mask */
 
 
1702
  if(sigdelset(&temporary_unblocked_sigmask, SIGCHLD) != 0){
 
 
1705
  struct epoll_event events[8]; /* Ignored */
 
 
1706
  int ret = epoll_pwait(epoll_fd, events,
 
 
1707
                        sizeof(events) / sizeof(struct epoll_event),
 
 
1708
                        queue_next_run == 0 ? -1 : (int)wait_time_ms,
 
 
1709
                        &temporary_unblocked_sigmask);
 
 
1710
  if(ret < 0 and errno != EINTR){
 
 
1711
    error(0, errno, "Failed epoll_pwait(epfd=%d, ..., timeout=%d,"
 
 
1713
          queue_next_run == 0 ? -1 : (int)wait_time_ms);
 
 
1716
  return clear_all_fds_from_epoll_set(epoll_fd);
 
 
1719
bool clear_all_fds_from_epoll_set(const int epoll_fd){
 
 
1720
  /* Create a new empty epoll set */
 
 
1721
  __attribute__((cleanup(cleanup_close)))
 
 
1722
    const int new_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
1723
  if(new_epoll_fd < 0){
 
 
1726
  /* dup3() the new epoll set fd over the old one, replacing it */
 
 
1727
  if(dup3(new_epoll_fd, epoll_fd, O_CLOEXEC) < 0){
 
 
1733
__attribute__((nonnull, warn_unused_result))
 
 
1734
bool run_queue(task_queue **const queue,
 
 
1735
               string_set *const cancelled_filenames,
 
 
1736
               bool *const quit_now){
 
 
1738
  task_queue *new_queue = create_queue();
 
 
1739
  if(new_queue == NULL){
 
 
1743
  __attribute__((cleanup(string_set_clear)))
 
 
1744
    string_set old_cancelled_filenames = {};
 
 
1745
  string_set_swap(cancelled_filenames, &old_cancelled_filenames);
 
 
1747
  /* Declare i outside the for loop, since we might need i after the
 
 
1748
     loop in case we aborted in the middle */
 
 
1750
  for(i=0; i < (*queue)->length and not *quit_now; i++){
 
 
1751
    task_context *const task = &((*queue)->tasks[i]);
 
 
1752
    const char *const question_filename = task->question_filename;
 
 
1753
    /* Skip any task referencing a cancelled question filename */
 
 
1754
    if(question_filename != NULL
 
 
1755
       and string_set_contains(old_cancelled_filenames,
 
 
1756
                               question_filename)){
 
 
1760
    task->func(*task, new_queue);
 
 
1764
    /* we might be in the middle of the queue, so clean up any
 
 
1765
       remaining tasks in the current queue */
 
 
1766
    for(; i < (*queue)->length; i++){
 
 
1767
      cleanup_task(&((*queue)->tasks[i]));
 
 
1781
/* End of regular code section */
 
 
1783
/* Start of tests section; here are the tests for the above code */
 
 
1785
/* This "fixture" data structure is used by the test setup and
 
 
1786
   teardown functions */
 
 
1788
  struct sigaction orig_sigaction;
 
 
1789
  sigset_t orig_sigmask;
 
 
1792
static void test_setup(test_fixture *fixture,
 
 
1793
                       __attribute__((unused))
 
 
1794
                       gconstpointer user_data){
 
 
1795
  g_assert_true(setup_signal_handler(&fixture->orig_sigaction));
 
 
1796
  g_assert_true(block_sigchld(&fixture->orig_sigmask));
 
 
1799
static void test_teardown(test_fixture *fixture,
 
 
1800
                          __attribute__((unused))
 
 
1801
                          gconstpointer user_data){
 
 
1802
  g_assert_true(restore_signal_handler(&fixture->orig_sigaction));
 
 
1803
  g_assert_true(restore_sigmask(&fixture->orig_sigmask));
 
 
1806
/* Utility function used by tests to search queue for matching task */
 
 
1807
__attribute__((pure, nonnull, warn_unused_result))
 
 
1808
static task_context *find_matching_task(const task_queue *const queue,
 
 
1809
                                        const task_context task){
 
 
1810
  /* The argument "task" structure is a pattern to match; 0 in any
 
 
1811
     member means any value matches, otherwise the value must match.
 
 
1812
     The filename strings are compared by strcmp(), not by pointer. */
 
 
1813
  for(size_t i = 0; i < queue->length; i++){
 
 
1814
    task_context *const current_task = queue->tasks+i;
 
 
1815
    /* Check all members of task_context, if set to a non-zero value.
 
 
1816
       If a member does not match, continue to next task in queue */
 
 
1818
    /* task_func *const func */
 
 
1819
    if(task.func != NULL and current_task->func != task.func){
 
 
1822
    /* char *const question_filename; */
 
 
1823
    if(task.question_filename != NULL
 
 
1824
       and (current_task->question_filename == NULL
 
 
1825
            or strcmp(current_task->question_filename,
 
 
1826
                      task.question_filename) != 0)){
 
 
1829
    /* const pid_t pid; */
 
 
1830
    if(task.pid != 0 and current_task->pid != task.pid){
 
 
1833
    /* const int epoll_fd; */
 
 
1834
    if(task.epoll_fd != 0
 
 
1835
       and current_task->epoll_fd != task.epoll_fd){
 
 
1838
    /* bool *const quit_now; */
 
 
1839
    if(task.quit_now != NULL
 
 
1840
       and current_task->quit_now != task.quit_now){
 
 
1844
    if(task.fd != 0 and current_task->fd != task.fd){
 
 
1847
    /* bool *const mandos_client_exited; */
 
 
1848
    if(task.mandos_client_exited != NULL
 
 
1849
       and current_task->mandos_client_exited
 
 
1850
       != task.mandos_client_exited){
 
 
1853
    /* buffer *const password; */
 
 
1854
    if(task.password != NULL
 
 
1855
       and current_task->password != task.password){
 
 
1858
    /* bool *const password_is_read; */
 
 
1859
    if(task.password_is_read != NULL
 
 
1860
       and current_task->password_is_read != task.password_is_read){
 
 
1863
    /* char *filename; */
 
 
1864
    if(task.filename != NULL
 
 
1865
       and (current_task->filename == NULL
 
 
1866
            or strcmp(current_task->filename, task.filename) != 0)){
 
 
1869
    /* string_set *const cancelled_filenames; */
 
 
1870
    if(task.cancelled_filenames != NULL
 
 
1871
       and current_task->cancelled_filenames
 
 
1872
       != task.cancelled_filenames){
 
 
1875
    /* const mono_microsecs notafter; */
 
 
1876
    if(task.notafter != 0
 
 
1877
       and current_task->notafter != task.notafter){
 
 
1880
    /* const mono_microsecs *const current_time; */
 
 
1881
    if(task.current_time != NULL
 
 
1882
       and current_task->current_time != task.current_time){
 
 
1885
    /* Current task matches all members; return it */
 
 
1886
    return current_task;
 
 
1888
  /* No task in queue matches passed pattern task */
 
 
1892
static void test_create_queue(__attribute__((unused))
 
 
1893
                              test_fixture *fixture,
 
 
1894
                              __attribute__((unused))
 
 
1895
                              gconstpointer user_data){
 
 
1896
  __attribute__((cleanup(cleanup_queue)))
 
 
1897
    task_queue *const queue = create_queue();
 
 
1898
  g_assert_nonnull(queue);
 
 
1899
  g_assert_null(queue->tasks);
 
 
1900
  g_assert_true(queue->length == 0);
 
 
1901
  g_assert_true(queue->next_run == 0);
 
 
1904
static task_func dummy_func;
 
 
1906
static void test_add_to_queue(__attribute__((unused))
 
 
1907
                              test_fixture *fixture,
 
 
1908
                              __attribute__((unused))
 
 
1909
                              gconstpointer user_data){
 
 
1910
  __attribute__((cleanup(cleanup_queue)))
 
 
1911
    task_queue *queue = create_queue();
 
 
1912
  g_assert_nonnull(queue);
 
 
1914
  g_assert_true(add_to_queue(queue,
 
 
1915
                             (task_context){ .func=dummy_func }));
 
 
1916
  g_assert_true(queue->length == 1);
 
 
1917
  g_assert_nonnull(queue->tasks);
 
 
1918
  g_assert_true(queue->tasks[0].func == dummy_func);
 
 
1921
static void test_add_to_queue_overflow(__attribute__((unused))
 
 
1922
                                       test_fixture *fixture,
 
 
1923
                                       __attribute__((unused))
 
 
1924
                                       gconstpointer user_data){
 
 
1925
  __attribute__((cleanup(cleanup_queue)))
 
 
1926
    task_queue *queue = create_queue();
 
 
1927
  g_assert_nonnull(queue);
 
 
1928
  g_assert_true(queue->length == 0);
 
 
1929
  queue->length = SIZE_MAX / sizeof(task_context); /* fake max size */
 
 
1931
  FILE *real_stderr = stderr;
 
 
1932
  FILE *devnull = fopen("/dev/null", "we");
 
 
1933
  g_assert_nonnull(devnull);
 
 
1935
  const bool ret = add_to_queue(queue,
 
 
1936
                                (task_context){ .func=dummy_func });
 
 
1937
  g_assert_true(errno == ENOMEM);
 
 
1938
  g_assert_false(ret);
 
 
1939
  stderr = real_stderr;
 
 
1940
  g_assert_cmpint(fclose(devnull), ==, 0);
 
 
1941
  queue->length = 0;            /* Restore real size */
 
 
1944
static void dummy_func(__attribute__((unused))
 
 
1945
                       const task_context task,
 
 
1946
                       __attribute__((unused))
 
 
1947
                       task_queue *const queue){
 
 
1950
static void test_queue_has_question_empty(__attribute__((unused))
 
 
1951
                                          test_fixture *fixture,
 
 
1952
                                          __attribute__((unused))
 
 
1953
                                          gconstpointer user_data){
 
 
1954
  __attribute__((cleanup(cleanup_queue)))
 
 
1955
    task_queue *queue = create_queue();
 
 
1956
  g_assert_nonnull(queue);
 
 
1957
  g_assert_false(queue_has_question(queue));
 
 
1960
static void test_queue_has_question_false(__attribute__((unused))
 
 
1961
                                          test_fixture *fixture,
 
 
1962
                                          __attribute__((unused))
 
 
1963
                                          gconstpointer user_data){
 
 
1964
  __attribute__((cleanup(cleanup_queue)))
 
 
1965
    task_queue *queue = create_queue();
 
 
1966
  g_assert_nonnull(queue);
 
 
1967
  g_assert_true(add_to_queue(queue,
 
 
1968
                             (task_context){ .func=dummy_func }));
 
 
1969
  g_assert_false(queue_has_question(queue));
 
 
1972
static void test_queue_has_question_true(__attribute__((unused))
 
 
1973
                                         test_fixture *fixture,
 
 
1974
                                         __attribute__((unused))
 
 
1975
                                         gconstpointer user_data){
 
 
1976
  __attribute__((cleanup(cleanup_queue)))
 
 
1977
    task_queue *queue = create_queue();
 
 
1978
  g_assert_nonnull(queue);
 
 
1979
  char *const question_filename
 
 
1980
    = strdup("/nonexistent/question_filename");
 
 
1981
  g_assert_nonnull(question_filename);
 
 
1982
  task_context task = {
 
 
1984
    .question_filename=question_filename,
 
 
1986
  g_assert_true(add_to_queue(queue, task));
 
 
1987
  g_assert_true(queue_has_question(queue));
 
 
1990
static void test_queue_has_question_false2(__attribute__((unused))
 
 
1991
                                           test_fixture *fixture,
 
 
1992
                                           __attribute__((unused))
 
 
1993
                                           gconstpointer user_data){
 
 
1994
  __attribute__((cleanup(cleanup_queue)))
 
 
1995
    task_queue *queue = create_queue();
 
 
1996
  g_assert_nonnull(queue);
 
 
1997
  task_context task = { .func=dummy_func };
 
 
1998
  g_assert_true(add_to_queue(queue, task));
 
 
1999
  g_assert_true(add_to_queue(queue, task));
 
 
2000
  g_assert_cmpint((int)queue->length, ==, 2);
 
 
2001
  g_assert_false(queue_has_question(queue));
 
 
2004
static void test_queue_has_question_true2(__attribute__((unused))
 
 
2005
                                          test_fixture *fixture,
 
 
2006
                                          __attribute__((unused))
 
 
2007
                                          gconstpointer user_data){
 
 
2008
  __attribute__((cleanup(cleanup_queue)))
 
 
2009
    task_queue *queue = create_queue();
 
 
2010
  g_assert_nonnull(queue);
 
 
2011
  task_context task1 = { .func=dummy_func };
 
 
2012
  g_assert_true(add_to_queue(queue, task1));
 
 
2013
  char *const question_filename
 
 
2014
    = strdup("/nonexistent/question_filename");
 
 
2015
  g_assert_nonnull(question_filename);
 
 
2016
  task_context task2 = {
 
 
2018
    .question_filename=question_filename,
 
 
2020
  g_assert_true(add_to_queue(queue, task2));
 
 
2021
  g_assert_cmpint((int)queue->length, ==, 2);
 
 
2022
  g_assert_true(queue_has_question(queue));
 
 
2025
static void test_cleanup_buffer(__attribute__((unused))
 
 
2026
                                test_fixture *fixture,
 
 
2027
                                __attribute__((unused))
 
 
2028
                                gconstpointer user_data){
 
 
2031
  const size_t buffersize = 10;
 
 
2033
  buf.data = malloc(buffersize);
 
 
2034
  g_assert_nonnull(buf.data);
 
 
2035
  if(mlock(buf.data, buffersize) != 0){
 
 
2036
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
2039
  cleanup_buffer(&buf);
 
 
2040
  g_assert_null(buf.data);
 
 
2044
void test_string_set_new_set_contains_nothing(__attribute__((unused))
 
 
2045
                                              test_fixture *fixture,
 
 
2046
                                              __attribute__((unused))
 
 
2049
  __attribute__((cleanup(string_set_clear)))
 
 
2050
    string_set set = {};
 
 
2051
  g_assert_false(string_set_contains(set, "")); /* Empty string */
 
 
2052
  g_assert_false(string_set_contains(set, "test_string"));
 
 
2056
test_string_set_with_added_string_contains_it(__attribute__((unused))
 
 
2057
                                              test_fixture *fixture,
 
 
2058
                                              __attribute__((unused))
 
 
2061
  __attribute__((cleanup(string_set_clear)))
 
 
2062
    string_set set = {};
 
 
2063
  g_assert_true(string_set_add(&set, "test_string"));
 
 
2064
  g_assert_true(string_set_contains(set, "test_string"));
 
 
2068
test_string_set_cleared_does_not_contain_str(__attribute__((unused))
 
 
2069
                                             test_fixture *fixture,
 
 
2070
                                             __attribute__((unused))
 
 
2071
                                             gconstpointer user_data){
 
 
2072
  __attribute__((cleanup(string_set_clear)))
 
 
2073
    string_set set = {};
 
 
2074
  g_assert_true(string_set_add(&set, "test_string"));
 
 
2075
  string_set_clear(&set);
 
 
2076
  g_assert_false(string_set_contains(set, "test_string"));
 
 
2080
void test_string_set_swap_one_with_empty(__attribute__((unused))
 
 
2081
                                         test_fixture *fixture,
 
 
2082
                                         __attribute__((unused))
 
 
2083
                                         gconstpointer user_data){
 
 
2084
  __attribute__((cleanup(string_set_clear)))
 
 
2085
    string_set set1 = {};
 
 
2086
  __attribute__((cleanup(string_set_clear)))
 
 
2087
    string_set set2 = {};
 
 
2088
  g_assert_true(string_set_add(&set1, "test_string1"));
 
 
2089
  string_set_swap(&set1, &set2);
 
 
2090
  g_assert_false(string_set_contains(set1, "test_string1"));
 
 
2091
  g_assert_true(string_set_contains(set2, "test_string1"));
 
 
2095
void test_string_set_swap_empty_with_one(__attribute__((unused))
 
 
2096
                                         test_fixture *fixture,
 
 
2097
                                         __attribute__((unused))
 
 
2098
                                         gconstpointer user_data){
 
 
2099
  __attribute__((cleanup(string_set_clear)))
 
 
2100
    string_set set1 = {};
 
 
2101
  __attribute__((cleanup(string_set_clear)))
 
 
2102
    string_set set2 = {};
 
 
2103
  g_assert_true(string_set_add(&set2, "test_string2"));
 
 
2104
  string_set_swap(&set1, &set2);
 
 
2105
  g_assert_true(string_set_contains(set1, "test_string2"));
 
 
2106
  g_assert_false(string_set_contains(set2, "test_string2"));
 
 
2109
static void test_string_set_swap_one_with_one(__attribute__((unused))
 
 
2110
                                              test_fixture *fixture,
 
 
2111
                                              __attribute__((unused))
 
 
2114
  __attribute__((cleanup(string_set_clear)))
 
 
2115
    string_set set1 = {};
 
 
2116
  __attribute__((cleanup(string_set_clear)))
 
 
2117
    string_set set2 = {};
 
 
2118
  g_assert_true(string_set_add(&set1, "test_string1"));
 
 
2119
  g_assert_true(string_set_add(&set2, "test_string2"));
 
 
2120
  string_set_swap(&set1, &set2);
 
 
2121
  g_assert_false(string_set_contains(set1, "test_string1"));
 
 
2122
  g_assert_true(string_set_contains(set1, "test_string2"));
 
 
2123
  g_assert_false(string_set_contains(set2, "test_string2"));
 
 
2124
  g_assert_true(string_set_contains(set2, "test_string1"));
 
 
2127
static bool fd_has_cloexec_and_nonblock(const int);
 
 
2129
static bool epoll_set_contains(int, int, uint32_t);
 
 
2131
static void test_start_mandos_client(test_fixture *fixture,
 
 
2132
                                     __attribute__((unused))
 
 
2133
                                     gconstpointer user_data){
 
 
2135
  bool mandos_client_exited = false;
 
 
2136
  bool quit_now = false;
 
 
2137
  __attribute__((cleanup(cleanup_close)))
 
 
2138
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2139
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2140
  __attribute__((cleanup(cleanup_queue)))
 
 
2141
    task_queue *queue = create_queue();
 
 
2142
  g_assert_nonnull(queue);
 
 
2143
  buffer password = {};
 
 
2144
  bool password_is_read = false;
 
 
2145
  const char helper_directory[] = "/nonexistent";
 
 
2146
  const char *const argv[] = { "/bin/true", NULL };
 
 
2148
  g_assert_true(start_mandos_client(queue, epoll_fd,
 
 
2149
                                    &mandos_client_exited, &quit_now,
 
 
2150
                                    &password, &password_is_read,
 
 
2151
                                    &fixture->orig_sigaction,
 
 
2152
                                    fixture->orig_sigmask,
 
 
2153
                                    helper_directory, 0, 0, argv));
 
 
2155
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
 
2157
  const task_context *const added_wait_task
 
 
2158
    = find_matching_task(queue, (task_context){
 
 
2159
        .func=wait_for_mandos_client_exit,
 
 
2160
        .mandos_client_exited=&mandos_client_exited,
 
 
2161
        .quit_now=&quit_now,
 
 
2163
  g_assert_nonnull(added_wait_task);
 
 
2164
  g_assert_cmpint(added_wait_task->pid, >, 0);
 
 
2165
  g_assert_cmpint(kill(added_wait_task->pid, SIGKILL), ==, 0);
 
 
2166
  waitpid(added_wait_task->pid, NULL, 0);
 
 
2168
  const task_context *const added_read_task
 
 
2169
    = find_matching_task(queue, (task_context){
 
 
2170
        .func=read_mandos_client_output,
 
 
2172
        .password=&password,
 
 
2173
        .password_is_read=&password_is_read,
 
 
2174
        .quit_now=&quit_now,
 
 
2176
  g_assert_nonnull(added_read_task);
 
 
2177
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
2178
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
2179
  g_assert_true(epoll_set_contains(epoll_fd, added_read_task->fd,
 
 
2180
                                   EPOLLIN | EPOLLRDHUP));
 
 
2183
static bool fd_has_cloexec_and_nonblock(const int fd){
 
 
2184
  const int socket_fd_flags = fcntl(fd, F_GETFD, 0);
 
 
2185
  const int socket_file_flags = fcntl(fd, F_GETFL, 0);
 
 
2186
  return ((socket_fd_flags >= 0)
 
 
2187
          and (socket_fd_flags & FD_CLOEXEC)
 
 
2188
          and (socket_file_flags >= 0)
 
 
2189
          and (socket_file_flags & O_NONBLOCK));
 
 
2192
__attribute__((const))
 
 
2193
bool is_privileged(void){
 
 
2194
  uid_t user = getuid() + 1;
 
 
2195
  if(user == 0){                /* Overflow check */
 
 
2198
  gid_t group = getuid() + 1;
 
 
2199
  if(group == 0){               /* Overflow check */
 
 
2202
  const pid_t pid = fork();
 
 
2203
  if(pid == 0){                 /* Child */
 
 
2204
    if(setresgid((uid_t)-1, group, group) == -1){
 
 
2206
        error(EXIT_FAILURE, errno, "Failed to setresgid(-1, %" PRIuMAX
 
 
2207
              ", %" PRIuMAX")", (uintmax_t)group, (uintmax_t)group);
 
 
2211
    if(setresuid((uid_t)-1, user, user) == -1){
 
 
2213
        error(EXIT_FAILURE, errno, "Failed to setresuid(-1, %" PRIuMAX
 
 
2214
              ", %" PRIuMAX")", (uintmax_t)user, (uintmax_t)user);
 
 
2221
    error(EXIT_FAILURE, errno, "Failed to fork()");
 
 
2225
  waitpid(pid, &status, 0);
 
 
2226
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
 
 
2232
static bool epoll_set_contains(int epoll_fd, int fd, uint32_t events){
 
 
2233
  /* Only scan for events in this eventmask */
 
 
2234
  const uint32_t eventmask = EPOLLIN | EPOLLOUT | EPOLLRDHUP;
 
 
2235
  __attribute__((cleanup(cleanup_string)))
 
 
2236
    char *fdinfo_name = NULL;
 
 
2237
  int ret = asprintf(&fdinfo_name, "/proc/self/fdinfo/%d", epoll_fd);
 
 
2238
  g_assert_cmpint(ret, >, 0);
 
 
2239
  g_assert_nonnull(fdinfo_name);
 
 
2241
  FILE *fdinfo = fopen(fdinfo_name, "r");
 
 
2242
  g_assert_nonnull(fdinfo);
 
 
2243
  uint32_t reported_events;
 
 
2248
    if(getline(&line.data, &line.allocated, fdinfo) < 0){
 
 
2251
    /* See proc(5) for format of /proc/PID/fdinfo/FD for epoll fd's */
 
 
2252
    if(sscanf(line.data, "tfd: %d events: %" SCNx32 " ",
 
 
2253
              &found_fd, &reported_events) == 2){
 
 
2258
  } while(not feof(fdinfo) and not ferror(fdinfo));
 
 
2259
  g_assert_cmpint(fclose(fdinfo), ==, 0);
 
 
2266
    /* Don't check events if none are given */
 
 
2269
  return (reported_events & eventmask) == (events & eventmask);
 
 
2272
static void test_start_mandos_client_execv(test_fixture *fixture,
 
 
2273
                                           __attribute__((unused))
 
 
2274
                                           gconstpointer user_data){
 
 
2275
  bool mandos_client_exited = false;
 
 
2276
  bool quit_now = false;
 
 
2277
  __attribute__((cleanup(cleanup_close)))
 
 
2278
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2279
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2280
  __attribute__((cleanup(cleanup_queue)))
 
 
2281
    task_queue *queue = create_queue();
 
 
2282
  g_assert_nonnull(queue);
 
 
2283
  __attribute__((cleanup(cleanup_buffer)))
 
 
2284
    buffer password = {};
 
 
2285
  const char helper_directory[] = "/nonexistent";
 
 
2286
  /* Can't execv("/", ...), so this should fail */
 
 
2287
  const char *const argv[] = { "/", NULL };
 
 
2290
    __attribute__((cleanup(cleanup_close)))
 
 
2291
      const int devnull_fd = open("/dev/null",
 
 
2292
                                  O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
 
2293
    g_assert_cmpint(devnull_fd, >=, 0);
 
 
2294
    __attribute__((cleanup(cleanup_close)))
 
 
2295
      const int real_stderr_fd = dup(STDERR_FILENO);
 
 
2296
    g_assert_cmpint(real_stderr_fd, >=, 0);
 
 
2297
    dup2(devnull_fd, STDERR_FILENO);
 
 
2299
    const bool success = start_mandos_client(queue, epoll_fd,
 
 
2300
                                             &mandos_client_exited,
 
 
2304
                                             &fixture->orig_sigaction,
 
 
2305
                                             fixture->orig_sigmask,
 
 
2306
                                             helper_directory, 0, 0,
 
 
2308
    dup2(real_stderr_fd, STDERR_FILENO);
 
 
2309
    g_assert_true(success);
 
 
2311
  g_assert_cmpuint((unsigned int)queue->length, ==, 2);
 
 
2313
  struct timespec starttime, currtime;
 
 
2314
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2316
    queue->next_run = 0;
 
 
2317
    string_set cancelled_filenames = {};
 
 
2320
      __attribute__((cleanup(cleanup_close)))
 
 
2321
        const int devnull_fd = open("/dev/null",
 
 
2322
                                    O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
 
2323
      g_assert_cmpint(devnull_fd, >=, 0);
 
 
2324
      __attribute__((cleanup(cleanup_close)))
 
 
2325
        const int real_stderr_fd = dup(STDERR_FILENO);
 
 
2326
      g_assert_cmpint(real_stderr_fd, >=, 0);
 
 
2327
      g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2328
      dup2(devnull_fd, STDERR_FILENO);
 
 
2329
      const bool success = run_queue(&queue, &cancelled_filenames,
 
 
2331
      dup2(real_stderr_fd, STDERR_FILENO);
 
 
2336
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2337
  } while(((queue->length) > 0)
 
 
2339
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2341
  g_assert_true(quit_now);
 
 
2342
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2343
  g_assert_true(mandos_client_exited);
 
 
2346
static void test_start_mandos_client_suid_euid(test_fixture *fixture,
 
 
2347
                                               __attribute__((unused))
 
 
2350
  if(not is_privileged()){
 
 
2351
    g_test_skip("Not privileged");
 
 
2355
  bool mandos_client_exited = false;
 
 
2356
  bool quit_now = false;
 
 
2357
  __attribute__((cleanup(cleanup_close)))
 
 
2358
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2359
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2360
  __attribute__((cleanup(cleanup_queue)))
 
 
2361
    task_queue *queue = create_queue();
 
 
2362
  g_assert_nonnull(queue);
 
 
2363
  __attribute__((cleanup(cleanup_buffer)))
 
 
2364
    buffer password = {};
 
 
2365
  bool password_is_read = false;
 
 
2366
  const char helper_directory[] = "/nonexistent";
 
 
2367
  const char *const argv[] = { "/usr/bin/id", "--user", NULL };
 
 
2371
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2372
                                           &mandos_client_exited,
 
 
2373
                                           &quit_now, &password,
 
 
2375
                                           &fixture->orig_sigaction,
 
 
2376
                                           fixture->orig_sigmask,
 
 
2377
                                           helper_directory, user,
 
 
2379
  g_assert_true(success);
 
 
2380
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2382
  struct timespec starttime, currtime;
 
 
2383
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2385
    queue->next_run = 0;
 
 
2386
    string_set cancelled_filenames = {};
 
 
2387
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2388
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2389
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2390
  } while(((queue->length) > 0)
 
 
2392
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2394
  g_assert_false(quit_now);
 
 
2395
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2396
  g_assert_true(mandos_client_exited);
 
 
2398
  g_assert_true(password_is_read);
 
 
2399
  g_assert_nonnull(password.data);
 
 
2402
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
 
2404
  g_assert_true((uid_t)id == id);
 
 
2406
  g_assert_cmpuint((unsigned int)id, ==, 0);
 
 
2409
static void test_start_mandos_client_suid_egid(test_fixture *fixture,
 
 
2410
                                               __attribute__((unused))
 
 
2413
  if(not is_privileged()){
 
 
2414
    g_test_skip("Not privileged");
 
 
2418
  bool mandos_client_exited = false;
 
 
2419
  bool quit_now = false;
 
 
2420
  __attribute__((cleanup(cleanup_close)))
 
 
2421
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2422
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2423
  __attribute__((cleanup(cleanup_queue)))
 
 
2424
    task_queue *queue = create_queue();
 
 
2425
  g_assert_nonnull(queue);
 
 
2426
  __attribute__((cleanup(cleanup_buffer)))
 
 
2427
    buffer password = {};
 
 
2428
  bool password_is_read = false;
 
 
2429
  const char helper_directory[] = "/nonexistent";
 
 
2430
  const char *const argv[] = { "/usr/bin/id", "--group", NULL };
 
 
2434
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2435
                                           &mandos_client_exited,
 
 
2436
                                           &quit_now, &password,
 
 
2438
                                           &fixture->orig_sigaction,
 
 
2439
                                           fixture->orig_sigmask,
 
 
2440
                                           helper_directory, user,
 
 
2442
  g_assert_true(success);
 
 
2443
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2445
  struct timespec starttime, currtime;
 
 
2446
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2448
    queue->next_run = 0;
 
 
2449
    string_set cancelled_filenames = {};
 
 
2450
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2451
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2452
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2453
  } while(((queue->length) > 0)
 
 
2455
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2457
  g_assert_false(quit_now);
 
 
2458
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2459
  g_assert_true(mandos_client_exited);
 
 
2461
  g_assert_true(password_is_read);
 
 
2462
  g_assert_nonnull(password.data);
 
 
2465
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
 
2467
  g_assert_true((gid_t)id == id);
 
 
2469
  g_assert_cmpuint((unsigned int)id, ==, 0);
 
 
2472
static void test_start_mandos_client_suid_ruid(test_fixture *fixture,
 
 
2473
                                               __attribute__((unused))
 
 
2476
  if(not is_privileged()){
 
 
2477
    g_test_skip("Not privileged");
 
 
2481
  bool mandos_client_exited = false;
 
 
2482
  bool quit_now = false;
 
 
2483
  __attribute__((cleanup(cleanup_close)))
 
 
2484
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2485
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2486
  __attribute__((cleanup(cleanup_queue)))
 
 
2487
    task_queue *queue = create_queue();
 
 
2488
  g_assert_nonnull(queue);
 
 
2489
  __attribute__((cleanup(cleanup_buffer)))
 
 
2490
    buffer password = {};
 
 
2491
  bool password_is_read = false;
 
 
2492
  const char helper_directory[] = "/nonexistent";
 
 
2493
  const char *const argv[] = { "/usr/bin/id", "--user", "--real",
 
 
2498
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2499
                                           &mandos_client_exited,
 
 
2500
                                           &quit_now, &password,
 
 
2502
                                           &fixture->orig_sigaction,
 
 
2503
                                           fixture->orig_sigmask,
 
 
2504
                                           helper_directory, user,
 
 
2506
  g_assert_true(success);
 
 
2507
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2509
  struct timespec starttime, currtime;
 
 
2510
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2512
    queue->next_run = 0;
 
 
2513
    string_set cancelled_filenames = {};
 
 
2514
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2515
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2516
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2517
  } while(((queue->length) > 0)
 
 
2519
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2521
  g_assert_false(quit_now);
 
 
2522
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2523
  g_assert_true(mandos_client_exited);
 
 
2525
  g_assert_true(password_is_read);
 
 
2526
  g_assert_nonnull(password.data);
 
 
2529
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
 
2531
  g_assert_true((uid_t)id == id);
 
 
2533
  g_assert_cmpuint((unsigned int)id, ==, user);
 
 
2536
static void test_start_mandos_client_suid_rgid(test_fixture *fixture,
 
 
2537
                                               __attribute__((unused))
 
 
2540
  if(not is_privileged()){
 
 
2541
    g_test_skip("Not privileged");
 
 
2545
  bool mandos_client_exited = false;
 
 
2546
  bool quit_now = false;
 
 
2547
  __attribute__((cleanup(cleanup_close)))
 
 
2548
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2549
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2550
  __attribute__((cleanup(cleanup_queue)))
 
 
2551
    task_queue *queue = create_queue();
 
 
2552
  g_assert_nonnull(queue);
 
 
2553
  __attribute__((cleanup(cleanup_buffer)))
 
 
2554
    buffer password = {};
 
 
2555
  bool password_is_read = false;
 
 
2556
  const char helper_directory[] = "/nonexistent";
 
 
2557
  const char *const argv[] = { "/usr/bin/id", "--group", "--real",
 
 
2562
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2563
                                           &mandos_client_exited,
 
 
2564
                                           &quit_now, &password,
 
 
2566
                                           &fixture->orig_sigaction,
 
 
2567
                                           fixture->orig_sigmask,
 
 
2568
                                           helper_directory, user,
 
 
2570
  g_assert_true(success);
 
 
2571
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2573
  struct timespec starttime, currtime;
 
 
2574
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2576
    queue->next_run = 0;
 
 
2577
    string_set cancelled_filenames = {};
 
 
2578
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2579
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2580
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2581
  } while(((queue->length) > 0)
 
 
2583
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2585
  g_assert_false(quit_now);
 
 
2586
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2587
  g_assert_true(mandos_client_exited);
 
 
2589
  g_assert_true(password_is_read);
 
 
2590
  g_assert_nonnull(password.data);
 
 
2593
  g_assert_cmpint(sscanf(password.data, "%" SCNuMAX "\n", &id),
 
 
2595
  g_assert_true((gid_t)id == id);
 
 
2597
  g_assert_cmpuint((unsigned int)id, ==, group);
 
 
2600
static void test_start_mandos_client_read(test_fixture *fixture,
 
 
2601
                                          __attribute__((unused))
 
 
2602
                                          gconstpointer user_data){
 
 
2603
  bool mandos_client_exited = false;
 
 
2604
  bool quit_now = false;
 
 
2605
  __attribute__((cleanup(cleanup_close)))
 
 
2606
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2607
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2608
  __attribute__((cleanup(cleanup_queue)))
 
 
2609
    task_queue *queue = create_queue();
 
 
2610
  g_assert_nonnull(queue);
 
 
2611
  __attribute__((cleanup(cleanup_buffer)))
 
 
2612
    buffer password = {};
 
 
2613
  bool password_is_read = false;
 
 
2614
  const char dummy_test_password[] = "dummy test password";
 
 
2615
  const char helper_directory[] = "/nonexistent";
 
 
2616
  const char *const argv[] = { "/bin/echo", "-n", dummy_test_password,
 
 
2619
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2620
                                           &mandos_client_exited,
 
 
2621
                                           &quit_now, &password,
 
 
2623
                                           &fixture->orig_sigaction,
 
 
2624
                                           fixture->orig_sigmask,
 
 
2625
                                           helper_directory, 0, 0,
 
 
2627
  g_assert_true(success);
 
 
2628
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2630
  struct timespec starttime, currtime;
 
 
2631
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2633
    queue->next_run = 0;
 
 
2634
    string_set cancelled_filenames = {};
 
 
2635
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2636
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2637
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2638
  } while(((queue->length) > 0)
 
 
2640
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2642
  g_assert_false(quit_now);
 
 
2643
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2644
  g_assert_true(mandos_client_exited);
 
 
2646
  g_assert_true(password_is_read);
 
 
2647
  g_assert_cmpint((int)password.length, ==,
 
 
2648
                  sizeof(dummy_test_password)-1);
 
 
2649
  g_assert_nonnull(password.data);
 
 
2650
  g_assert_cmpint(memcmp(dummy_test_password, password.data,
 
 
2651
                         sizeof(dummy_test_password)-1), ==, 0);
 
 
2655
void test_start_mandos_client_helper_directory(test_fixture *fixture,
 
 
2656
                                               __attribute__((unused))
 
 
2659
  bool mandos_client_exited = false;
 
 
2660
  bool quit_now = false;
 
 
2661
  __attribute__((cleanup(cleanup_close)))
 
 
2662
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2663
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2664
  __attribute__((cleanup(cleanup_queue)))
 
 
2665
    task_queue *queue = create_queue();
 
 
2666
  g_assert_nonnull(queue);
 
 
2667
  __attribute__((cleanup(cleanup_buffer)))
 
 
2668
    buffer password = {};
 
 
2669
  bool password_is_read = false;
 
 
2670
  const char helper_directory[] = "/nonexistent";
 
 
2671
  const char *const argv[] = { "/bin/sh", "-c",
 
 
2672
    "printf %s \"${MANDOSPLUGINHELPERDIR}\"", NULL };
 
 
2674
  const bool success = start_mandos_client(queue, epoll_fd,
 
 
2675
                                           &mandos_client_exited,
 
 
2676
                                           &quit_now, &password,
 
 
2678
                                           &fixture->orig_sigaction,
 
 
2679
                                           fixture->orig_sigmask,
 
 
2680
                                           helper_directory, 0, 0,
 
 
2682
  g_assert_true(success);
 
 
2683
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
2685
  struct timespec starttime, currtime;
 
 
2686
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2688
    queue->next_run = 0;
 
 
2689
    string_set cancelled_filenames = {};
 
 
2690
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2691
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2692
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2693
  } while(((queue->length) > 0)
 
 
2695
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2697
  g_assert_false(quit_now);
 
 
2698
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2699
  g_assert_true(mandos_client_exited);
 
 
2701
  g_assert_true(password_is_read);
 
 
2702
  g_assert_cmpint((int)password.length, ==,
 
 
2703
                  sizeof(helper_directory)-1);
 
 
2704
  g_assert_nonnull(password.data);
 
 
2705
  g_assert_cmpint(memcmp(helper_directory, password.data,
 
 
2706
                         sizeof(helper_directory)-1), ==, 0);
 
 
2709
__attribute__((nonnull, warn_unused_result))
 
 
2710
static bool proc_status_sigblk_to_sigset(const char *const,
 
 
2713
static void test_start_mandos_client_sigmask(test_fixture *fixture,
 
 
2714
                                             __attribute__((unused))
 
 
2715
                                             gconstpointer user_data){
 
 
2716
  bool mandos_client_exited = false;
 
 
2717
  bool quit_now = false;
 
 
2718
  __attribute__((cleanup(cleanup_close)))
 
 
2719
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2720
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2721
  __attribute__((cleanup(cleanup_queue)))
 
 
2722
    task_queue *queue = create_queue();
 
 
2723
  g_assert_nonnull(queue);
 
 
2724
  __attribute__((cleanup(cleanup_buffer)))
 
 
2725
    buffer password = {};
 
 
2726
  bool password_is_read = false;
 
 
2727
  const char helper_directory[] = "/nonexistent";
 
 
2728
  /* see proc(5) for format of /proc/self/status */
 
 
2729
  const char *const argv[] = { "/usr/bin/awk",
 
 
2730
    "$1==\"SigBlk:\"{ print $2 }", "/proc/self/status", NULL };
 
 
2732
  g_assert_true(start_mandos_client(queue, epoll_fd,
 
 
2733
                                    &mandos_client_exited, &quit_now,
 
 
2734
                                    &password, &password_is_read,
 
 
2735
                                    &fixture->orig_sigaction,
 
 
2736
                                    fixture->orig_sigmask,
 
 
2737
                                    helper_directory, 0, 0, argv));
 
 
2739
  struct timespec starttime, currtime;
 
 
2740
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2742
    queue->next_run = 0;
 
 
2743
    string_set cancelled_filenames = {};
 
 
2744
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2745
    g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
2746
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2747
  } while((not (mandos_client_exited and password_is_read))
 
 
2749
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2750
  g_assert_true(mandos_client_exited);
 
 
2751
  g_assert_true(password_is_read);
 
 
2753
  sigset_t parsed_sigmask;
 
 
2754
  g_assert_true(proc_status_sigblk_to_sigset(password.data,
 
 
2757
  for(int signum = 1; signum < NSIG; signum++){
 
 
2758
    const bool has_signal = sigismember(&parsed_sigmask, signum);
 
 
2759
    if(sigismember(&fixture->orig_sigmask, signum)){
 
 
2760
      g_assert_true(has_signal);
 
 
2762
      g_assert_false(has_signal);
 
 
2767
__attribute__((nonnull, warn_unused_result))
 
 
2768
static bool proc_status_sigblk_to_sigset(const char *const sigblk,
 
 
2769
                                         sigset_t *const sigmask){
 
 
2770
  /* parse /proc/PID/status SigBlk value and convert to a sigset_t */
 
 
2771
  uintmax_t scanned_sigmask;
 
 
2772
  if(sscanf(sigblk, "%" SCNxMAX " ", &scanned_sigmask) != 1){
 
 
2775
  if(sigemptyset(sigmask) != 0){
 
 
2778
  for(int signum = 1; signum < NSIG; signum++){
 
 
2779
    if(scanned_sigmask & ((uintmax_t)1 << (signum-1))){
 
 
2780
      if(sigaddset(sigmask, signum) != 0){
 
 
2788
static void run_task_with_stderr_to_dev_null(const task_context task,
 
 
2789
                                             task_queue *const queue);
 
 
2792
void test_wait_for_mandos_client_exit_badpid(__attribute__((unused))
 
 
2793
                                             test_fixture *fixture,
 
 
2794
                                             __attribute__((unused))
 
 
2795
                                             gconstpointer user_data){
 
 
2797
  bool mandos_client_exited = false;
 
 
2798
  bool quit_now = false;
 
 
2800
  __attribute__((cleanup(cleanup_queue)))
 
 
2801
    task_queue *queue = create_queue();
 
 
2802
  g_assert_nonnull(queue);
 
 
2803
  const task_context task = {
 
 
2804
    .func=wait_for_mandos_client_exit,
 
 
2806
    .mandos_client_exited=&mandos_client_exited,
 
 
2807
    .quit_now=&quit_now,
 
 
2809
  run_task_with_stderr_to_dev_null(task, queue);
 
 
2811
  g_assert_false(mandos_client_exited);
 
 
2812
  g_assert_true(quit_now);
 
 
2813
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2816
static void run_task_with_stderr_to_dev_null(const task_context task,
 
 
2817
                                             task_queue *const queue){
 
 
2818
  FILE *real_stderr = stderr;
 
 
2819
  FILE *devnull = fopen("/dev/null", "we");
 
 
2820
  g_assert_nonnull(devnull);
 
 
2823
  task.func(task, queue);
 
 
2824
  stderr = real_stderr;
 
 
2826
  g_assert_cmpint(fclose(devnull), ==, 0);
 
 
2830
void test_wait_for_mandos_client_exit_noexit(test_fixture *fixture,
 
 
2831
                                             __attribute__((unused))
 
 
2832
                                             gconstpointer user_data){
 
 
2833
  bool mandos_client_exited = false;
 
 
2834
  bool quit_now = false;
 
 
2836
  pid_t create_eternal_process(void){
 
 
2837
    const pid_t pid = fork();
 
 
2838
    if(pid == 0){               /* Child */
 
 
2839
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
 
2840
        _exit(EXIT_FAILURE);
 
 
2842
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
 
2843
        _exit(EXIT_FAILURE);
 
 
2851
  pid_t pid = create_eternal_process();
 
 
2852
  g_assert_true(pid != -1);
 
 
2854
  __attribute__((cleanup(cleanup_queue)))
 
 
2855
    task_queue *queue = create_queue();
 
 
2856
  g_assert_nonnull(queue);
 
 
2857
  const task_context task = {
 
 
2858
    .func=wait_for_mandos_client_exit,
 
 
2860
    .mandos_client_exited=&mandos_client_exited,
 
 
2861
    .quit_now=&quit_now,
 
 
2863
  task.func(task, queue);
 
 
2865
  g_assert_false(mandos_client_exited);
 
 
2866
  g_assert_false(quit_now);
 
 
2867
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
2869
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
2870
        .func=wait_for_mandos_client_exit,
 
 
2872
        .mandos_client_exited=&mandos_client_exited,
 
 
2873
        .quit_now=&quit_now,
 
 
2878
void test_wait_for_mandos_client_exit_success(test_fixture *fixture,
 
 
2879
                                              __attribute__((unused))
 
 
2882
  bool mandos_client_exited = false;
 
 
2883
  bool quit_now = false;
 
 
2885
  pid_t create_successful_process(void){
 
 
2886
    const pid_t pid = fork();
 
 
2887
    if(pid == 0){               /* Child */
 
 
2888
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
 
2889
        _exit(EXIT_FAILURE);
 
 
2891
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
 
2892
        _exit(EXIT_FAILURE);
 
 
2898
  const pid_t pid = create_successful_process();
 
 
2899
  g_assert_true(pid != -1);
 
 
2901
  __attribute__((cleanup(cleanup_queue)))
 
 
2902
    task_queue *queue = create_queue();
 
 
2903
  g_assert_nonnull(queue);
 
 
2904
  const task_context initial_task = {
 
 
2905
    .func=wait_for_mandos_client_exit,
 
 
2907
    .mandos_client_exited=&mandos_client_exited,
 
 
2908
    .quit_now=&quit_now,
 
 
2910
  g_assert_true(add_to_queue(queue, initial_task));
 
 
2912
  struct timespec starttime, currtime;
 
 
2913
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2914
  __attribute__((cleanup(cleanup_close)))
 
 
2915
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2917
    queue->next_run = 0;
 
 
2918
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2919
    g_assert_true(run_queue(&queue, (string_set[]){{}}, &quit_now));
 
 
2920
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2921
  } while((not mandos_client_exited)
 
 
2923
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2925
  g_assert_true(mandos_client_exited);
 
 
2926
  g_assert_false(quit_now);
 
 
2927
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
2931
void test_wait_for_mandos_client_exit_failure(test_fixture *fixture,
 
 
2932
                                              __attribute__((unused))
 
 
2935
  bool mandos_client_exited = false;
 
 
2936
  bool quit_now = false;
 
 
2938
  pid_t create_failing_process(void){
 
 
2939
    const pid_t pid = fork();
 
 
2940
    if(pid == 0){               /* Child */
 
 
2941
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
 
2942
        _exit(EXIT_FAILURE);
 
 
2944
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
 
2945
        _exit(EXIT_FAILURE);
 
 
2951
  const pid_t pid = create_failing_process();
 
 
2952
  g_assert_true(pid != -1);
 
 
2954
  __attribute__((cleanup(string_set_clear)))
 
 
2955
    string_set cancelled_filenames = {};
 
 
2956
  __attribute__((cleanup(cleanup_close)))
 
 
2957
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
2958
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
2959
  __attribute__((cleanup(cleanup_queue)))
 
 
2960
    task_queue *queue = create_queue();
 
 
2961
  g_assert_nonnull(queue);
 
 
2962
  g_assert_true(add_to_queue(queue, (task_context){
 
 
2963
        .func=wait_for_mandos_client_exit,
 
 
2965
        .mandos_client_exited=&mandos_client_exited,
 
 
2966
        .quit_now=&quit_now,
 
 
2969
  g_assert_true(sigismember(&fixture->orig_sigmask, SIGCHLD) == 0);
 
 
2971
  __attribute__((cleanup(cleanup_close)))
 
 
2972
    const int devnull_fd = open("/dev/null",
 
 
2973
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
 
2974
  g_assert_cmpint(devnull_fd, >=, 0);
 
 
2975
  __attribute__((cleanup(cleanup_close)))
 
 
2976
    const int real_stderr_fd = dup(STDERR_FILENO);
 
 
2977
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
 
2979
  struct timespec starttime, currtime;
 
 
2980
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
2982
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
2983
    dup2(devnull_fd, STDERR_FILENO);
 
 
2984
    const bool success = run_queue(&queue, &cancelled_filenames,
 
 
2986
    dup2(real_stderr_fd, STDERR_FILENO);
 
 
2991
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
2992
  } while((not mandos_client_exited)
 
 
2994
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
2996
  g_assert_true(quit_now);
 
 
2997
  g_assert_true(mandos_client_exited);
 
 
2998
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3002
void test_wait_for_mandos_client_exit_killed(test_fixture *fixture,
 
 
3003
                                             __attribute__((unused))
 
 
3004
                                             gconstpointer user_data){
 
 
3005
  bool mandos_client_exited = false;
 
 
3006
  bool quit_now = false;
 
 
3008
  pid_t create_killed_process(void){
 
 
3009
    const pid_t pid = fork();
 
 
3010
    if(pid == 0){               /* Child */
 
 
3011
      if(not restore_signal_handler(&fixture->orig_sigaction)){
 
 
3012
        _exit(EXIT_FAILURE);
 
 
3014
      if(not restore_sigmask(&fixture->orig_sigmask)){
 
 
3015
        _exit(EXIT_FAILURE);
 
 
3024
  const pid_t pid = create_killed_process();
 
 
3025
  g_assert_true(pid != -1);
 
 
3027
  __attribute__((cleanup(string_set_clear)))
 
 
3028
    string_set cancelled_filenames = {};
 
 
3029
  __attribute__((cleanup(cleanup_close)))
 
 
3030
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3031
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3032
  __attribute__((cleanup(cleanup_queue)))
 
 
3033
    task_queue *queue = create_queue();
 
 
3034
  g_assert_nonnull(queue);
 
 
3035
  g_assert_true(add_to_queue(queue, (task_context){
 
 
3036
        .func=wait_for_mandos_client_exit,
 
 
3038
        .mandos_client_exited=&mandos_client_exited,
 
 
3039
        .quit_now=&quit_now,
 
 
3042
  __attribute__((cleanup(cleanup_close)))
 
 
3043
    const int devnull_fd = open("/dev/null",
 
 
3044
                                O_WRONLY | O_CLOEXEC, O_NOCTTY);
 
 
3045
  g_assert_cmpint(devnull_fd, >=, 0);
 
 
3046
  __attribute__((cleanup(cleanup_close)))
 
 
3047
    const int real_stderr_fd = dup(STDERR_FILENO);
 
 
3048
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
 
3050
  struct timespec starttime, currtime;
 
 
3051
  g_assert_true(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);
 
 
3053
    g_assert_true(wait_for_event(epoll_fd, queue->next_run, 0));
 
 
3054
    dup2(devnull_fd, STDERR_FILENO);
 
 
3055
    const bool success = run_queue(&queue, &cancelled_filenames,
 
 
3057
    dup2(real_stderr_fd, STDERR_FILENO);
 
 
3062
    g_assert_true(clock_gettime(CLOCK_MONOTONIC, &currtime) == 0);
 
 
3063
  } while((not mandos_client_exited)
 
 
3065
          and ((currtime.tv_sec - starttime.tv_sec) < 10));
 
 
3067
  g_assert_true(mandos_client_exited);
 
 
3068
  g_assert_true(quit_now);
 
 
3069
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3072
static bool epoll_set_does_not_contain(int, int);
 
 
3075
void test_read_mandos_client_output_readerror(__attribute__((unused))
 
 
3076
                                              test_fixture *fixture,
 
 
3077
                                              __attribute__((unused))
 
 
3080
  __attribute__((cleanup(cleanup_close)))
 
 
3081
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3082
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3084
  __attribute__((cleanup(cleanup_buffer)))
 
 
3085
    buffer password = {};
 
 
3087
  /* Reading /proc/self/mem from offset 0 will always give EIO */
 
 
3088
  const int fd = open("/proc/self/mem",
 
 
3089
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
 
 
3091
  bool password_is_read = false;
 
 
3092
  bool quit_now = false;
 
 
3093
  __attribute__((cleanup(cleanup_queue)))
 
 
3094
    task_queue *queue = create_queue();
 
 
3095
  g_assert_nonnull(queue);
 
 
3097
  task_context task = {
 
 
3098
    .func=read_mandos_client_output,
 
 
3101
    .password=&password,
 
 
3102
    .password_is_read=&password_is_read,
 
 
3103
    .quit_now=&quit_now,
 
 
3105
  run_task_with_stderr_to_dev_null(task, queue);
 
 
3106
  g_assert_false(password_is_read);
 
 
3107
  g_assert_cmpint((int)password.length, ==, 0);
 
 
3108
  g_assert_true(quit_now);
 
 
3109
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3111
  g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
 
 
3113
  g_assert_cmpint(close(fd), ==, -1);
 
 
3116
static bool epoll_set_does_not_contain(int epoll_fd, int fd){
 
 
3117
  return not epoll_set_contains(epoll_fd, fd, 0);
 
 
3121
void test_read_mandos_client_output_nodata(__attribute__((unused))
 
 
3122
                                           test_fixture *fixture,
 
 
3123
                                           __attribute__((unused))
 
 
3124
                                           gconstpointer user_data){
 
 
3125
  __attribute__((cleanup(cleanup_close)))
 
 
3126
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3127
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3130
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3132
  __attribute__((cleanup(cleanup_buffer)))
 
 
3133
    buffer password = {};
 
 
3135
  bool password_is_read = false;
 
 
3136
  bool quit_now = false;
 
 
3137
  __attribute__((cleanup(cleanup_queue)))
 
 
3138
    task_queue *queue = create_queue();
 
 
3139
  g_assert_nonnull(queue);
 
 
3141
  task_context task = {
 
 
3142
    .func=read_mandos_client_output,
 
 
3145
    .password=&password,
 
 
3146
    .password_is_read=&password_is_read,
 
 
3147
    .quit_now=&quit_now,
 
 
3149
  task.func(task, queue);
 
 
3150
  g_assert_false(password_is_read);
 
 
3151
  g_assert_cmpint((int)password.length, ==, 0);
 
 
3152
  g_assert_false(quit_now);
 
 
3153
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
3155
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
3156
        .func=read_mandos_client_output,
 
 
3159
        .password=&password,
 
 
3160
        .password_is_read=&password_is_read,
 
 
3161
        .quit_now=&quit_now,
 
 
3164
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
3165
                                   EPOLLIN | EPOLLRDHUP));
 
 
3167
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
3170
static void test_read_mandos_client_output_eof(__attribute__((unused))
 
 
3171
                                               test_fixture *fixture,
 
 
3172
                                               __attribute__((unused))
 
 
3175
  __attribute__((cleanup(cleanup_close)))
 
 
3176
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3177
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3180
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3181
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
3183
  __attribute__((cleanup(cleanup_buffer)))
 
 
3184
    buffer password = {};
 
 
3186
  bool password_is_read = false;
 
 
3187
  bool quit_now = false;
 
 
3188
  __attribute__((cleanup(cleanup_queue)))
 
 
3189
    task_queue *queue = create_queue();
 
 
3190
  g_assert_nonnull(queue);
 
 
3192
  task_context task = {
 
 
3193
    .func=read_mandos_client_output,
 
 
3196
    .password=&password,
 
 
3197
    .password_is_read=&password_is_read,
 
 
3198
    .quit_now=&quit_now,
 
 
3200
  task.func(task, queue);
 
 
3201
  g_assert_true(password_is_read);
 
 
3202
  g_assert_cmpint((int)password.length, ==, 0);
 
 
3203
  g_assert_false(quit_now);
 
 
3204
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3206
  g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
 
 
3208
  g_assert_cmpint(close(pipefds[0]), ==, -1);
 
 
3212
void test_read_mandos_client_output_once(__attribute__((unused))
 
 
3213
                                         test_fixture *fixture,
 
 
3214
                                         __attribute__((unused))
 
 
3215
                                         gconstpointer user_data){
 
 
3216
  __attribute__((cleanup(cleanup_close)))
 
 
3217
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3218
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3221
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3223
  const char dummy_test_password[] = "dummy test password";
 
 
3224
  /* Start with a pre-allocated buffer */
 
 
3225
  __attribute__((cleanup(cleanup_buffer)))
 
 
3227
    .data=malloc(sizeof(dummy_test_password)),
 
 
3229
    .allocated=sizeof(dummy_test_password),
 
 
3231
  g_assert_nonnull(password.data);
 
 
3232
  if(mlock(password.data, password.allocated) != 0){
 
 
3233
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
3236
  bool password_is_read = false;
 
 
3237
  bool quit_now = false;
 
 
3238
  __attribute__((cleanup(cleanup_queue)))
 
 
3239
    task_queue *queue = create_queue();
 
 
3240
  g_assert_nonnull(queue);
 
 
3242
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
 
3243
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
 
3244
                             sizeof(dummy_test_password)),
 
 
3245
                  ==, (int)sizeof(dummy_test_password));
 
 
3247
  task_context task = {
 
 
3248
    .func=read_mandos_client_output,
 
 
3251
    .password=&password,
 
 
3252
    .password_is_read=&password_is_read,
 
 
3253
    .quit_now=&quit_now,
 
 
3255
  task.func(task, queue);
 
 
3257
  g_assert_false(password_is_read);
 
 
3258
  g_assert_cmpint((int)password.length, ==,
 
 
3259
                  (int)sizeof(dummy_test_password));
 
 
3260
  g_assert_nonnull(password.data);
 
 
3261
  g_assert_cmpint(memcmp(password.data, dummy_test_password,
 
 
3262
                         sizeof(dummy_test_password)), ==, 0);
 
 
3264
  g_assert_false(quit_now);
 
 
3265
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
3267
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
3268
        .func=read_mandos_client_output,
 
 
3271
        .password=&password,
 
 
3272
        .password_is_read=&password_is_read,
 
 
3273
        .quit_now=&quit_now,
 
 
3276
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
3277
                                   EPOLLIN | EPOLLRDHUP));
 
 
3279
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
3283
void test_read_mandos_client_output_malloc(__attribute__((unused))
 
 
3284
                                           test_fixture *fixture,
 
 
3285
                                           __attribute__((unused))
 
 
3286
                                           gconstpointer user_data){
 
 
3287
  __attribute__((cleanup(cleanup_close)))
 
 
3288
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3289
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3292
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3294
  const char dummy_test_password[] = "dummy test password";
 
 
3295
  /* Start with an empty buffer */
 
 
3296
  __attribute__((cleanup(cleanup_buffer)))
 
 
3297
    buffer password = {};
 
 
3299
  bool password_is_read = false;
 
 
3300
  bool quit_now = false;
 
 
3301
  __attribute__((cleanup(cleanup_queue)))
 
 
3302
    task_queue *queue = create_queue();
 
 
3303
  g_assert_nonnull(queue);
 
 
3305
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
 
3306
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
 
3307
                             sizeof(dummy_test_password)),
 
 
3308
                  ==, (int)sizeof(dummy_test_password));
 
 
3310
  task_context task = {
 
 
3311
    .func=read_mandos_client_output,
 
 
3314
    .password=&password,
 
 
3315
    .password_is_read=&password_is_read,
 
 
3316
    .quit_now=&quit_now,
 
 
3318
  task.func(task, queue);
 
 
3320
  g_assert_false(password_is_read);
 
 
3321
  g_assert_cmpint((int)password.length, ==,
 
 
3322
                  (int)sizeof(dummy_test_password));
 
 
3323
  g_assert_nonnull(password.data);
 
 
3324
  g_assert_cmpint(memcmp(password.data, dummy_test_password,
 
 
3325
                         sizeof(dummy_test_password)), ==, 0);
 
 
3327
  g_assert_false(quit_now);
 
 
3328
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
3330
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
3331
        .func=read_mandos_client_output,
 
 
3334
        .password=&password,
 
 
3335
        .password_is_read=&password_is_read,
 
 
3336
        .quit_now=&quit_now,
 
 
3339
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
3340
                                   EPOLLIN | EPOLLRDHUP));
 
 
3342
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
3346
void test_read_mandos_client_output_append(__attribute__((unused))
 
 
3347
                                           test_fixture *fixture,
 
 
3348
                                           __attribute__((unused))
 
 
3349
                                           gconstpointer user_data){
 
 
3350
  __attribute__((cleanup(cleanup_close)))
 
 
3351
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3352
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3355
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
3357
  const char dummy_test_password[] = "dummy test password";
 
 
3358
  __attribute__((cleanup(cleanup_buffer)))
 
 
3360
    .data=malloc(PIPE_BUF),
 
 
3362
    .allocated=PIPE_BUF,
 
 
3364
  g_assert_nonnull(password.data);
 
 
3365
  if(mlock(password.data, password.allocated) != 0){
 
 
3366
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
3369
  memset(password.data, 'x', PIPE_BUF);
 
 
3370
  char password_expected[PIPE_BUF];
 
 
3371
  memcpy(password_expected, password.data, PIPE_BUF);
 
 
3373
  bool password_is_read = false;
 
 
3374
  bool quit_now = false;
 
 
3375
  __attribute__((cleanup(cleanup_queue)))
 
 
3376
    task_queue *queue = create_queue();
 
 
3377
  g_assert_nonnull(queue);
 
 
3379
  g_assert_true(sizeof(dummy_test_password) <= PIPE_BUF);
 
 
3380
  g_assert_cmpint((int)write(pipefds[1], dummy_test_password,
 
 
3381
                             sizeof(dummy_test_password)),
 
 
3382
                  ==, (int)sizeof(dummy_test_password));
 
 
3384
  task_context task = {
 
 
3385
    .func=read_mandos_client_output,
 
 
3388
    .password=&password,
 
 
3389
    .password_is_read=&password_is_read,
 
 
3390
    .quit_now=&quit_now,
 
 
3392
  task.func(task, queue);
 
 
3394
  g_assert_false(password_is_read);
 
 
3395
  g_assert_cmpint((int)password.length, ==,
 
 
3396
                  PIPE_BUF + sizeof(dummy_test_password));
 
 
3397
  g_assert_nonnull(password.data);
 
 
3398
  g_assert_cmpint(memcmp(password_expected, password.data, PIPE_BUF),
 
 
3400
  g_assert_cmpint(memcmp(password.data + PIPE_BUF,
 
 
3401
                         dummy_test_password,
 
 
3402
                         sizeof(dummy_test_password)), ==, 0);
 
 
3403
  g_assert_false(quit_now);
 
 
3404
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
3406
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
3407
        .func=read_mandos_client_output,
 
 
3410
        .password=&password,
 
 
3411
        .password_is_read=&password_is_read,
 
 
3412
        .quit_now=&quit_now,
 
 
3415
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
3416
                                   EPOLLIN | EPOLLRDHUP));
 
 
3419
static char *make_temporary_directory(void);
 
 
3421
static void test_add_inotify_dir_watch(__attribute__((unused))
 
 
3422
                                       test_fixture *fixture,
 
 
3423
                                       __attribute__((unused))
 
 
3424
                                       gconstpointer user_data){
 
 
3425
  __attribute__((cleanup(cleanup_close)))
 
 
3426
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3427
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3428
  __attribute__((cleanup(cleanup_queue)))
 
 
3429
    task_queue *queue = create_queue();
 
 
3430
  g_assert_nonnull(queue);
 
 
3431
  __attribute__((cleanup(string_set_clear)))
 
 
3432
    string_set cancelled_filenames = {};
 
 
3433
  const mono_microsecs current_time = 0;
 
 
3435
  bool quit_now = false;
 
 
3436
  buffer password = {};
 
 
3437
  bool mandos_client_exited = false;
 
 
3438
  bool password_is_read = false;
 
 
3440
  __attribute__((cleanup(cleanup_string)))
 
 
3441
    char *tempdir = make_temporary_directory();
 
 
3442
  g_assert_nonnull(tempdir);
 
 
3444
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3446
                                      &cancelled_filenames,
 
 
3448
                                      &mandos_client_exited,
 
 
3449
                                      &password_is_read));
 
 
3451
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3453
  const task_context *const added_read_task
 
 
3454
    = find_matching_task(queue, (task_context){
 
 
3455
        .func=read_inotify_event,
 
 
3457
        .quit_now=&quit_now,
 
 
3458
        .password=&password,
 
 
3460
        .cancelled_filenames=&cancelled_filenames,
 
 
3461
        .current_time=¤t_time,
 
 
3462
        .mandos_client_exited=&mandos_client_exited,
 
 
3463
        .password_is_read=&password_is_read,
 
 
3465
  g_assert_nonnull(added_read_task);
 
 
3467
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3468
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3469
  g_assert_true(epoll_set_contains(added_read_task->epoll_fd,
 
 
3470
                                   added_read_task->fd,
 
 
3471
                                   EPOLLIN | EPOLLRDHUP));
 
 
3473
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3476
static char *make_temporary_directory(void){
 
 
3477
  char *name = strdup("/tmp/mandosXXXXXX");
 
 
3478
  g_assert_nonnull(name);
 
 
3479
  char *result = mkdtemp(name);
 
 
3486
static void test_add_inotify_dir_watch_fail(__attribute__((unused))
 
 
3487
                                            test_fixture *fixture,
 
 
3488
                                            __attribute__((unused))
 
 
3489
                                            gconstpointer user_data){
 
 
3490
  __attribute__((cleanup(cleanup_close)))
 
 
3491
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3492
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3493
  __attribute__((cleanup(cleanup_queue)))
 
 
3494
    task_queue *queue = create_queue();
 
 
3495
  g_assert_nonnull(queue);
 
 
3496
  __attribute__((cleanup(string_set_clear)))
 
 
3497
    string_set cancelled_filenames = {};
 
 
3498
  const mono_microsecs current_time = 0;
 
 
3500
  bool quit_now = false;
 
 
3501
  buffer password = {};
 
 
3502
  bool mandos_client_exited = false;
 
 
3503
  bool password_is_read = false;
 
 
3505
  const char nonexistent_dir[] = "/nonexistent";
 
 
3507
  FILE *real_stderr = stderr;
 
 
3508
  FILE *devnull = fopen("/dev/null", "we");
 
 
3509
  g_assert_nonnull(devnull);
 
 
3511
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3512
                                       &password, nonexistent_dir,
 
 
3513
                                       &cancelled_filenames,
 
 
3515
                                       &mandos_client_exited,
 
 
3516
                                       &password_is_read));
 
 
3517
  stderr = real_stderr;
 
 
3518
  g_assert_cmpint(fclose(devnull), ==, 0);
 
 
3520
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3523
static void test_add_inotify_dir_watch_nondir(__attribute__((unused))
 
 
3524
                                              test_fixture *fixture,
 
 
3525
                                            __attribute__((unused))
 
 
3528
  __attribute__((cleanup(cleanup_close)))
 
 
3529
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3530
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3531
  __attribute__((cleanup(cleanup_queue)))
 
 
3532
    task_queue *queue = create_queue();
 
 
3533
  g_assert_nonnull(queue);
 
 
3534
  __attribute__((cleanup(string_set_clear)))
 
 
3535
    string_set cancelled_filenames = {};
 
 
3536
  const mono_microsecs current_time = 0;
 
 
3538
  bool quit_now = false;
 
 
3539
  buffer password = {};
 
 
3540
  bool mandos_client_exited = false;
 
 
3541
  bool password_is_read = false;
 
 
3543
  const char not_a_directory[] = "/dev/tty";
 
 
3545
  FILE *real_stderr = stderr;
 
 
3546
  FILE *devnull = fopen("/dev/null", "we");
 
 
3547
  g_assert_nonnull(devnull);
 
 
3549
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3550
                                       &password, not_a_directory,
 
 
3551
                                       &cancelled_filenames,
 
 
3553
                                       &mandos_client_exited,
 
 
3554
                                       &password_is_read));
 
 
3555
  stderr = real_stderr;
 
 
3556
  g_assert_cmpint(fclose(devnull), ==, 0);
 
 
3558
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
3561
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
 
 
3562
                                              test_fixture *fixture,
 
 
3563
                                              __attribute__((unused))
 
 
3566
  __attribute__((cleanup(cleanup_close)))
 
 
3567
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3568
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3569
  __attribute__((cleanup(cleanup_queue)))
 
 
3570
    task_queue *queue = create_queue();
 
 
3571
  g_assert_nonnull(queue);
 
 
3572
  __attribute__((cleanup(string_set_clear)))
 
 
3573
    string_set cancelled_filenames = {};
 
 
3574
  const mono_microsecs current_time = 0;
 
 
3576
  bool quit_now = false;
 
 
3577
  buffer password = {};
 
 
3578
  bool mandos_client_exited = false;
 
 
3579
  bool password_is_read = false;
 
 
3581
  __attribute__((cleanup(cleanup_string)))
 
 
3582
    char *tempdir = make_temporary_directory();
 
 
3583
  g_assert_nonnull(tempdir);
 
 
3585
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3587
                                      &cancelled_filenames,
 
 
3589
                                      &mandos_client_exited,
 
 
3590
                                      &password_is_read));
 
 
3592
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3594
  const task_context *const added_read_task
 
 
3595
    = find_matching_task(queue,
 
 
3596
                         (task_context){ .func=read_inotify_event });
 
 
3597
  g_assert_nonnull(added_read_task);
 
 
3599
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3600
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3602
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3603
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3605
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3606
  g_assert_nonnull(ievent);
 
 
3608
  g_assert_cmpint(read(added_read_task->fd, ievent, ievent_size), ==,
 
 
3610
  g_assert_cmpint(errno, ==, EAGAIN);
 
 
3614
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3617
static char *make_temporary_file_in_directory(const char
 
 
3621
void test_add_inotify_dir_watch_IN_CLOSE_WRITE(__attribute__((unused))
 
 
3622
                                               test_fixture *fixture,
 
 
3623
                                               __attribute__((unused))
 
 
3626
  __attribute__((cleanup(cleanup_close)))
 
 
3627
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3628
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3629
  __attribute__((cleanup(cleanup_queue)))
 
 
3630
    task_queue *queue = create_queue();
 
 
3631
  g_assert_nonnull(queue);
 
 
3632
  __attribute__((cleanup(string_set_clear)))
 
 
3633
    string_set cancelled_filenames = {};
 
 
3634
  const mono_microsecs current_time = 0;
 
 
3636
  bool quit_now = false;
 
 
3637
  buffer password = {};
 
 
3638
  bool mandos_client_exited = false;
 
 
3639
  bool password_is_read = false;
 
 
3641
  __attribute__((cleanup(cleanup_string)))
 
 
3642
    char *tempdir = make_temporary_directory();
 
 
3643
  g_assert_nonnull(tempdir);
 
 
3645
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3647
                                      &cancelled_filenames,
 
 
3649
                                      &mandos_client_exited,
 
 
3650
                                      &password_is_read));
 
 
3652
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3654
  const task_context *const added_read_task
 
 
3655
    = find_matching_task(queue,
 
 
3656
                         (task_context){ .func=read_inotify_event });
 
 
3657
  g_assert_nonnull(added_read_task);
 
 
3659
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3660
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3662
  __attribute__((cleanup(cleanup_string)))
 
 
3663
    char *filename = make_temporary_file_in_directory(tempdir);
 
 
3664
  g_assert_nonnull(filename);
 
 
3666
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3667
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3669
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3670
  g_assert_nonnull(ievent);
 
 
3672
  ssize_t read_size = 0;
 
 
3673
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3675
  g_assert_cmpint((int)read_size, >, 0);
 
 
3676
  g_assert_true(ievent->mask & IN_CLOSE_WRITE);
 
 
3677
  g_assert_cmpstr(ievent->name, ==, basename(filename));
 
 
3681
  g_assert_cmpint(unlink(filename), ==, 0);
 
 
3682
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3685
static char *make_temporary_prefixed_file_in_directory(const char
 
 
3689
  char *filename = NULL;
 
 
3690
  g_assert_cmpint(asprintf(&filename, "%s/%sXXXXXX", dir, prefix),
 
 
3692
  g_assert_nonnull(filename);
 
 
3693
  const int fd = mkostemp(filename, O_CLOEXEC);
 
 
3694
  g_assert_cmpint(fd, >=, 0);
 
 
3695
  g_assert_cmpint(close(fd), ==, 0);
 
 
3699
static char *make_temporary_file_in_directory(const char
 
 
3701
  return make_temporary_prefixed_file_in_directory("temp", dir);
 
 
3705
void test_add_inotify_dir_watch_IN_MOVED_TO(__attribute__((unused))
 
 
3706
                                            test_fixture *fixture,
 
 
3707
                                            __attribute__((unused))
 
 
3708
                                            gconstpointer user_data){
 
 
3709
  __attribute__((cleanup(cleanup_close)))
 
 
3710
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3711
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3712
  __attribute__((cleanup(cleanup_queue)))
 
 
3713
    task_queue *queue = create_queue();
 
 
3714
  g_assert_nonnull(queue);
 
 
3715
  __attribute__((cleanup(string_set_clear)))
 
 
3716
    string_set cancelled_filenames = {};
 
 
3717
  const mono_microsecs current_time = 0;
 
 
3719
  bool quit_now = false;
 
 
3720
  buffer password = {};
 
 
3721
  bool mandos_client_exited = false;
 
 
3722
  bool password_is_read = false;
 
 
3724
  __attribute__((cleanup(cleanup_string)))
 
 
3725
    char *watchdir = make_temporary_directory();
 
 
3726
  g_assert_nonnull(watchdir);
 
 
3728
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3729
                                      &password, watchdir,
 
 
3730
                                      &cancelled_filenames,
 
 
3732
                                      &mandos_client_exited,
 
 
3733
                                      &password_is_read));
 
 
3735
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3737
  const task_context *const added_read_task
 
 
3738
    = find_matching_task(queue,
 
 
3739
                         (task_context){ .func=read_inotify_event });
 
 
3740
  g_assert_nonnull(added_read_task);
 
 
3742
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3743
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3745
  char *sourcedir = make_temporary_directory();
 
 
3746
  g_assert_nonnull(sourcedir);
 
 
3748
  __attribute__((cleanup(cleanup_string)))
 
 
3749
    char *filename = make_temporary_file_in_directory(sourcedir);
 
 
3750
  g_assert_nonnull(filename);
 
 
3752
  __attribute__((cleanup(cleanup_string)))
 
 
3753
    char *targetfilename = NULL;
 
 
3754
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", watchdir,
 
 
3755
                           basename(filename)), >, 0);
 
 
3756
  g_assert_nonnull(targetfilename);
 
 
3758
  g_assert_cmpint(rename(filename, targetfilename), ==, 0);
 
 
3759
  g_assert_cmpint(rmdir(sourcedir), ==, 0);
 
 
3762
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3763
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3765
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3766
  g_assert_nonnull(ievent);
 
 
3768
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3770
  g_assert_cmpint((int)read_size, >, 0);
 
 
3771
  g_assert_true(ievent->mask & IN_MOVED_TO);
 
 
3772
  g_assert_cmpstr(ievent->name, ==, basename(targetfilename));
 
 
3776
  g_assert_cmpint(unlink(targetfilename), ==, 0);
 
 
3777
  g_assert_cmpint(rmdir(watchdir), ==, 0);
 
 
3781
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
 
 
3782
                                              test_fixture *fixture,
 
 
3783
                                              __attribute__((unused))
 
 
3786
  __attribute__((cleanup(cleanup_close)))
 
 
3787
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3788
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3789
  __attribute__((cleanup(cleanup_queue)))
 
 
3790
    task_queue *queue = create_queue();
 
 
3791
  g_assert_nonnull(queue);
 
 
3792
  __attribute__((cleanup(string_set_clear)))
 
 
3793
    string_set cancelled_filenames = {};
 
 
3794
  const mono_microsecs current_time = 0;
 
 
3796
  bool quit_now = false;
 
 
3797
  buffer password = {};
 
 
3798
  bool mandos_client_exited = false;
 
 
3799
  bool password_is_read = false;
 
 
3801
  __attribute__((cleanup(cleanup_string)))
 
 
3802
    char *tempdir = make_temporary_directory();
 
 
3803
  g_assert_nonnull(tempdir);
 
 
3805
  __attribute__((cleanup(cleanup_string)))
 
 
3806
    char *tempfilename = make_temporary_file_in_directory(tempdir);
 
 
3807
  g_assert_nonnull(tempfilename);
 
 
3809
  __attribute__((cleanup(cleanup_string)))
 
 
3810
    char *targetdir = make_temporary_directory();
 
 
3811
  g_assert_nonnull(targetdir);
 
 
3813
  __attribute__((cleanup(cleanup_string)))
 
 
3814
    char *targetfilename = NULL;
 
 
3815
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
 
 
3816
                           basename(tempfilename)), >, 0);
 
 
3817
  g_assert_nonnull(targetfilename);
 
 
3819
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3821
                                      &cancelled_filenames,
 
 
3823
                                      &mandos_client_exited,
 
 
3824
                                      &password_is_read));
 
 
3826
  g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
 
 
3828
  const task_context *const added_read_task
 
 
3829
    = find_matching_task(queue,
 
 
3830
                         (task_context){ .func=read_inotify_event });
 
 
3831
  g_assert_nonnull(added_read_task);
 
 
3833
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3834
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3836
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3837
  g_assert_nonnull(ievent);
 
 
3839
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3841
  g_assert_cmpint((int)read_size, >, 0);
 
 
3842
  g_assert_true(ievent->mask & IN_MOVED_FROM);
 
 
3843
  g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
 
 
3847
  g_assert_cmpint(unlink(targetfilename), ==, 0);
 
 
3848
  g_assert_cmpint(rmdir(targetdir), ==, 0);
 
 
3849
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3853
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
 
 
3854
                                          test_fixture *fixture,
 
 
3855
                                          __attribute__((unused))
 
 
3856
                                          gconstpointer user_data){
 
 
3857
  __attribute__((cleanup(cleanup_close)))
 
 
3858
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3859
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3860
  __attribute__((cleanup(cleanup_queue)))
 
 
3861
    task_queue *queue = create_queue();
 
 
3862
  g_assert_nonnull(queue);
 
 
3863
  __attribute__((cleanup(string_set_clear)))
 
 
3864
    string_set cancelled_filenames = {};
 
 
3865
  const mono_microsecs current_time = 0;
 
 
3867
  bool quit_now = false;
 
 
3868
  buffer password = {};
 
 
3869
  bool mandos_client_exited = false;
 
 
3870
  bool password_is_read = false;
 
 
3872
  __attribute__((cleanup(cleanup_string)))
 
 
3873
    char *tempdir = make_temporary_directory();
 
 
3874
  g_assert_nonnull(tempdir);
 
 
3876
  __attribute__((cleanup(cleanup_string)))
 
 
3877
    char *tempfile = make_temporary_file_in_directory(tempdir);
 
 
3878
  g_assert_nonnull(tempfile);
 
 
3880
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3882
                                      &cancelled_filenames,
 
 
3884
                                      &mandos_client_exited,
 
 
3885
                                      &password_is_read));
 
 
3886
  g_assert_cmpint(unlink(tempfile), ==, 0);
 
 
3888
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3890
  const task_context *const added_read_task
 
 
3891
    = find_matching_task(queue,
 
 
3892
                         (task_context){ .func=read_inotify_event });
 
 
3893
  g_assert_nonnull(added_read_task);
 
 
3895
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3896
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3898
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3899
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3901
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3902
  g_assert_nonnull(ievent);
 
 
3904
  ssize_t read_size = 0;
 
 
3905
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3907
  g_assert_cmpint((int)read_size, >, 0);
 
 
3908
  g_assert_true(ievent->mask & IN_DELETE);
 
 
3909
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
 
 
3913
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3917
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
 
 
3918
                                               test_fixture *fixture,
 
 
3919
                                               __attribute__((unused))
 
 
3922
  __attribute__((cleanup(cleanup_close)))
 
 
3923
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3924
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
3925
  __attribute__((cleanup(cleanup_queue)))
 
 
3926
    task_queue *queue = create_queue();
 
 
3927
  g_assert_nonnull(queue);
 
 
3928
  __attribute__((cleanup(string_set_clear)))
 
 
3929
    string_set cancelled_filenames = {};
 
 
3930
  const mono_microsecs current_time = 0;
 
 
3932
  bool quit_now = false;
 
 
3933
  buffer password = {};
 
 
3934
  bool mandos_client_exited = false;
 
 
3935
  bool password_is_read = false;
 
 
3937
  __attribute__((cleanup(cleanup_string)))
 
 
3938
    char *tempdir = make_temporary_directory();
 
 
3939
  g_assert_nonnull(tempdir);
 
 
3941
  __attribute__((cleanup(cleanup_string)))
 
 
3942
    char *tempfile = make_temporary_file_in_directory(tempdir);
 
 
3943
  g_assert_nonnull(tempfile);
 
 
3944
  int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
 
 
3946
  g_assert_cmpint(tempfile_fd, >, 2);
 
 
3948
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
 
3950
                                      &cancelled_filenames,
 
 
3952
                                      &mandos_client_exited,
 
 
3953
                                      &password_is_read));
 
 
3954
  g_assert_cmpint(unlink(tempfile), ==, 0);
 
 
3956
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
 
3958
  const task_context *const added_read_task
 
 
3959
    = find_matching_task(queue,
 
 
3960
                         (task_context){ .func=read_inotify_event });
 
 
3961
  g_assert_nonnull(added_read_task);
 
 
3963
  g_assert_cmpint(added_read_task->fd, >, 2);
 
 
3964
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
 
3966
  /* "sufficient to read at least one event." - inotify(7) */
 
 
3967
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
3969
  struct inotify_event *ievent = malloc(ievent_size);
 
 
3970
  g_assert_nonnull(ievent);
 
 
3972
  ssize_t read_size = 0;
 
 
3973
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3975
  g_assert_cmpint((int)read_size, >, 0);
 
 
3976
  g_assert_true(ievent->mask & IN_DELETE);
 
 
3977
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
 
 
3979
  g_assert_cmpint(close(tempfile_fd), ==, 0);
 
 
3981
  /* IN_EXCL_UNLINK should make the closing of the previously unlinked
 
 
3982
     file not appear as an ievent, so we should not see it now. */
 
 
3983
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
 
3984
  g_assert_cmpint((int)read_size, ==, -1);
 
 
3985
  g_assert_true(errno == EAGAIN);
 
 
3989
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
3992
static void test_read_inotify_event_readerror(__attribute__((unused))
 
 
3993
                                              test_fixture *fixture,
 
 
3994
                                              __attribute__((unused))
 
 
3997
  __attribute__((cleanup(cleanup_close)))
 
 
3998
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
3999
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4000
  const mono_microsecs current_time = 0;
 
 
4002
  /* Reading /proc/self/mem from offset 0 will always result in EIO */
 
 
4003
  const int fd = open("/proc/self/mem",
 
 
4004
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
 
 
4006
  bool quit_now = false;
 
 
4007
  __attribute__((cleanup(cleanup_queue)))
 
 
4008
    task_queue *queue = create_queue();
 
 
4009
  g_assert_nonnull(queue);
 
 
4011
  task_context task = {
 
 
4012
    .func=read_inotify_event,
 
 
4015
    .quit_now=&quit_now,
 
 
4016
    .filename=strdup("/nonexistent"),
 
 
4017
    .cancelled_filenames = &(string_set){},
 
 
4019
    .current_time=¤t_time,
 
 
4021
  g_assert_nonnull(task.filename);
 
 
4022
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4023
  g_assert_true(quit_now);
 
 
4024
  g_assert_true(queue->next_run == 0);
 
 
4025
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4027
  g_assert_true(epoll_set_does_not_contain(epoll_fd, fd));
 
 
4029
  g_assert_cmpint(close(fd), ==, -1);
 
 
4032
static void test_read_inotify_event_bad_epoll(__attribute__((unused))
 
 
4033
                                              test_fixture *fixture,
 
 
4034
                                              __attribute__((unused))
 
 
4037
  const mono_microsecs current_time = 17;
 
 
4040
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4041
  const int epoll_fd = pipefds[0]; /* This will obviously fail */
 
 
4043
  bool quit_now = false;
 
 
4044
  buffer password = {};
 
 
4045
  bool mandos_client_exited = false;
 
 
4046
  bool password_is_read = false;
 
 
4047
  __attribute__((cleanup(cleanup_queue)))
 
 
4048
    task_queue *queue = create_queue();
 
 
4049
  g_assert_nonnull(queue);
 
 
4051
  task_context task = {
 
 
4052
    .func=read_inotify_event,
 
 
4055
    .quit_now=&quit_now,
 
 
4056
    .password=&password,
 
 
4057
    .filename=strdup("/nonexistent"),
 
 
4058
    .cancelled_filenames = &(string_set){},
 
 
4060
    .current_time=¤t_time,
 
 
4061
    .mandos_client_exited=&mandos_client_exited,
 
 
4062
    .password_is_read=&password_is_read,
 
 
4064
  g_assert_nonnull(task.filename);
 
 
4065
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4067
  g_assert_nonnull(find_matching_task(queue, task));
 
 
4068
  g_assert_true(queue->next_run == 1000000 + current_time);
 
 
4070
  g_assert_cmpint(close(pipefds[0]), ==, 0);
 
 
4071
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4074
static void test_read_inotify_event_nodata(__attribute__((unused))
 
 
4075
                                           test_fixture *fixture,
 
 
4076
                                           __attribute__((unused))
 
 
4077
                                           gconstpointer user_data){
 
 
4078
  __attribute__((cleanup(cleanup_close)))
 
 
4079
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4080
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4081
  const mono_microsecs current_time = 0;
 
 
4084
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4086
  bool quit_now = false;
 
 
4087
  buffer password = {};
 
 
4088
  bool mandos_client_exited = false;
 
 
4089
  bool password_is_read = false;
 
 
4090
  __attribute__((cleanup(cleanup_queue)))
 
 
4091
    task_queue *queue = create_queue();
 
 
4092
  g_assert_nonnull(queue);
 
 
4094
  task_context task = {
 
 
4095
    .func=read_inotify_event,
 
 
4098
    .quit_now=&quit_now,
 
 
4099
    .password=&password,
 
 
4100
    .filename=strdup("/nonexistent"),
 
 
4101
    .cancelled_filenames = &(string_set){},
 
 
4103
    .current_time=¤t_time,
 
 
4104
    .mandos_client_exited=&mandos_client_exited,
 
 
4105
    .password_is_read=&password_is_read,
 
 
4107
  g_assert_nonnull(task.filename);
 
 
4108
  task.func(task, queue);
 
 
4109
  g_assert_false(quit_now);
 
 
4110
  g_assert_true(queue->next_run == 0);
 
 
4111
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4113
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4114
        .func=read_inotify_event,
 
 
4117
        .quit_now=&quit_now,
 
 
4118
        .password=&password,
 
 
4119
        .filename=task.filename,
 
 
4120
        .cancelled_filenames=task.cancelled_filenames,
 
 
4121
        .current_time=¤t_time,
 
 
4122
        .mandos_client_exited=&mandos_client_exited,
 
 
4123
        .password_is_read=&password_is_read,
 
 
4126
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4127
                                   EPOLLIN | EPOLLRDHUP));
 
 
4129
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4132
static void test_read_inotify_event_eof(__attribute__((unused))
 
 
4133
                                        test_fixture *fixture,
 
 
4134
                                        __attribute__((unused))
 
 
4135
                                        gconstpointer user_data){
 
 
4136
  __attribute__((cleanup(cleanup_close)))
 
 
4137
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4138
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4139
  const mono_microsecs current_time = 0;
 
 
4142
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4143
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4145
  bool quit_now = false;
 
 
4146
  buffer password = {};
 
 
4147
  __attribute__((cleanup(cleanup_queue)))
 
 
4148
    task_queue *queue = create_queue();
 
 
4149
  g_assert_nonnull(queue);
 
 
4151
  task_context task = {
 
 
4152
    .func=read_inotify_event,
 
 
4155
    .quit_now=&quit_now,
 
 
4156
    .password=&password,
 
 
4157
    .filename=strdup("/nonexistent"),
 
 
4158
    .cancelled_filenames = &(string_set){},
 
 
4160
    .current_time=¤t_time,
 
 
4162
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4163
  g_assert_true(quit_now);
 
 
4164
  g_assert_true(queue->next_run == 0);
 
 
4165
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4167
  g_assert_true(epoll_set_does_not_contain(epoll_fd, pipefds[0]));
 
 
4169
  g_assert_cmpint(close(pipefds[0]), ==, -1);
 
 
4173
void test_read_inotify_event_IN_CLOSE_WRITE(__attribute__((unused))
 
 
4174
                                            test_fixture *fixture,
 
 
4175
                                            __attribute__((unused))
 
 
4176
                                            gconstpointer user_data){
 
 
4177
  __attribute__((cleanup(cleanup_close)))
 
 
4178
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4179
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4180
  const mono_microsecs current_time = 0;
 
 
4183
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4185
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4186
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4188
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4190
    struct inotify_event event;
 
 
4191
    char name_buffer[NAME_MAX + 1];
 
 
4193
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4195
  const char dummy_file_name[] = "ask.dummy_file_name";
 
 
4196
  ievent->mask = IN_CLOSE_WRITE;
 
 
4197
  ievent->len = sizeof(dummy_file_name);
 
 
4198
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4199
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4200
                              + sizeof(dummy_file_name));
 
 
4201
#if defined(__GNUC__) and __GNUC__ >= 11
 
 
4202
#pragma GCC diagnostic push
 
 
4203
  /* ievent is pointing into a struct which is of sufficient size */
 
 
4204
#pragma GCC diagnostic ignored "-Wstringop-overread"
 
 
4206
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4208
#if defined(__GNUC__) and __GNUC__ >= 11
 
 
4209
#pragma GCC diagnostic pop
 
 
4211
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4213
  bool quit_now = false;
 
 
4214
  buffer password = {};
 
 
4215
  bool mandos_client_exited = false;
 
 
4216
  bool password_is_read = false;
 
 
4217
  __attribute__((cleanup(cleanup_queue)))
 
 
4218
    task_queue *queue = create_queue();
 
 
4219
  g_assert_nonnull(queue);
 
 
4221
  task_context task = {
 
 
4222
    .func=read_inotify_event,
 
 
4225
    .quit_now=&quit_now,
 
 
4226
    .password=&password,
 
 
4227
    .filename=strdup("/nonexistent"),
 
 
4228
    .cancelled_filenames = &(string_set){},
 
 
4230
    .current_time=¤t_time,
 
 
4231
    .mandos_client_exited=&mandos_client_exited,
 
 
4232
    .password_is_read=&password_is_read,
 
 
4234
  task.func(task, queue);
 
 
4235
  g_assert_false(quit_now);
 
 
4236
  g_assert_true(queue->next_run != 0);
 
 
4237
  g_assert_cmpuint((unsigned int)queue->length, >=, 1);
 
 
4239
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4240
        .func=read_inotify_event,
 
 
4243
        .quit_now=&quit_now,
 
 
4244
        .password=&password,
 
 
4245
        .filename=task.filename,
 
 
4246
        .cancelled_filenames=task.cancelled_filenames,
 
 
4247
        .current_time=¤t_time,
 
 
4248
        .mandos_client_exited=&mandos_client_exited,
 
 
4249
        .password_is_read=&password_is_read,
 
 
4252
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4253
                                   EPOLLIN | EPOLLRDHUP));
 
 
4255
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
 
4257
  __attribute__((cleanup(cleanup_string)))
 
 
4258
    char *filename = NULL;
 
 
4259
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4260
                           dummy_file_name), >, 0);
 
 
4261
  g_assert_nonnull(filename);
 
 
4262
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4263
        .func=open_and_parse_question,
 
 
4266
        .question_filename=filename,
 
 
4267
        .password=&password,
 
 
4268
        .cancelled_filenames=task.cancelled_filenames,
 
 
4269
        .current_time=¤t_time,
 
 
4270
        .mandos_client_exited=&mandos_client_exited,
 
 
4271
        .password_is_read=&password_is_read,
 
 
4276
void test_read_inotify_event_IN_MOVED_TO(__attribute__((unused))
 
 
4277
                                         test_fixture *fixture,
 
 
4278
                                         __attribute__((unused))
 
 
4279
                                         gconstpointer user_data){
 
 
4280
  __attribute__((cleanup(cleanup_close)))
 
 
4281
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4282
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4283
  const mono_microsecs current_time = 0;
 
 
4286
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4288
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4289
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4291
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4293
    struct inotify_event event;
 
 
4294
    char name_buffer[NAME_MAX + 1];
 
 
4296
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4298
  const char dummy_file_name[] = "ask.dummy_file_name";
 
 
4299
  ievent->mask = IN_MOVED_TO;
 
 
4300
  ievent->len = sizeof(dummy_file_name);
 
 
4301
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4302
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4303
                              + sizeof(dummy_file_name));
 
 
4304
#if defined(__GNUC__) and __GNUC__ >= 11
 
 
4305
#pragma GCC diagnostic push
 
 
4306
  /* ievent is pointing into a struct which is of sufficient size */
 
 
4307
#pragma GCC diagnostic ignored "-Wstringop-overread"
 
 
4309
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4311
#if defined(__GNUC__) and __GNUC__ >= 11
 
 
4312
#pragma GCC diagnostic pop
 
 
4314
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4316
  bool quit_now = false;
 
 
4317
  buffer password = {};
 
 
4318
  bool mandos_client_exited = false;
 
 
4319
  bool password_is_read = false;
 
 
4320
  __attribute__((cleanup(cleanup_queue)))
 
 
4321
    task_queue *queue = create_queue();
 
 
4322
  g_assert_nonnull(queue);
 
 
4324
  task_context task = {
 
 
4325
    .func=read_inotify_event,
 
 
4328
    .quit_now=&quit_now,
 
 
4329
    .password=&password,
 
 
4330
    .filename=strdup("/nonexistent"),
 
 
4331
    .cancelled_filenames = &(string_set){},
 
 
4333
    .current_time=¤t_time,
 
 
4334
    .mandos_client_exited=&mandos_client_exited,
 
 
4335
    .password_is_read=&password_is_read,
 
 
4337
  task.func(task, queue);
 
 
4338
  g_assert_false(quit_now);
 
 
4339
  g_assert_true(queue->next_run != 0);
 
 
4340
  g_assert_cmpuint((unsigned int)queue->length, >=, 1);
 
 
4342
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4343
        .func=read_inotify_event,
 
 
4346
        .quit_now=&quit_now,
 
 
4347
        .password=&password,
 
 
4348
        .filename=task.filename,
 
 
4349
        .cancelled_filenames=task.cancelled_filenames,
 
 
4350
        .current_time=¤t_time,
 
 
4351
        .mandos_client_exited=&mandos_client_exited,
 
 
4352
        .password_is_read=&password_is_read,
 
 
4355
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4356
                                   EPOLLIN | EPOLLRDHUP));
 
 
4358
  g_assert_cmpuint((unsigned int)queue->length, >=, 2);
 
 
4360
  __attribute__((cleanup(cleanup_string)))
 
 
4361
    char *filename = NULL;
 
 
4362
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4363
                           dummy_file_name), >, 0);
 
 
4364
  g_assert_nonnull(filename);
 
 
4365
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4366
        .func=open_and_parse_question,
 
 
4369
        .question_filename=filename,
 
 
4370
        .password=&password,
 
 
4371
        .cancelled_filenames=task.cancelled_filenames,
 
 
4372
        .current_time=¤t_time,
 
 
4373
        .mandos_client_exited=&mandos_client_exited,
 
 
4374
        .password_is_read=&password_is_read,
 
 
4379
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
 
 
4380
                                           test_fixture *fixture,
 
 
4381
                                           __attribute__((unused))
 
 
4382
                                           gconstpointer user_data){
 
 
4383
  __attribute__((cleanup(cleanup_close)))
 
 
4384
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4385
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4386
  __attribute__((cleanup(string_set_clear)))
 
 
4387
    string_set cancelled_filenames = {};
 
 
4388
  const mono_microsecs current_time = 0;
 
 
4391
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4393
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4394
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4396
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4398
    struct inotify_event event;
 
 
4399
    char name_buffer[NAME_MAX + 1];
 
 
4401
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4403
  const char dummy_file_name[] = "ask.dummy_file_name";
 
 
4404
  ievent->mask = IN_MOVED_FROM;
 
 
4405
  ievent->len = sizeof(dummy_file_name);
 
 
4406
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4407
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4408
                              + sizeof(dummy_file_name));
 
 
4409
#if defined(__GNUC__) and __GNUC__ >= 11
 
 
4410
#pragma GCC diagnostic push
 
 
4411
  /* ievent is pointing into a struct which is of sufficient size */
 
 
4412
#pragma GCC diagnostic ignored "-Wstringop-overread"
 
 
4414
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4416
#if defined(__GNUC__) and __GNUC__ >= 11
 
 
4417
#pragma GCC diagnostic pop
 
 
4419
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4421
  bool quit_now = false;
 
 
4422
  buffer password = {};
 
 
4423
  bool mandos_client_exited = false;
 
 
4424
  bool password_is_read = false;
 
 
4425
  __attribute__((cleanup(cleanup_queue)))
 
 
4426
    task_queue *queue = create_queue();
 
 
4427
  g_assert_nonnull(queue);
 
 
4429
  task_context task = {
 
 
4430
    .func=read_inotify_event,
 
 
4433
    .quit_now=&quit_now,
 
 
4434
    .password=&password,
 
 
4435
    .filename=strdup("/nonexistent"),
 
 
4436
    .cancelled_filenames=&cancelled_filenames,
 
 
4437
    .current_time=¤t_time,
 
 
4438
    .mandos_client_exited=&mandos_client_exited,
 
 
4439
    .password_is_read=&password_is_read,
 
 
4441
  task.func(task, queue);
 
 
4442
  g_assert_false(quit_now);
 
 
4443
  g_assert_true(queue->next_run == 0);
 
 
4444
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4446
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4447
        .func=read_inotify_event,
 
 
4450
        .quit_now=&quit_now,
 
 
4451
        .password=&password,
 
 
4452
        .filename=task.filename,
 
 
4453
        .cancelled_filenames=&cancelled_filenames,
 
 
4454
        .current_time=¤t_time,
 
 
4455
        .mandos_client_exited=&mandos_client_exited,
 
 
4456
        .password_is_read=&password_is_read,
 
 
4459
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4460
                                   EPOLLIN | EPOLLRDHUP));
 
 
4462
  __attribute__((cleanup(cleanup_string)))
 
 
4463
    char *filename = NULL;
 
 
4464
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4465
                           dummy_file_name), >, 0);
 
 
4466
  g_assert_nonnull(filename);
 
 
4467
  g_assert_true(string_set_contains(*task.cancelled_filenames,
 
 
4471
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
 
 
4472
                                              test_fixture *fixture,
 
 
4473
                                              __attribute__((unused))
 
 
4476
  __attribute__((cleanup(cleanup_close)))
 
 
4477
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4478
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4479
  __attribute__((cleanup(string_set_clear)))
 
 
4480
    string_set cancelled_filenames = {};
 
 
4481
  const mono_microsecs current_time = 0;
 
 
4484
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4486
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4487
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4489
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4491
    struct inotify_event event;
 
 
4492
    char name_buffer[NAME_MAX + 1];
 
 
4494
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4496
  const char dummy_file_name[] = "ask.dummy_file_name";
 
 
4497
  ievent->mask = IN_DELETE;
 
 
4498
  ievent->len = sizeof(dummy_file_name);
 
 
4499
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4500
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4501
                              + sizeof(dummy_file_name));
 
 
4502
#if defined(__GNUC__) and __GNUC__ >= 11
 
 
4503
#pragma GCC diagnostic push
 
 
4504
  /* ievent is pointing into a struct which is of sufficient size */
 
 
4505
#pragma GCC diagnostic ignored "-Wstringop-overread"
 
 
4507
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4509
#if defined(__GNUC__) and __GNUC__ >= 11
 
 
4510
#pragma GCC diagnostic pop
 
 
4512
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4514
  bool quit_now = false;
 
 
4515
  buffer password = {};
 
 
4516
  bool mandos_client_exited = false;
 
 
4517
  bool password_is_read = false;
 
 
4518
  __attribute__((cleanup(cleanup_queue)))
 
 
4519
    task_queue *queue = create_queue();
 
 
4520
  g_assert_nonnull(queue);
 
 
4522
  task_context task = {
 
 
4523
    .func=read_inotify_event,
 
 
4526
    .quit_now=&quit_now,
 
 
4527
    .password=&password,
 
 
4528
    .filename=strdup("/nonexistent"),
 
 
4529
    .cancelled_filenames=&cancelled_filenames,
 
 
4530
    .current_time=¤t_time,
 
 
4531
    .mandos_client_exited=&mandos_client_exited,
 
 
4532
    .password_is_read=&password_is_read,
 
 
4534
  task.func(task, queue);
 
 
4535
  g_assert_false(quit_now);
 
 
4536
  g_assert_true(queue->next_run == 0);
 
 
4537
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4539
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4540
        .func=read_inotify_event,
 
 
4543
        .quit_now=&quit_now,
 
 
4544
        .password=&password,
 
 
4545
        .filename=task.filename,
 
 
4546
        .cancelled_filenames=&cancelled_filenames,
 
 
4547
        .current_time=¤t_time,
 
 
4548
        .mandos_client_exited=&mandos_client_exited,
 
 
4549
        .password_is_read=&password_is_read,
 
 
4552
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4553
                                   EPOLLIN | EPOLLRDHUP));
 
 
4555
  __attribute__((cleanup(cleanup_string)))
 
 
4556
    char *filename = NULL;
 
 
4557
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4558
                           dummy_file_name), >, 0);
 
 
4559
  g_assert_nonnull(filename);
 
 
4560
  g_assert_true(string_set_contains(*task.cancelled_filenames,
 
 
4565
test_read_inotify_event_IN_CLOSE_WRITE_badname(__attribute__((unused))
 
 
4566
                                               test_fixture *fixture,
 
 
4567
                                               __attribute__((unused))
 
 
4570
  __attribute__((cleanup(cleanup_close)))
 
 
4571
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4572
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4573
  const mono_microsecs current_time = 0;
 
 
4576
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4578
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4579
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4581
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4583
    struct inotify_event event;
 
 
4584
    char name_buffer[NAME_MAX + 1];
 
 
4586
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4588
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
 
4589
  ievent->mask = IN_CLOSE_WRITE;
 
 
4590
  ievent->len = sizeof(dummy_file_name);
 
 
4591
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4592
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4593
                              + sizeof(dummy_file_name));
 
 
4594
#if defined(__GNUC__) and __GNUC__ >= 11
 
 
4595
#pragma GCC diagnostic push
 
 
4596
  /* ievent is pointing into a struct which is of sufficient size */
 
 
4597
#pragma GCC diagnostic ignored "-Wstringop-overread"
 
 
4599
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4601
#if defined(__GNUC__) and __GNUC__ >= 11
 
 
4602
#pragma GCC diagnostic pop
 
 
4604
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4606
  bool quit_now = false;
 
 
4607
  buffer password = {};
 
 
4608
  bool mandos_client_exited = false;
 
 
4609
  bool password_is_read = false;
 
 
4610
  __attribute__((cleanup(cleanup_queue)))
 
 
4611
    task_queue *queue = create_queue();
 
 
4612
  g_assert_nonnull(queue);
 
 
4614
  task_context task = {
 
 
4615
    .func=read_inotify_event,
 
 
4618
    .quit_now=&quit_now,
 
 
4619
    .password=&password,
 
 
4620
    .filename=strdup("/nonexistent"),
 
 
4621
    .cancelled_filenames = &(string_set){},
 
 
4623
    .current_time=¤t_time,
 
 
4624
    .mandos_client_exited=&mandos_client_exited,
 
 
4625
    .password_is_read=&password_is_read,
 
 
4627
  task.func(task, queue);
 
 
4628
  g_assert_false(quit_now);
 
 
4629
  g_assert_true(queue->next_run == 0);
 
 
4630
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4632
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4633
        .func=read_inotify_event,
 
 
4636
        .quit_now=&quit_now,
 
 
4637
        .password=&password,
 
 
4638
        .filename=task.filename,
 
 
4639
        .cancelled_filenames=task.cancelled_filenames,
 
 
4640
        .current_time=¤t_time,
 
 
4641
        .mandos_client_exited=&mandos_client_exited,
 
 
4642
        .password_is_read=&password_is_read,
 
 
4645
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4646
                                   EPOLLIN | EPOLLRDHUP));
 
 
4650
test_read_inotify_event_IN_MOVED_TO_badname(__attribute__((unused))
 
 
4651
                                            test_fixture *fixture,
 
 
4652
                                            __attribute__((unused))
 
 
4653
                                            gconstpointer user_data){
 
 
4654
  __attribute__((cleanup(cleanup_close)))
 
 
4655
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4656
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4657
  const mono_microsecs current_time = 0;
 
 
4660
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4662
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4663
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4665
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4667
    struct inotify_event event;
 
 
4668
    char name_buffer[NAME_MAX + 1];
 
 
4670
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4672
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
 
4673
  ievent->mask = IN_MOVED_TO;
 
 
4674
  ievent->len = sizeof(dummy_file_name);
 
 
4675
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4676
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4677
                              + sizeof(dummy_file_name));
 
 
4678
#if defined(__GNUC__) and __GNUC__ >= 11
 
 
4679
#pragma GCC diagnostic push
 
 
4680
  /* ievent is pointing into a struct which is of sufficient size */
 
 
4681
#pragma GCC diagnostic ignored "-Wstringop-overread"
 
 
4683
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4685
#if defined(__GNUC__) and __GNUC__ >= 11
 
 
4686
#pragma GCC diagnostic pop
 
 
4688
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4690
  bool quit_now = false;
 
 
4691
  buffer password = {};
 
 
4692
  bool mandos_client_exited = false;
 
 
4693
  bool password_is_read = false;
 
 
4694
  __attribute__((cleanup(cleanup_queue)))
 
 
4695
    task_queue *queue = create_queue();
 
 
4696
  g_assert_nonnull(queue);
 
 
4698
  task_context task = {
 
 
4699
    .func=read_inotify_event,
 
 
4702
    .quit_now=&quit_now,
 
 
4703
    .password=&password,
 
 
4704
    .filename=strdup("/nonexistent"),
 
 
4705
    .cancelled_filenames = &(string_set){},
 
 
4707
    .current_time=¤t_time,
 
 
4708
    .mandos_client_exited=&mandos_client_exited,
 
 
4709
    .password_is_read=&password_is_read,
 
 
4711
  task.func(task, queue);
 
 
4712
  g_assert_false(quit_now);
 
 
4713
  g_assert_true(queue->next_run == 0);
 
 
4714
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4716
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4717
        .func=read_inotify_event,
 
 
4720
        .quit_now=&quit_now,
 
 
4721
        .password=&password,
 
 
4722
        .filename=task.filename,
 
 
4723
        .cancelled_filenames=task.cancelled_filenames,
 
 
4724
        .current_time=¤t_time,
 
 
4725
        .mandos_client_exited=&mandos_client_exited,
 
 
4726
        .password_is_read=&password_is_read,
 
 
4729
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4730
                                   EPOLLIN | EPOLLRDHUP));
 
 
4734
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
 
 
4735
                                              test_fixture *fixture,
 
 
4736
                                              __attribute__((unused))
 
 
4739
  __attribute__((cleanup(cleanup_close)))
 
 
4740
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4741
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4742
  __attribute__((cleanup(string_set_clear)))
 
 
4743
    string_set cancelled_filenames = {};
 
 
4744
  const mono_microsecs current_time = 0;
 
 
4747
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4749
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4750
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4752
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4754
    struct inotify_event event;
 
 
4755
    char name_buffer[NAME_MAX + 1];
 
 
4757
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4759
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
 
4760
  ievent->mask = IN_MOVED_FROM;
 
 
4761
  ievent->len = sizeof(dummy_file_name);
 
 
4762
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4763
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4764
                              + sizeof(dummy_file_name));
 
 
4765
#if defined(__GNUC__) and __GNUC__ >= 11
 
 
4766
#pragma GCC diagnostic push
 
 
4767
  /* ievent is pointing into a struct which is of sufficient size */
 
 
4768
#pragma GCC diagnostic ignored "-Wstringop-overread"
 
 
4770
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4772
#if defined(__GNUC__) and __GNUC__ >= 11
 
 
4773
#pragma GCC diagnostic pop
 
 
4775
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4777
  bool quit_now = false;
 
 
4778
  buffer password = {};
 
 
4779
  bool mandos_client_exited = false;
 
 
4780
  bool password_is_read = false;
 
 
4781
  __attribute__((cleanup(cleanup_queue)))
 
 
4782
    task_queue *queue = create_queue();
 
 
4783
  g_assert_nonnull(queue);
 
 
4785
  task_context task = {
 
 
4786
    .func=read_inotify_event,
 
 
4789
    .quit_now=&quit_now,
 
 
4790
    .password=&password,
 
 
4791
    .filename=strdup("/nonexistent"),
 
 
4792
    .cancelled_filenames=&cancelled_filenames,
 
 
4793
    .current_time=¤t_time,
 
 
4794
    .mandos_client_exited=&mandos_client_exited,
 
 
4795
    .password_is_read=&password_is_read,
 
 
4797
  task.func(task, queue);
 
 
4798
  g_assert_false(quit_now);
 
 
4799
  g_assert_true(queue->next_run == 0);
 
 
4800
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4802
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4803
        .func=read_inotify_event,
 
 
4806
        .quit_now=&quit_now,
 
 
4807
        .password=&password,
 
 
4808
        .filename=task.filename,
 
 
4809
        .cancelled_filenames=&cancelled_filenames,
 
 
4810
        .current_time=¤t_time,
 
 
4811
        .mandos_client_exited=&mandos_client_exited,
 
 
4812
        .password_is_read=&password_is_read,
 
 
4815
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4816
                                   EPOLLIN | EPOLLRDHUP));
 
 
4818
  __attribute__((cleanup(cleanup_string)))
 
 
4819
    char *filename = NULL;
 
 
4820
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4821
                           dummy_file_name), >, 0);
 
 
4822
  g_assert_nonnull(filename);
 
 
4823
  g_assert_false(string_set_contains(cancelled_filenames, filename));
 
 
4827
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
 
 
4828
                                               test_fixture *fixture,
 
 
4829
                                               __attribute__((unused))
 
 
4832
  __attribute__((cleanup(cleanup_close)))
 
 
4833
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4834
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4835
  __attribute__((cleanup(string_set_clear)))
 
 
4836
    string_set cancelled_filenames = {};
 
 
4837
  const mono_microsecs current_time = 0;
 
 
4840
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
4842
  /* "sufficient to read at least one event." - inotify(7) */
 
 
4843
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
 
4845
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
 
4847
    struct inotify_event event;
 
 
4848
    char name_buffer[NAME_MAX + 1];
 
 
4850
  struct inotify_event *const ievent = &ievent_buffer.event;
 
 
4852
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
 
4853
  ievent->mask = IN_DELETE;
 
 
4854
  ievent->len = sizeof(dummy_file_name);
 
 
4855
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
 
4856
  const size_t ievent_size = (sizeof(struct inotify_event)
 
 
4857
                              + sizeof(dummy_file_name));
 
 
4858
#if defined(__GNUC__) and __GNUC__ >= 11
 
 
4859
#pragma GCC diagnostic push
 
 
4860
  /* ievent is pointing into a struct which is of sufficient size */
 
 
4861
#pragma GCC diagnostic ignored "-Wstringop-overread"
 
 
4863
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
 
4865
#if defined(__GNUC__) and __GNUC__ >= 11
 
 
4866
#pragma GCC diagnostic pop
 
 
4868
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
4870
  bool quit_now = false;
 
 
4871
  buffer password = {};
 
 
4872
  bool mandos_client_exited = false;
 
 
4873
  bool password_is_read = false;
 
 
4874
  __attribute__((cleanup(cleanup_queue)))
 
 
4875
    task_queue *queue = create_queue();
 
 
4876
  g_assert_nonnull(queue);
 
 
4878
  task_context task = {
 
 
4879
    .func=read_inotify_event,
 
 
4882
    .quit_now=&quit_now,
 
 
4883
    .password=&password,
 
 
4884
    .filename=strdup("/nonexistent"),
 
 
4885
    .cancelled_filenames=&cancelled_filenames,
 
 
4886
    .current_time=¤t_time,
 
 
4887
    .mandos_client_exited=&mandos_client_exited,
 
 
4888
    .password_is_read=&password_is_read,
 
 
4890
  task.func(task, queue);
 
 
4891
  g_assert_false(quit_now);
 
 
4892
  g_assert_true(queue->next_run == 0);
 
 
4893
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
4895
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
4896
        .func=read_inotify_event,
 
 
4899
        .quit_now=&quit_now,
 
 
4900
        .password=&password,
 
 
4901
        .filename=task.filename,
 
 
4902
        .cancelled_filenames=&cancelled_filenames,
 
 
4903
        .current_time=¤t_time,
 
 
4904
        .mandos_client_exited=&mandos_client_exited,
 
 
4905
        .password_is_read=&password_is_read,
 
 
4908
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
 
4909
                                   EPOLLIN | EPOLLRDHUP));
 
 
4911
  __attribute__((cleanup(cleanup_string)))
 
 
4912
    char *filename = NULL;
 
 
4913
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
 
4914
                           dummy_file_name), >, 0);
 
 
4915
  g_assert_nonnull(filename);
 
 
4916
  g_assert_false(string_set_contains(cancelled_filenames, filename));
 
 
4920
void test_open_and_parse_question_ENOENT(__attribute__((unused))
 
 
4921
                                         test_fixture *fixture,
 
 
4922
                                         __attribute__((unused))
 
 
4923
                                         gconstpointer user_data){
 
 
4924
  __attribute__((cleanup(cleanup_close)))
 
 
4925
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4926
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4927
  __attribute__((cleanup(string_set_clear)))
 
 
4928
    string_set cancelled_filenames = {};
 
 
4929
  bool mandos_client_exited = false;
 
 
4930
  bool password_is_read = false;
 
 
4931
  __attribute__((cleanup(cleanup_queue)))
 
 
4932
    task_queue *queue = create_queue();
 
 
4933
  g_assert_nonnull(queue);
 
 
4935
  char *const filename = strdup("/nonexistent");
 
 
4936
  g_assert_nonnull(filename);
 
 
4937
  task_context task = {
 
 
4938
    .func=open_and_parse_question,
 
 
4939
    .question_filename=filename,
 
 
4941
    .password=(buffer[]){{}},
 
 
4943
    .cancelled_filenames=&cancelled_filenames,
 
 
4944
    .current_time=(mono_microsecs[]){0},
 
 
4945
    .mandos_client_exited=&mandos_client_exited,
 
 
4946
    .password_is_read=&password_is_read,
 
 
4948
  task.func(task, queue);
 
 
4949
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4952
static void test_open_and_parse_question_EIO(__attribute__((unused))
 
 
4953
                                             test_fixture *fixture,
 
 
4954
                                             __attribute__((unused))
 
 
4955
                                             gconstpointer user_data){
 
 
4956
  __attribute__((cleanup(cleanup_close)))
 
 
4957
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4958
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4959
  __attribute__((cleanup(string_set_clear)))
 
 
4960
    string_set cancelled_filenames = {};
 
 
4961
  buffer password = {};
 
 
4962
  bool mandos_client_exited = false;
 
 
4963
  bool password_is_read = false;
 
 
4964
  __attribute__((cleanup(cleanup_queue)))
 
 
4965
    task_queue *queue = create_queue();
 
 
4966
  g_assert_nonnull(queue);
 
 
4967
  const mono_microsecs current_time = 0;
 
 
4969
  char *filename = strdup("/proc/self/mem");
 
 
4970
  task_context task = {
 
 
4971
    .func=open_and_parse_question,
 
 
4972
    .question_filename=filename,
 
 
4974
    .password=&password,
 
 
4976
    .cancelled_filenames=&cancelled_filenames,
 
 
4977
    .current_time=¤t_time,
 
 
4978
    .mandos_client_exited=&mandos_client_exited,
 
 
4979
    .password_is_read=&password_is_read,
 
 
4981
  run_task_with_stderr_to_dev_null(task, queue);
 
 
4982
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
4986
test_open_and_parse_question_parse_error(__attribute__((unused))
 
 
4987
                                         test_fixture *fixture,
 
 
4988
                                         __attribute__((unused))
 
 
4989
                                         gconstpointer user_data){
 
 
4990
  __attribute__((cleanup(cleanup_close)))
 
 
4991
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
4992
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
4993
  __attribute__((cleanup(string_set_clear)))
 
 
4994
    string_set cancelled_filenames = {};
 
 
4995
  __attribute__((cleanup(cleanup_queue)))
 
 
4996
    task_queue *queue = create_queue();
 
 
4997
  g_assert_nonnull(queue);
 
 
4999
  __attribute__((cleanup(cleanup_string)))
 
 
5000
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5001
  g_assert_nonnull(tempfilename);
 
 
5002
  int tempfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5003
  g_assert_cmpint(tempfile, >, 0);
 
 
5004
  const char bad_data[] = "this is bad syntax\n";
 
 
5005
  g_assert_cmpint(write(tempfile, bad_data, sizeof(bad_data)),
 
 
5006
                  ==, sizeof(bad_data));
 
 
5007
  g_assert_cmpint(close(tempfile), ==, 0);
 
 
5009
  char *const filename = strdup(tempfilename);
 
 
5010
  g_assert_nonnull(filename);
 
 
5011
  task_context task = {
 
 
5012
    .func=open_and_parse_question,
 
 
5013
    .question_filename=filename,
 
 
5015
    .password=(buffer[]){{}},
 
 
5017
    .cancelled_filenames=&cancelled_filenames,
 
 
5018
    .current_time=(mono_microsecs[]){0},
 
 
5019
    .mandos_client_exited=(bool[]){false},
 
 
5020
    .password_is_read=(bool[]){false},
 
 
5022
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5024
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5026
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5030
void test_open_and_parse_question_nosocket(__attribute__((unused))
 
 
5031
                                           test_fixture *fixture,
 
 
5032
                                           __attribute__((unused))
 
 
5033
                                           gconstpointer user_data){
 
 
5034
  __attribute__((cleanup(cleanup_close)))
 
 
5035
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5036
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5037
  __attribute__((cleanup(string_set_clear)))
 
 
5038
    string_set cancelled_filenames = {};
 
 
5039
  __attribute__((cleanup(cleanup_queue)))
 
 
5040
    task_queue *queue = create_queue();
 
 
5041
  g_assert_nonnull(queue);
 
 
5043
  __attribute__((cleanup(cleanup_string)))
 
 
5044
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5045
  g_assert_nonnull(tempfilename);
 
 
5046
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5047
  g_assert_cmpint(questionfile, >, 0);
 
 
5048
  FILE *qf = fdopen(questionfile, "w");
 
 
5049
  g_assert_cmpint(fprintf(qf, "[Ask]\nPID=1\n"), >, 0);
 
 
5050
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5052
  char *const filename = strdup(tempfilename);
 
 
5053
  g_assert_nonnull(filename);
 
 
5054
  task_context task = {
 
 
5055
    .func=open_and_parse_question,
 
 
5056
    .question_filename=filename,
 
 
5058
    .password=(buffer[]){{}},
 
 
5060
    .cancelled_filenames=&cancelled_filenames,
 
 
5061
    .current_time=(mono_microsecs[]){0},
 
 
5062
    .mandos_client_exited=(bool[]){false},
 
 
5063
    .password_is_read=(bool[]){false},
 
 
5065
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5066
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5068
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5072
void test_open_and_parse_question_badsocket(__attribute__((unused))
 
 
5073
                                            test_fixture *fixture,
 
 
5074
                                            __attribute__((unused))
 
 
5075
                                            gconstpointer user_data){
 
 
5076
  __attribute__((cleanup(cleanup_close)))
 
 
5077
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5078
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5079
  __attribute__((cleanup(string_set_clear)))
 
 
5080
    string_set cancelled_filenames = {};
 
 
5081
  __attribute__((cleanup(cleanup_queue)))
 
 
5082
    task_queue *queue = create_queue();
 
 
5083
  g_assert_nonnull(queue);
 
 
5085
  __attribute__((cleanup(cleanup_string)))
 
 
5086
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5087
  g_assert_nonnull(tempfilename);
 
 
5088
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5089
  g_assert_cmpint(questionfile, >, 0);
 
 
5090
  FILE *qf = fdopen(questionfile, "w");
 
 
5091
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=\nPID=1\n"), >, 0);
 
 
5092
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5094
  char *const filename = strdup(tempfilename);
 
 
5095
  g_assert_nonnull(filename);
 
 
5096
  task_context task = {
 
 
5097
    .func=open_and_parse_question,
 
 
5098
    .question_filename=filename,
 
 
5100
    .password=(buffer[]){{}},
 
 
5102
    .cancelled_filenames=&cancelled_filenames,
 
 
5103
    .current_time=(mono_microsecs[]){0},
 
 
5104
    .mandos_client_exited=(bool[]){false},
 
 
5105
    .password_is_read=(bool[]){false},
 
 
5107
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5108
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5110
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5114
void test_open_and_parse_question_nopid(__attribute__((unused))
 
 
5115
                                        test_fixture *fixture,
 
 
5116
                                        __attribute__((unused))
 
 
5117
                                        gconstpointer user_data){
 
 
5118
  __attribute__((cleanup(cleanup_close)))
 
 
5119
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5120
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5121
  __attribute__((cleanup(string_set_clear)))
 
 
5122
    string_set cancelled_filenames = {};
 
 
5123
  __attribute__((cleanup(cleanup_queue)))
 
 
5124
    task_queue *queue = create_queue();
 
 
5125
  g_assert_nonnull(queue);
 
 
5127
  __attribute__((cleanup(cleanup_string)))
 
 
5128
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5129
  g_assert_nonnull(tempfilename);
 
 
5130
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5131
  g_assert_cmpint(questionfile, >, 0);
 
 
5132
  FILE *qf = fdopen(questionfile, "w");
 
 
5133
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\n"), >, 0);
 
 
5134
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5136
  char *const filename = strdup(tempfilename);
 
 
5137
  g_assert_nonnull(filename);
 
 
5138
  task_context task = {
 
 
5139
    .func=open_and_parse_question,
 
 
5140
    .question_filename=filename,
 
 
5142
    .password=(buffer[]){{}},
 
 
5144
    .cancelled_filenames=&cancelled_filenames,
 
 
5145
    .current_time=(mono_microsecs[]){0},
 
 
5146
    .mandos_client_exited=(bool[]){false},
 
 
5147
    .password_is_read=(bool[]){false},
 
 
5149
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5150
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5152
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5156
void test_open_and_parse_question_badpid(__attribute__((unused))
 
 
5157
                                         test_fixture *fixture,
 
 
5158
                                         __attribute__((unused))
 
 
5159
                                         gconstpointer user_data){
 
 
5160
  __attribute__((cleanup(cleanup_close)))
 
 
5161
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5162
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5163
  __attribute__((cleanup(string_set_clear)))
 
 
5164
    string_set cancelled_filenames = {};
 
 
5165
  __attribute__((cleanup(cleanup_queue)))
 
 
5166
    task_queue *queue = create_queue();
 
 
5167
  g_assert_nonnull(queue);
 
 
5169
  __attribute__((cleanup(cleanup_string)))
 
 
5170
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5171
  g_assert_nonnull(tempfilename);
 
 
5172
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5173
  g_assert_cmpint(questionfile, >, 0);
 
 
5174
  FILE *qf = fdopen(questionfile, "w");
 
 
5175
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=\n"),
 
 
5177
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5179
  char *const filename = strdup(tempfilename);
 
 
5180
  g_assert_nonnull(filename);
 
 
5181
  task_context task = {
 
 
5182
    .func=open_and_parse_question,
 
 
5183
    .question_filename=filename,
 
 
5185
    .password=(buffer[]){{}},
 
 
5187
    .cancelled_filenames=&cancelled_filenames,
 
 
5188
    .current_time=(mono_microsecs[]){0},
 
 
5189
    .mandos_client_exited=(bool[]){false},
 
 
5190
    .password_is_read=(bool[]){false},
 
 
5192
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5193
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5195
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5199
test_open_and_parse_question_noexist_pid(__attribute__((unused))
 
 
5200
                                         test_fixture *fixture,
 
 
5201
                                         __attribute__((unused))
 
 
5202
                                         gconstpointer user_data){
 
 
5203
  __attribute__((cleanup(cleanup_close)))
 
 
5204
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5205
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5206
  __attribute__((cleanup(string_set_clear)))
 
 
5207
    string_set cancelled_filenames = {};
 
 
5208
  buffer password = {};
 
 
5209
  bool mandos_client_exited = false;
 
 
5210
  bool password_is_read = false;
 
 
5211
  __attribute__((cleanup(cleanup_queue)))
 
 
5212
    task_queue *queue = create_queue();
 
 
5213
  g_assert_nonnull(queue);
 
 
5214
  const mono_microsecs current_time = 0;
 
 
5216
  /* Find value of sysctl kernel.pid_max */
 
 
5217
  uintmax_t pid_max = 0;
 
 
5218
  FILE *sysctl_pid_max = fopen("/proc/sys/kernel/pid_max", "r");
 
 
5219
  g_assert_nonnull(sysctl_pid_max);
 
 
5220
  g_assert_cmpint(fscanf(sysctl_pid_max, "%" PRIuMAX, &pid_max),
 
 
5222
  g_assert_cmpint(fclose(sysctl_pid_max), ==, 0);
 
 
5224
  pid_t nonexisting_pid = ((pid_t)pid_max)+1;
 
 
5225
  g_assert_true(nonexisting_pid > 0); /* Overflow check */
 
 
5227
  __attribute__((cleanup(cleanup_string)))
 
 
5228
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5229
  g_assert_nonnull(tempfilename);
 
 
5230
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5231
  g_assert_cmpint(questionfile, >, 0);
 
 
5232
  FILE *qf = fdopen(questionfile, "w");
 
 
5233
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
 
5234
                          PRIuMAX"\n", (uintmax_t)nonexisting_pid),
 
 
5236
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5238
  char *const question_filename = strdup(tempfilename);
 
 
5239
  g_assert_nonnull(question_filename);
 
 
5240
  task_context task = {
 
 
5241
    .func=open_and_parse_question,
 
 
5242
    .question_filename=question_filename,
 
 
5244
    .password=&password,
 
 
5245
    .filename=question_filename,
 
 
5246
    .cancelled_filenames=&cancelled_filenames,
 
 
5247
    .current_time=¤t_time,
 
 
5248
    .mandos_client_exited=&mandos_client_exited,
 
 
5249
    .password_is_read=&password_is_read,
 
 
5251
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5252
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5254
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5258
test_open_and_parse_question_no_notafter(__attribute__((unused))
 
 
5259
                                         test_fixture *fixture,
 
 
5260
                                         __attribute__((unused))
 
 
5261
                                         gconstpointer user_data){
 
 
5262
  __attribute__((cleanup(cleanup_close)))
 
 
5263
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5264
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5265
  __attribute__((cleanup(string_set_clear)))
 
 
5266
    string_set cancelled_filenames = {};
 
 
5267
  buffer password = {};
 
 
5268
  bool mandos_client_exited = false;
 
 
5269
  bool password_is_read = false;
 
 
5270
  __attribute__((cleanup(cleanup_queue)))
 
 
5271
    task_queue *queue = create_queue();
 
 
5272
  g_assert_nonnull(queue);
 
 
5273
  const mono_microsecs current_time = 0;
 
 
5275
  __attribute__((cleanup(cleanup_string)))
 
 
5276
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5277
  g_assert_nonnull(tempfilename);
 
 
5278
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5279
  g_assert_cmpint(questionfile, >, 0);
 
 
5280
  FILE *qf = fdopen(questionfile, "w");
 
 
5281
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
 
5282
                          PRIuMAX "\n", (uintmax_t)getpid()), >, 0);
 
 
5283
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5285
  char *const filename = strdup(tempfilename);
 
 
5286
  g_assert_nonnull(filename);
 
 
5287
  task_context task = {
 
 
5288
    .func=open_and_parse_question,
 
 
5289
    .question_filename=filename,
 
 
5291
    .password=&password,
 
 
5293
    .cancelled_filenames=&cancelled_filenames,
 
 
5294
    .current_time=¤t_time,
 
 
5295
    .mandos_client_exited=&mandos_client_exited,
 
 
5296
    .password_is_read=&password_is_read,
 
 
5298
  task.func(task, queue);
 
 
5299
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5301
  __attribute__((cleanup(cleanup_string)))
 
 
5302
    char *socket_filename = strdup("/nonexistent");
 
 
5303
  g_assert_nonnull(socket_filename);
 
 
5304
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
5305
        .func=connect_question_socket,
 
 
5306
        .question_filename=tempfilename,
 
 
5307
        .filename=socket_filename,
 
 
5309
        .password=&password,
 
 
5310
        .current_time=¤t_time,
 
 
5311
        .mandos_client_exited=&mandos_client_exited,
 
 
5312
        .password_is_read=&password_is_read,
 
 
5315
  g_assert_true(queue->next_run != 0);
 
 
5317
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5321
test_open_and_parse_question_bad_notafter(__attribute__((unused))
 
 
5322
                                          test_fixture *fixture,
 
 
5323
                                          __attribute__((unused))
 
 
5324
                                          gconstpointer user_data){
 
 
5325
  __attribute__((cleanup(cleanup_close)))
 
 
5326
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5327
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5328
  __attribute__((cleanup(string_set_clear)))
 
 
5329
    string_set cancelled_filenames = {};
 
 
5330
  buffer password = {};
 
 
5331
  bool mandos_client_exited = false;
 
 
5332
  bool password_is_read = false;
 
 
5333
  __attribute__((cleanup(cleanup_queue)))
 
 
5334
    task_queue *queue = create_queue();
 
 
5335
  g_assert_nonnull(queue);
 
 
5336
  const mono_microsecs current_time = 0;
 
 
5338
  __attribute__((cleanup(cleanup_string)))
 
 
5339
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5340
  g_assert_nonnull(tempfilename);
 
 
5341
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5342
  g_assert_cmpint(questionfile, >, 0);
 
 
5343
  FILE *qf = fdopen(questionfile, "w");
 
 
5344
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
 
5345
                          PRIuMAX "\nNotAfter=\n",
 
 
5346
                          (uintmax_t)getpid()), >, 0);
 
 
5347
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5349
  char *const filename = strdup(tempfilename);
 
 
5350
  g_assert_nonnull(filename);
 
 
5351
  task_context task = {
 
 
5352
    .func=open_and_parse_question,
 
 
5353
    .question_filename=filename,
 
 
5355
    .password=&password,
 
 
5357
    .cancelled_filenames=&cancelled_filenames,
 
 
5358
    .current_time=¤t_time,
 
 
5359
    .mandos_client_exited=&mandos_client_exited,
 
 
5360
    .password_is_read=&password_is_read,
 
 
5362
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5363
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5365
  __attribute__((cleanup(cleanup_string)))
 
 
5366
    char *socket_filename = strdup("/nonexistent");
 
 
5367
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
5368
        .func=connect_question_socket,
 
 
5369
        .question_filename=tempfilename,
 
 
5370
        .filename=socket_filename,
 
 
5372
        .password=&password,
 
 
5373
        .current_time=¤t_time,
 
 
5374
        .mandos_client_exited=&mandos_client_exited,
 
 
5375
        .password_is_read=&password_is_read,
 
 
5377
  g_assert_true(queue->next_run != 0);
 
 
5379
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5383
void assert_open_and_parse_question_with_notafter(const mono_microsecs
 
 
5385
                                                  const mono_microsecs
 
 
5387
                                                  const mono_microsecs
 
 
5389
  __attribute__((cleanup(cleanup_close)))
 
 
5390
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5391
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5392
  __attribute__((cleanup(string_set_clear)))
 
 
5393
    string_set cancelled_filenames = {};
 
 
5394
  buffer password = {};
 
 
5395
  bool mandos_client_exited = false;
 
 
5396
  bool password_is_read = false;
 
 
5397
  __attribute__((cleanup(cleanup_queue)))
 
 
5398
    task_queue *queue = create_queue();
 
 
5399
  g_assert_nonnull(queue);
 
 
5400
  queue->next_run = next_queue_run;
 
 
5402
  __attribute__((cleanup(cleanup_string)))
 
 
5403
    char *tempfilename = strdup("/tmp/mandosXXXXXX");
 
 
5404
  g_assert_nonnull(tempfilename);
 
 
5405
  int questionfile = mkostemp(tempfilename, O_CLOEXEC);
 
 
5406
  g_assert_cmpint(questionfile, >, 0);
 
 
5407
  FILE *qf = fdopen(questionfile, "w");
 
 
5408
  g_assert_cmpint(fprintf(qf, "[Ask]\nSocket=/nonexistent\nPID=%"
 
 
5409
                          PRIuMAX "\nNotAfter=%" PRIuMAX "\n",
 
 
5410
                          (uintmax_t)getpid(), notafter), >, 0);
 
 
5411
  g_assert_cmpint(fclose(qf), ==, 0);
 
 
5413
  char *const filename = strdup(tempfilename);
 
 
5414
  g_assert_nonnull(filename);
 
 
5415
  task_context task = {
 
 
5416
    .func=open_and_parse_question,
 
 
5417
    .question_filename=filename,
 
 
5419
    .password=&password,
 
 
5421
    .cancelled_filenames=&cancelled_filenames,
 
 
5422
    .current_time=¤t_time,
 
 
5423
    .mandos_client_exited=&mandos_client_exited,
 
 
5424
    .password_is_read=&password_is_read,
 
 
5426
  task.func(task, queue);
 
 
5428
  if(queue->length >= 1){
 
 
5429
    __attribute__((cleanup(cleanup_string)))
 
 
5430
      char *socket_filename = strdup("/nonexistent");
 
 
5431
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
5432
          .func=connect_question_socket,
 
 
5433
          .filename=socket_filename,
 
 
5435
          .password=&password,
 
 
5436
          .current_time=¤t_time,
 
 
5437
          .cancelled_filenames=&cancelled_filenames,
 
 
5438
          .mandos_client_exited=&mandos_client_exited,
 
 
5439
          .password_is_read=&password_is_read,
 
 
5441
    g_assert_true(queue->next_run != 0);
 
 
5445
    g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5446
  } else if(current_time >= notafter) {
 
 
5447
    g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5449
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
5450
          .func=cancel_old_question,
 
 
5451
          .question_filename=tempfilename,
 
 
5452
          .filename=tempfilename,
 
 
5454
          .cancelled_filenames=&cancelled_filenames,
 
 
5455
          .current_time=¤t_time,
 
 
5458
  g_assert_true(queue->next_run == 1);
 
 
5460
  g_assert_cmpint(unlink(tempfilename), ==, 0);
 
 
5464
test_open_and_parse_question_notafter_0(__attribute__((unused))
 
 
5465
                                        test_fixture *fixture,
 
 
5466
                                        __attribute__((unused))
 
 
5467
                                        gconstpointer user_data){
 
 
5468
  /* current_time, notafter, next_queue_run */
 
 
5469
  assert_open_and_parse_question_with_notafter(0, 0, 0);
 
 
5473
test_open_and_parse_question_notafter_1(__attribute__((unused))
 
 
5474
                                        test_fixture *fixture,
 
 
5475
                                        __attribute__((unused))
 
 
5476
                                        gconstpointer user_data){
 
 
5477
  /* current_time, notafter, next_queue_run */
 
 
5478
  assert_open_and_parse_question_with_notafter(0, 1, 0);
 
 
5482
test_open_and_parse_question_notafter_1_1(__attribute__((unused))
 
 
5483
                                          test_fixture *fixture,
 
 
5484
                                          __attribute__((unused))
 
 
5485
                                          gconstpointer user_data){
 
 
5486
  /* current_time, notafter, next_queue_run */
 
 
5487
  assert_open_and_parse_question_with_notafter(0, 1, 1);
 
 
5491
test_open_and_parse_question_notafter_1_2(__attribute__((unused))
 
 
5492
                                          test_fixture *fixture,
 
 
5493
                                          __attribute__((unused))
 
 
5494
                                          gconstpointer user_data){
 
 
5495
  /* current_time, notafter, next_queue_run */
 
 
5496
  assert_open_and_parse_question_with_notafter(0, 1, 2);
 
 
5500
test_open_and_parse_question_equal_notafter(__attribute__((unused))
 
 
5501
                                            test_fixture *fixture,
 
 
5502
                                            __attribute__((unused))
 
 
5503
                                            gconstpointer user_data){
 
 
5504
  /* current_time, notafter, next_queue_run */
 
 
5505
  assert_open_and_parse_question_with_notafter(1, 1, 0);
 
 
5509
test_open_and_parse_question_late_notafter(__attribute__((unused))
 
 
5510
                                           test_fixture *fixture,
 
 
5511
                                           __attribute__((unused))
 
 
5512
                                           gconstpointer user_data){
 
 
5513
  /* current_time, notafter, next_queue_run */
 
 
5514
  assert_open_and_parse_question_with_notafter(2, 1, 0);
 
 
5517
static void assert_cancel_old_question_param(const mono_microsecs
 
 
5519
                                             const mono_microsecs
 
 
5521
                                             const mono_microsecs
 
 
5523
                                             const mono_microsecs
 
 
5525
  __attribute__((cleanup(string_set_clear)))
 
 
5526
    string_set cancelled_filenames = {};
 
 
5527
  __attribute__((cleanup(cleanup_queue)))
 
 
5528
    task_queue *queue = create_queue();
 
 
5529
  g_assert_nonnull(queue);
 
 
5530
  queue->next_run = next_queue_run;
 
 
5532
  char *const question_filename = strdup("/nonexistent");
 
 
5533
  g_assert_nonnull(question_filename);
 
 
5534
  task_context task = {
 
 
5535
    .func=cancel_old_question,
 
 
5536
    .question_filename=question_filename,
 
 
5537
    .filename=question_filename,
 
 
5539
    .cancelled_filenames=&cancelled_filenames,
 
 
5540
    .current_time=¤t_time,
 
 
5542
  task.func(task, queue);
 
 
5544
  if(current_time >= notafter){
 
 
5545
    g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5546
    g_assert_true(string_set_contains(cancelled_filenames,
 
 
5549
    g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
5550
          .func=cancel_old_question,
 
 
5551
          .question_filename=question_filename,
 
 
5552
          .filename=question_filename,
 
 
5554
          .cancelled_filenames=&cancelled_filenames,
 
 
5555
          .current_time=¤t_time,
 
 
5558
    g_assert_false(string_set_contains(cancelled_filenames,
 
 
5559
                                       question_filename));
 
 
5561
  g_assert_cmpuint((unsigned int)queue->next_run, ==,
 
 
5562
                   (unsigned int)next_set_to);
 
 
5565
static void test_cancel_old_question_0_1_2(__attribute__((unused))
 
 
5566
                                           test_fixture *fixture,
 
 
5567
                                           __attribute__((unused))
 
 
5568
                                           gconstpointer user_data){
 
 
5569
  /* next_queue_run unset,
 
 
5570
     cancellation should happen because time has come,
 
 
5571
     next_queue_run should be unchanged */
 
 
5572
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5573
  assert_cancel_old_question_param(0, 1, 2, 0);
 
 
5576
static void test_cancel_old_question_0_2_1(__attribute__((unused))
 
 
5577
                                           test_fixture *fixture,
 
 
5578
                                           __attribute__((unused))
 
 
5579
                                           gconstpointer user_data){
 
 
5580
  /* If next_queue_run is 0, meaning unset, and notafter is 2,
 
 
5581
     and current_time is not yet notafter or greater,
 
 
5582
     update value of next_queue_run to value of notafter */
 
 
5583
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5584
  assert_cancel_old_question_param(0, 2, 1, 2);
 
 
5587
static void test_cancel_old_question_1_2_3(__attribute__((unused))
 
 
5588
                                           test_fixture *fixture,
 
 
5589
                                           __attribute__((unused))
 
 
5590
                                           gconstpointer user_data){
 
 
5591
  /* next_queue_run 1,
 
 
5592
     cancellation should happen because time has come,
 
 
5593
     next_queue_run should be unchanged */
 
 
5594
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5595
  assert_cancel_old_question_param(1, 2, 3, 1);
 
 
5598
static void test_cancel_old_question_1_3_2(__attribute__((unused))
 
 
5599
                                           test_fixture *fixture,
 
 
5600
                                           __attribute__((unused))
 
 
5601
                                           gconstpointer user_data){
 
 
5602
  /* If next_queue_run is set,
 
 
5603
     and current_time is not yet notafter or greater,
 
 
5604
     and notafter is larger than next_queue_run
 
 
5605
     next_queue_run should be unchanged */
 
 
5606
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5607
  assert_cancel_old_question_param(1, 3, 2, 1);
 
 
5610
static void test_cancel_old_question_2_1_3(__attribute__((unused))
 
 
5611
                                           test_fixture *fixture,
 
 
5612
                                           __attribute__((unused))
 
 
5613
                                           gconstpointer user_data){
 
 
5614
  /* next_queue_run 2,
 
 
5615
     cancellation should happen because time has come,
 
 
5616
     next_queue_run should be unchanged */
 
 
5617
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5618
  assert_cancel_old_question_param(2, 1, 3, 2);
 
 
5621
static void test_cancel_old_question_2_3_1(__attribute__((unused))
 
 
5622
                                           test_fixture *fixture,
 
 
5623
                                           __attribute__((unused))
 
 
5624
                                           gconstpointer user_data){
 
 
5625
  /* If next_queue_run is set,
 
 
5626
     and current_time is not yet notafter or greater,
 
 
5627
     and notafter is larger than next_queue_run
 
 
5628
     next_queue_run should be unchanged */
 
 
5629
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5630
  assert_cancel_old_question_param(2, 3, 1, 2);
 
 
5633
static void test_cancel_old_question_3_1_2(__attribute__((unused))
 
 
5634
                                           test_fixture *fixture,
 
 
5635
                                           __attribute__((unused))
 
 
5636
                                           gconstpointer user_data){
 
 
5637
  /* next_queue_run 3,
 
 
5638
     cancellation should happen because time has come,
 
 
5639
     next_queue_run should be unchanged */
 
 
5640
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5641
  assert_cancel_old_question_param(3, 1, 2, 3);
 
 
5644
static void test_cancel_old_question_3_2_1(__attribute__((unused))
 
 
5645
                                           test_fixture *fixture,
 
 
5646
                                           __attribute__((unused))
 
 
5647
                                           gconstpointer user_data){
 
 
5648
  /* If next_queue_run is set,
 
 
5649
     and current_time is not yet notafter or greater,
 
 
5650
     and notafter is smaller than next_queue_run
 
 
5651
     update value of next_queue_run to value of notafter */
 
 
5652
  /* next_queue_run, notafter, current_time, next_set_to */
 
 
5653
  assert_cancel_old_question_param(3, 2, 1, 2);
 
 
5657
test_connect_question_socket_name_too_long(__attribute__((unused))
 
 
5658
                                           test_fixture *fixture,
 
 
5659
                                           __attribute__((unused))
 
 
5660
                                           gconstpointer user_data){
 
 
5661
  __attribute__((cleanup(cleanup_close)))
 
 
5662
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5663
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5664
  const char question_filename[] = "/nonexistent/question";
 
 
5665
  __attribute__((cleanup(string_set_clear)))
 
 
5666
    string_set cancelled_filenames = {};
 
 
5667
  __attribute__((cleanup(cleanup_queue)))
 
 
5668
    task_queue *queue = create_queue();
 
 
5669
  g_assert_nonnull(queue);
 
 
5670
  __attribute__((cleanup(cleanup_string)))
 
 
5671
    char *tempdir = make_temporary_directory();
 
 
5672
  g_assert_nonnull(tempdir);
 
 
5673
  struct sockaddr_un unix_socket = { .sun_family=AF_LOCAL };
 
 
5674
  char socket_name[sizeof(unix_socket.sun_path)];
 
 
5675
  memset(socket_name, 'x', sizeof(socket_name));
 
 
5676
  socket_name[sizeof(socket_name)-1] = '\0';
 
 
5677
  char *filename = NULL;
 
 
5678
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
 
5680
  g_assert_nonnull(filename);
 
 
5682
  task_context task = {
 
 
5683
    .func=connect_question_socket,
 
 
5684
    .question_filename=strdup(question_filename),
 
 
5686
    .password=(buffer[]){{}},
 
 
5688
    .cancelled_filenames=&cancelled_filenames,
 
 
5689
    .mandos_client_exited=(bool[]){false},
 
 
5690
    .password_is_read=(bool[]){false},
 
 
5691
    .current_time=(mono_microsecs[]){0},
 
 
5693
  g_assert_nonnull(task.question_filename);
 
 
5694
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5696
  g_assert_true(string_set_contains(cancelled_filenames,
 
 
5697
                                    question_filename));
 
 
5698
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
5699
  g_assert_true(queue->next_run == 0);
 
 
5701
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
5705
void test_connect_question_socket_connect_fail(__attribute__((unused))
 
 
5706
                                               test_fixture *fixture,
 
 
5707
                                               __attribute__((unused))
 
 
5710
  __attribute__((cleanup(cleanup_close)))
 
 
5711
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5712
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5713
  const char question_filename[] = "/nonexistent/question";
 
 
5714
  __attribute__((cleanup(string_set_clear)))
 
 
5715
    string_set cancelled_filenames = {};
 
 
5716
  const mono_microsecs current_time = 3;
 
 
5717
  __attribute__((cleanup(cleanup_queue)))
 
 
5718
    task_queue *queue = create_queue();
 
 
5719
  g_assert_nonnull(queue);
 
 
5720
  __attribute__((cleanup(cleanup_string)))
 
 
5721
    char *tempdir = make_temporary_directory();
 
 
5722
  g_assert_nonnull(tempdir);
 
 
5723
  char socket_name[] = "nonexistent";
 
 
5724
  char *filename = NULL;
 
 
5725
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
 
5727
  g_assert_nonnull(filename);
 
 
5729
  task_context task = {
 
 
5730
    .func=connect_question_socket,
 
 
5731
    .question_filename=strdup(question_filename),
 
 
5733
    .password=(buffer[]){{}},
 
 
5735
    .cancelled_filenames=&cancelled_filenames,
 
 
5736
    .mandos_client_exited=(bool[]){false},
 
 
5737
    .password_is_read=(bool[]){false},
 
 
5738
    .current_time=¤t_time,
 
 
5740
  g_assert_nonnull(task.question_filename);
 
 
5741
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5743
  g_assert_nonnull(find_matching_task(queue, task));
 
 
5745
  g_assert_false(string_set_contains(cancelled_filenames,
 
 
5746
                                     question_filename));
 
 
5747
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5748
  g_assert_true(queue->next_run == 1000000 + current_time);
 
 
5750
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
5754
void test_connect_question_socket_bad_epoll(__attribute__((unused))
 
 
5755
                                            test_fixture *fixture,
 
 
5756
                                            __attribute__((unused))
 
 
5757
                                            gconstpointer user_data){
 
 
5758
  __attribute__((cleanup(cleanup_close)))
 
 
5759
    const int epoll_fd = open("/dev/null",
 
 
5760
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
 
5761
  __attribute__((cleanup(cleanup_string)))
 
 
5762
    char *const question_filename = strdup("/nonexistent/question");
 
 
5763
  g_assert_nonnull(question_filename);
 
 
5764
  __attribute__((cleanup(string_set_clear)))
 
 
5765
    string_set cancelled_filenames = {};
 
 
5766
  const mono_microsecs current_time = 5;
 
 
5767
  __attribute__((cleanup(cleanup_queue)))
 
 
5768
    task_queue *queue = create_queue();
 
 
5769
  g_assert_nonnull(queue);
 
 
5770
  __attribute__((cleanup(cleanup_string)))
 
 
5771
    char *tempdir = make_temporary_directory();
 
 
5772
  g_assert_nonnull(tempdir);
 
 
5773
  __attribute__((cleanup(cleanup_close)))
 
 
5774
    const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
 
 
5775
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 
 
5776
  g_assert_cmpint(sock_fd, >=, 0);
 
 
5777
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
 
 
5778
  const char socket_name[] = "socket_name";
 
 
5779
  __attribute__((cleanup(cleanup_string)))
 
 
5780
    char *filename = NULL;
 
 
5781
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
 
5783
  g_assert_nonnull(filename);
 
 
5784
  g_assert_cmpint((int)strlen(filename), <,
 
 
5785
                  (int)sizeof(sock_name.sun_path));
 
 
5786
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
 
 
5787
  sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
 
 
5788
  g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
 
 
5789
                            (socklen_t)SUN_LEN(&sock_name)), >=, 0);
 
 
5790
  task_context task = {
 
 
5791
    .func=connect_question_socket,
 
 
5792
    .question_filename=strdup(question_filename),
 
 
5794
    .password=(buffer[]){{}},
 
 
5795
    .filename=strdup(filename),
 
 
5796
    .cancelled_filenames=&cancelled_filenames,
 
 
5797
    .mandos_client_exited=(bool[]){false},
 
 
5798
    .password_is_read=(bool[]){false},
 
 
5799
    .current_time=¤t_time,
 
 
5801
  g_assert_nonnull(task.question_filename);
 
 
5802
  run_task_with_stderr_to_dev_null(task, queue);
 
 
5804
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5805
  const task_context *const added_task
 
 
5806
    = find_matching_task(queue, task);
 
 
5807
  g_assert_nonnull(added_task);
 
 
5808
  g_assert_true(queue->next_run == 1000000 + current_time);
 
 
5810
  g_assert_cmpint(unlink(filename), ==, 0);
 
 
5811
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
5815
void test_connect_question_socket_usable(__attribute__((unused))
 
 
5816
                                         test_fixture *fixture,
 
 
5817
                                         __attribute__((unused))
 
 
5818
                                         gconstpointer user_data){
 
 
5819
  __attribute__((cleanup(cleanup_close)))
 
 
5820
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5821
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5822
  __attribute__((cleanup(cleanup_string)))
 
 
5823
    char *const question_filename = strdup("/nonexistent/question");
 
 
5824
  g_assert_nonnull(question_filename);
 
 
5825
  __attribute__((cleanup(string_set_clear)))
 
 
5826
    string_set cancelled_filenames = {};
 
 
5827
  buffer password = {};
 
 
5828
  bool mandos_client_exited = false;
 
 
5829
  bool password_is_read = false;
 
 
5830
  const mono_microsecs current_time = 0;
 
 
5831
  __attribute__((cleanup(cleanup_queue)))
 
 
5832
    task_queue *queue = create_queue();
 
 
5833
  g_assert_nonnull(queue);
 
 
5834
  __attribute__((cleanup(cleanup_string)))
 
 
5835
    char *tempdir = make_temporary_directory();
 
 
5836
  g_assert_nonnull(tempdir);
 
 
5837
  __attribute__((cleanup(cleanup_close)))
 
 
5838
    const int sock_fd = socket(PF_LOCAL, SOCK_DGRAM
 
 
5839
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
 
 
5840
  g_assert_cmpint(sock_fd, >=, 0);
 
 
5841
  struct sockaddr_un sock_name = { .sun_family=AF_LOCAL };
 
 
5842
  const char socket_name[] = "socket_name";
 
 
5843
  __attribute__((cleanup(cleanup_string)))
 
 
5844
    char *filename = NULL;
 
 
5845
  g_assert_cmpint(asprintf(&filename, "%s/%s", tempdir, socket_name),
 
 
5847
  g_assert_nonnull(filename);
 
 
5848
  g_assert_cmpint((int)strlen(filename), <,
 
 
5849
                  (int)sizeof(sock_name.sun_path));
 
 
5850
  strncpy(sock_name.sun_path, filename, sizeof(sock_name.sun_path));
 
 
5851
  sock_name.sun_path[sizeof(sock_name.sun_path)-1] = '\0';
 
 
5852
  g_assert_cmpint((int)bind(sock_fd, (struct sockaddr *)&sock_name,
 
 
5853
                            (socklen_t)SUN_LEN(&sock_name)), >=, 0);
 
 
5854
  task_context task = {
 
 
5855
    .func=connect_question_socket,
 
 
5856
    .question_filename=strdup(question_filename),
 
 
5858
    .password=&password,
 
 
5859
    .filename=strdup(filename),
 
 
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(task.question_filename);
 
 
5866
  task.func(task, queue);
 
 
5868
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5869
  const task_context *const added_task
 
 
5870
    = find_matching_task(queue, (task_context){
 
 
5871
        .func=send_password_to_socket,
 
 
5872
        .question_filename=question_filename,
 
 
5875
        .password=&password,
 
 
5876
        .cancelled_filenames=&cancelled_filenames,
 
 
5877
        .mandos_client_exited=&mandos_client_exited,
 
 
5878
        .password_is_read=&password_is_read,
 
 
5879
        .current_time=¤t_time,
 
 
5881
  g_assert_nonnull(added_task);
 
 
5882
  g_assert_cmpint(added_task->fd, >, 0);
 
 
5884
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
 
5887
  const int fd = added_task->fd;
 
 
5888
  g_assert_cmpint(fd, >, 0);
 
 
5889
  g_assert_true(fd_has_cloexec_and_nonblock(fd));
 
 
5892
  char write_data[PIPE_BUF];
 
 
5894
    /* Construct test password buffer */
 
 
5895
    /* Start with + since that is what the real protocol uses */
 
 
5896
    write_data[0] = '+';
 
 
5897
    /* Set a special character at string end just to mark the end */
 
 
5898
    write_data[sizeof(write_data)-2] = 'y';
 
 
5899
    /* Set NUL at buffer end, as suggested by the protocol */
 
 
5900
    write_data[sizeof(write_data)-1] = '\0';
 
 
5901
    /* Fill rest of password with 'x' */
 
 
5902
    memset(write_data+1, 'x', sizeof(write_data)-3);
 
 
5903
    g_assert_cmpint((int)send(fd, write_data, sizeof(write_data),
 
 
5904
                              MSG_NOSIGNAL), ==, sizeof(write_data));
 
 
5907
  /* read from sock_fd */
 
 
5908
  char read_data[sizeof(write_data)];
 
 
5909
  g_assert_cmpint((int)read(sock_fd, read_data, sizeof(read_data)),
 
 
5910
                  ==, sizeof(read_data));
 
 
5912
  g_assert_true(memcmp(write_data, read_data, sizeof(write_data))
 
 
5915
  /* writing to sock_fd should fail */
 
 
5916
  g_assert_cmpint(send(sock_fd, write_data, sizeof(write_data),
 
 
5917
                       MSG_NOSIGNAL), <, 0);
 
 
5919
  /* reading from fd should fail */
 
 
5920
  g_assert_cmpint((int)recv(fd, read_data, sizeof(read_data),
 
 
5921
                            MSG_NOSIGNAL), <, 0);
 
 
5923
  g_assert_cmpint(unlink(filename), ==, 0);
 
 
5924
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
5928
test_send_password_to_socket_client_not_exited(__attribute__((unused))
 
 
5929
                                               test_fixture *fixture,
 
 
5930
                                               __attribute__((unused))
 
 
5933
  __attribute__((cleanup(cleanup_close)))
 
 
5934
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5935
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5936
  __attribute__((cleanup(cleanup_string)))
 
 
5937
    char *const question_filename = strdup("/nonexistent/question");
 
 
5938
  g_assert_nonnull(question_filename);
 
 
5939
  __attribute__((cleanup(cleanup_string)))
 
 
5940
    char *const filename = strdup("/nonexistent/socket");
 
 
5941
  g_assert_nonnull(filename);
 
 
5942
  __attribute__((cleanup(string_set_clear)))
 
 
5943
    string_set cancelled_filenames = {};
 
 
5944
  buffer password = {};
 
 
5945
  bool password_is_read = true;
 
 
5946
  __attribute__((cleanup(cleanup_queue)))
 
 
5947
    task_queue *queue = create_queue();
 
 
5948
  g_assert_nonnull(queue);
 
 
5950
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
5951
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
5953
  __attribute__((cleanup(cleanup_close)))
 
 
5954
    const int read_socket = socketfds[0];
 
 
5955
  const int write_socket = socketfds[1];
 
 
5956
  task_context task = {
 
 
5957
    .func=send_password_to_socket,
 
 
5958
    .question_filename=strdup(question_filename),
 
 
5959
    .filename=strdup(filename),
 
 
5962
    .password=&password,
 
 
5963
    .cancelled_filenames=&cancelled_filenames,
 
 
5964
    .mandos_client_exited=(bool[]){false},
 
 
5965
    .password_is_read=&password_is_read,
 
 
5966
    .current_time=(mono_microsecs[]){0},
 
 
5968
  g_assert_nonnull(task.question_filename);
 
 
5970
  task.func(task, queue);
 
 
5972
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
5974
  const task_context *const added_task
 
 
5975
    = find_matching_task(queue, task);
 
 
5976
  g_assert_nonnull(added_task);
 
 
5977
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
 
5978
  g_assert_true(password_is_read);
 
 
5980
  g_assert_cmpint(added_task->fd, >, 0);
 
 
5981
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
 
5986
test_send_password_to_socket_password_not_read(__attribute__((unused))
 
 
5987
                                               test_fixture *fixture,
 
 
5988
                                               __attribute__((unused))
 
 
5991
  __attribute__((cleanup(cleanup_close)))
 
 
5992
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
5993
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
5994
  __attribute__((cleanup(cleanup_string)))
 
 
5995
    char *const question_filename = strdup("/nonexistent/question");
 
 
5996
  g_assert_nonnull(question_filename);
 
 
5997
  __attribute__((cleanup(cleanup_string)))
 
 
5998
    char *const filename = strdup("/nonexistent/socket");
 
 
5999
  __attribute__((cleanup(string_set_clear)))
 
 
6000
    string_set cancelled_filenames = {};
 
 
6001
  buffer password = {};
 
 
6002
  __attribute__((cleanup(cleanup_queue)))
 
 
6003
    task_queue *queue = create_queue();
 
 
6004
  g_assert_nonnull(queue);
 
 
6006
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
6007
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
6009
  __attribute__((cleanup(cleanup_close)))
 
 
6010
    const int read_socket = socketfds[0];
 
 
6011
  const int write_socket = socketfds[1];
 
 
6012
  task_context task = {
 
 
6013
    .func=send_password_to_socket,
 
 
6014
    .question_filename=strdup(question_filename),
 
 
6015
    .filename=strdup(filename),
 
 
6018
    .password=&password,
 
 
6019
    .cancelled_filenames=&cancelled_filenames,
 
 
6020
    .mandos_client_exited=(bool[]){false},
 
 
6021
    .password_is_read=(bool[]){false},
 
 
6022
    .current_time=(mono_microsecs[]){0},
 
 
6024
  g_assert_nonnull(task.question_filename);
 
 
6026
  task.func(task, queue);
 
 
6028
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
6030
  const task_context *const added_task = find_matching_task(queue,
 
 
6032
  g_assert_nonnull(added_task);
 
 
6033
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
 
6034
  g_assert_true(queue->next_run == 0);
 
 
6036
  g_assert_cmpint(added_task->fd, >, 0);
 
 
6037
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
 
6042
void test_send_password_to_socket_EMSGSIZE(__attribute__((unused))
 
 
6043
                                           test_fixture *fixture,
 
 
6044
                                           __attribute__((unused))
 
 
6045
                                           gconstpointer user_data){
 
 
6046
  __attribute__((cleanup(cleanup_close)))
 
 
6047
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6048
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6049
  const char question_filename[] = "/nonexistent/question";
 
 
6050
  char *const filename = strdup("/nonexistent/socket");
 
 
6051
  __attribute__((cleanup(string_set_clear)))
 
 
6052
    string_set cancelled_filenames = {};
 
 
6055
  /* Find a message size which triggers EMSGSIZE */
 
 
6056
  __attribute__((cleanup(cleanup_string)))
 
 
6057
    char *message_buffer = NULL;
 
 
6058
  size_t message_size = PIPE_BUF + 1;
 
 
6059
  for(ssize_t ssret = 0; ssret >= 0; message_size += 1024){
 
 
6060
    if(message_size >= 1024*1024*1024){ /* 1 GiB */
 
 
6061
      g_test_skip("Skipping EMSGSIZE test: Will not try 1GiB");
 
 
6064
    message_buffer = realloc(message_buffer, message_size);
 
 
6065
    if(message_buffer == NULL){
 
 
6066
      g_test_skip("Skipping EMSGSIZE test");
 
 
6067
      g_test_message("Failed to malloc() %" PRIuMAX " bytes",
 
 
6068
                     (uintmax_t)message_size);
 
 
6071
    /* Fill buffer with 'x' */
 
 
6072
    memset(message_buffer, 'x', message_size);
 
 
6073
    /* Create a new socketpair for each message size to avoid having
 
 
6074
       to empty the pipe by reading the message to a separate buffer
 
 
6076
    g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
6077
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
6079
    ssret = send(socketfds[1], message_buffer, message_size,
 
 
6081
    error_t saved_errno = errno;
 
 
6082
    g_assert_cmpint(close(socketfds[0]), ==, 0);
 
 
6083
    g_assert_cmpint(close(socketfds[1]), ==, 0);
 
 
6086
      if(saved_errno != EMSGSIZE) {
 
 
6087
        g_test_skip("Skipping EMSGSIZE test");
 
 
6088
        g_test_message("Error on send(%" PRIuMAX " bytes): %s",
 
 
6089
                       (uintmax_t)message_size,
 
 
6090
                       strerror(saved_errno));
 
 
6094
    } else if(ssret != (ssize_t)message_size){
 
 
6095
      g_test_skip("Skipping EMSGSIZE test");
 
 
6096
      g_test_message("Partial send(): %" PRIuMAX " of %" PRIdMAX
 
 
6097
                     " bytes", (uintmax_t)ssret,
 
 
6098
                     (intmax_t)message_size);
 
 
6102
  g_test_message("EMSGSIZE triggered by %" PRIdMAX " bytes",
 
 
6103
                 (intmax_t)message_size);
 
 
6106
    .data=message_buffer,
 
 
6107
    .length=message_size - 2,   /* Compensate for added '+' and NUL */
 
 
6108
    .allocated=message_size,
 
 
6110
  if(mlock(password.data, password.allocated) != 0){
 
 
6111
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
6114
  __attribute__((cleanup(cleanup_queue)))
 
 
6115
    task_queue *queue = create_queue();
 
 
6116
  g_assert_nonnull(queue);
 
 
6117
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
6118
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
6120
  __attribute__((cleanup(cleanup_close)))
 
 
6121
    const int read_socket = socketfds[0];
 
 
6122
  __attribute__((cleanup(cleanup_close)))
 
 
6123
    const int write_socket = socketfds[1];
 
 
6124
  task_context task = {
 
 
6125
    .func=send_password_to_socket,
 
 
6126
    .question_filename=strdup(question_filename),
 
 
6130
    .password=&password,
 
 
6131
    .cancelled_filenames=&cancelled_filenames,
 
 
6132
    .mandos_client_exited=(bool[]){true},
 
 
6133
    .password_is_read=(bool[]){true},
 
 
6134
    .current_time=(mono_microsecs[]){0},
 
 
6136
  g_assert_nonnull(task.question_filename);
 
 
6138
  run_task_with_stderr_to_dev_null(task, queue);
 
 
6140
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
6141
  g_assert_true(string_set_contains(cancelled_filenames,
 
 
6142
                                    question_filename));
 
 
6145
static void test_send_password_to_socket_retry(__attribute__((unused))
 
 
6146
                                               test_fixture *fixture,
 
 
6147
                                               __attribute__((unused))
 
 
6150
  __attribute__((cleanup(cleanup_close)))
 
 
6151
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6152
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6153
  __attribute__((cleanup(cleanup_string)))
 
 
6154
    char *const question_filename = strdup("/nonexistent/question");
 
 
6155
  g_assert_nonnull(question_filename);
 
 
6156
  __attribute__((cleanup(cleanup_string)))
 
 
6157
    char *const filename = strdup("/nonexistent/socket");
 
 
6158
  g_assert_nonnull(filename);
 
 
6159
  __attribute__((cleanup(string_set_clear)))
 
 
6160
    string_set cancelled_filenames = {};
 
 
6161
  __attribute__((cleanup(cleanup_buffer)))
 
 
6162
    buffer password = {};
 
 
6164
  __attribute__((cleanup(cleanup_queue)))
 
 
6165
    task_queue *queue = create_queue();
 
 
6166
  g_assert_nonnull(queue);
 
 
6168
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
6169
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
6171
  __attribute__((cleanup(cleanup_close)))
 
 
6172
    const int read_socket = socketfds[0];
 
 
6173
  const int write_socket = socketfds[1];
 
 
6174
  /* Close the server side socket to force ECONNRESET on client */
 
 
6175
  g_assert_cmpint(close(read_socket), ==, 0);
 
 
6176
  task_context task = {
 
 
6177
    .func=send_password_to_socket,
 
 
6178
    .question_filename=strdup(question_filename),
 
 
6179
    .filename=strdup(filename),
 
 
6182
    .password=&password,
 
 
6183
    .cancelled_filenames=&cancelled_filenames,
 
 
6184
    .mandos_client_exited=(bool[]){true},
 
 
6185
    .password_is_read=(bool[]){true},
 
 
6186
    .current_time=(mono_microsecs[]){0},
 
 
6188
  g_assert_nonnull(task.question_filename);
 
 
6190
  task.func(task, queue);
 
 
6192
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
6194
  const task_context *const added_task = find_matching_task(queue,
 
 
6196
  g_assert_nonnull(added_task);
 
 
6197
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
 
6199
  g_assert_true(epoll_set_contains(epoll_fd, added_task->fd,
 
 
6204
void test_send_password_to_socket_bad_epoll(__attribute__((unused))
 
 
6205
                                            test_fixture *fixture,
 
 
6206
                                            __attribute__((unused))
 
 
6207
                                            gconstpointer user_data){
 
 
6208
  __attribute__((cleanup(cleanup_close)))
 
 
6209
    const int epoll_fd = open("/dev/null",
 
 
6210
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
 
6211
  __attribute__((cleanup(cleanup_string)))
 
 
6212
    char *const question_filename = strdup("/nonexistent/question");
 
 
6213
  g_assert_nonnull(question_filename);
 
 
6214
  __attribute__((cleanup(cleanup_string)))
 
 
6215
    char *const filename = strdup("/nonexistent/socket");
 
 
6216
  g_assert_nonnull(filename);
 
 
6217
  __attribute__((cleanup(string_set_clear)))
 
 
6218
    string_set cancelled_filenames = {};
 
 
6219
  __attribute__((cleanup(cleanup_buffer)))
 
 
6220
    buffer password = {};
 
 
6222
  const mono_microsecs current_time = 11;
 
 
6223
  __attribute__((cleanup(cleanup_queue)))
 
 
6224
    task_queue *queue = create_queue();
 
 
6225
  g_assert_nonnull(queue);
 
 
6227
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
6228
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
6230
  __attribute__((cleanup(cleanup_close)))
 
 
6231
    const int read_socket = socketfds[0];
 
 
6232
  const int write_socket = socketfds[1];
 
 
6233
  /* Close the server side socket to force ECONNRESET on client */
 
 
6234
  g_assert_cmpint(close(read_socket), ==, 0);
 
 
6235
  task_context task = {
 
 
6236
    .func=send_password_to_socket,
 
 
6237
    .question_filename=strdup(question_filename),
 
 
6238
    .filename=strdup(filename),
 
 
6241
    .password=&password,
 
 
6242
    .cancelled_filenames=&cancelled_filenames,
 
 
6243
    .mandos_client_exited=(bool[]){true},
 
 
6244
    .password_is_read=(bool[]){true},
 
 
6245
    .current_time=¤t_time,
 
 
6247
  g_assert_nonnull(task.question_filename);
 
 
6249
  run_task_with_stderr_to_dev_null(task, queue);
 
 
6251
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
6253
  const task_context *const added_task = find_matching_task(queue,
 
 
6255
  g_assert_nonnull(added_task);
 
 
6256
  g_assert_true(queue->next_run == current_time + 1000000);
 
 
6257
  g_assert_cmpuint((unsigned int)password.length, ==, 0);
 
 
6260
static void assert_send_password_to_socket_password(buffer password){
 
 
6261
  __attribute__((cleanup(cleanup_close)))
 
 
6262
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6263
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6264
  char *const question_filename = strdup("/nonexistent/question");
 
 
6265
  g_assert_nonnull(question_filename);
 
 
6266
  char *const filename = strdup("/nonexistent/socket");
 
 
6267
  g_assert_nonnull(filename);
 
 
6268
  __attribute__((cleanup(string_set_clear)))
 
 
6269
    string_set cancelled_filenames = {};
 
 
6271
  __attribute__((cleanup(cleanup_queue)))
 
 
6272
    task_queue *queue = create_queue();
 
 
6273
  g_assert_nonnull(queue);
 
 
6275
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
 
6276
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
 
6278
  __attribute__((cleanup(cleanup_close)))
 
 
6279
    const int read_socket = socketfds[0];
 
 
6280
  const int write_socket = socketfds[1];
 
 
6281
  task_context task = {
 
 
6282
    .func=send_password_to_socket,
 
 
6283
    .question_filename=question_filename,
 
 
6287
    .password=&password,
 
 
6288
    .cancelled_filenames=&cancelled_filenames,
 
 
6289
    .mandos_client_exited=(bool[]){true},
 
 
6290
    .password_is_read=(bool[]){true},
 
 
6291
    .current_time=(mono_microsecs[]){0},
 
 
6294
  char *expected_written_data = malloc(password.length + 2);
 
 
6295
  g_assert_nonnull(expected_written_data);
 
 
6296
  expected_written_data[0] = '+';
 
 
6297
  expected_written_data[password.length + 1] = '\0';
 
 
6298
  if(password.length > 0){
 
 
6299
    g_assert_nonnull(password.data);
 
 
6300
    memcpy(expected_written_data + 1, password.data, password.length);
 
 
6303
  task.func(task, queue);
 
 
6306
  g_assert_cmpint((int)read(read_socket, buf, PIPE_BUF), ==,
 
 
6307
                  (int)(password.length + 2));
 
 
6308
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
6310
  g_assert_true(memcmp(expected_written_data, buf,
 
 
6311
                       password.length + 2) == 0);
 
 
6313
  g_assert_true(epoll_set_does_not_contain(epoll_fd, write_socket));
 
 
6315
  free(expected_written_data);
 
 
6319
test_send_password_to_socket_null_password(__attribute__((unused))
 
 
6320
                                           test_fixture *fixture,
 
 
6321
                                           __attribute__((unused))
 
 
6322
                                           gconstpointer user_data){
 
 
6323
  __attribute__((cleanup(cleanup_buffer)))
 
 
6324
    buffer password = {};
 
 
6325
  assert_send_password_to_socket_password(password);
 
 
6329
test_send_password_to_socket_empty_password(__attribute__((unused))
 
 
6330
                                            test_fixture *fixture,
 
 
6331
                                            __attribute__((unused))
 
 
6332
                                            gconstpointer user_data){
 
 
6333
  __attribute__((cleanup(cleanup_buffer)))
 
 
6335
    .data=malloc(1),           /* because malloc(0) may return NULL */
 
 
6337
    .allocated=0,               /* deliberate lie */
 
 
6339
  g_assert_nonnull(password.data);
 
 
6340
  assert_send_password_to_socket_password(password);
 
 
6344
test_send_password_to_socket_empty_str_pass(__attribute__((unused))
 
 
6345
                                            test_fixture *fixture,
 
 
6346
                                            __attribute__((unused))
 
 
6347
                                            gconstpointer user_data){
 
 
6348
  __attribute__((cleanup(cleanup_buffer)))
 
 
6354
  if(mlock(password.data, password.allocated) != 0){
 
 
6355
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
6357
  assert_send_password_to_socket_password(password);
 
 
6361
test_send_password_to_socket_text_password(__attribute__((unused))
 
 
6362
                                           test_fixture *fixture,
 
 
6363
                                           __attribute__((unused))
 
 
6364
                                           gconstpointer user_data){
 
 
6365
  const char dummy_test_password[] = "dummy test password";
 
 
6366
  __attribute__((cleanup(cleanup_buffer)))
 
 
6368
    .data = strdup(dummy_test_password),
 
 
6369
    .length = strlen(dummy_test_password),
 
 
6370
    .allocated = sizeof(dummy_test_password),
 
 
6372
  if(mlock(password.data, password.allocated) != 0){
 
 
6373
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
6375
  assert_send_password_to_socket_password(password);
 
 
6379
test_send_password_to_socket_binary_password(__attribute__((unused))
 
 
6380
                                             test_fixture *fixture,
 
 
6381
                                             __attribute__((unused))
 
 
6382
                                             gconstpointer user_data){
 
 
6383
  __attribute__((cleanup(cleanup_buffer)))
 
 
6389
  g_assert_nonnull(password.data);
 
 
6390
  if(mlock(password.data, password.allocated) != 0){
 
 
6391
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
6393
  char c = 1;                   /* Start at 1, avoiding NUL */
 
 
6394
  for(int i=0; i < 255; i++){
 
 
6395
    password.data[i] = c++;
 
 
6397
  assert_send_password_to_socket_password(password);
 
 
6401
test_send_password_to_socket_nuls_in_password(__attribute__((unused))
 
 
6402
                                              test_fixture *fixture,
 
 
6403
                                              __attribute__((unused))
 
 
6406
  char test_password[] = {'\0', 'a', '\0', 'b', '\0', 'c', '\0'};
 
 
6407
  __attribute__((cleanup(cleanup_buffer)))
 
 
6409
    .data=malloc(sizeof(test_password)),
 
 
6410
    .length=sizeof(test_password),
 
 
6411
    .allocated=sizeof(test_password),
 
 
6413
  g_assert_nonnull(password.data);
 
 
6414
  if(mlock(password.data, password.allocated) !=0){
 
 
6415
    g_assert_true(errno == EPERM or errno == ENOMEM);
 
 
6417
  memcpy(password.data, test_password, password.allocated);
 
 
6418
  assert_send_password_to_socket_password(password);
 
 
6421
static bool assert_add_existing_questions_to_devnull(task_queue
 
 
6434
static void test_add_existing_questions_ENOENT(__attribute__((unused))
 
 
6435
                                               test_fixture *fixture,
 
 
6436
                                               __attribute__((unused))
 
 
6439
  __attribute__((cleanup(cleanup_queue)))
 
 
6440
    task_queue *queue = create_queue();
 
 
6441
  g_assert_nonnull(queue);
 
 
6442
  __attribute__((cleanup(cleanup_close)))
 
 
6443
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6444
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6445
  __attribute__((cleanup(string_set_clear)))
 
 
6446
    string_set cancelled_filenames = {};
 
 
6448
  g_assert_false(assert_add_existing_questions_to_devnull
 
 
6451
                  (buffer[]){{}}, /* password */
 
 
6452
                  &cancelled_filenames,
 
 
6453
                  (mono_microsecs[]){0}, /* current_time */
 
 
6454
                  (bool[]){false},       /* mandos_client_exited */
 
 
6455
                  (bool[]){false},       /* password_is_read */
 
 
6456
                  "/nonexistent"));      /* dirname */
 
 
6458
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
6462
bool assert_add_existing_questions_to_devnull(task_queue
 
 
6469
                                              *cancelled_filenames,
 
 
6470
                                              const mono_microsecs
 
 
6471
                                              *const current_time,
 
 
6473
                                              mandos_client_exited,
 
 
6478
  __attribute__((cleanup(cleanup_close)))
 
 
6479
    const int devnull_fd = open("/dev/null",
 
 
6480
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
 
6481
  g_assert_cmpint(devnull_fd, >=, 0);
 
 
6482
  __attribute__((cleanup(cleanup_close)))
 
 
6483
    const int real_stderr_fd = dup(STDERR_FILENO);
 
 
6484
  g_assert_cmpint(real_stderr_fd, >=, 0);
 
 
6485
  dup2(devnull_fd, STDERR_FILENO);
 
 
6486
  const bool ret = add_existing_questions(queue, epoll_fd, password,
 
 
6487
                                          cancelled_filenames,
 
 
6489
                                          mandos_client_exited,
 
 
6490
                                          password_is_read, dirname);
 
 
6491
  dup2(real_stderr_fd, STDERR_FILENO);
 
 
6496
void test_add_existing_questions_no_questions(__attribute__((unused))
 
 
6497
                                              test_fixture *fixture,
 
 
6498
                                              __attribute__((unused))
 
 
6501
  __attribute__((cleanup(cleanup_queue)))
 
 
6502
    task_queue *queue = create_queue();
 
 
6503
  g_assert_nonnull(queue);
 
 
6504
  __attribute__((cleanup(cleanup_close)))
 
 
6505
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6506
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6507
  __attribute__((cleanup(string_set_clear)))
 
 
6508
    string_set cancelled_filenames = {};
 
 
6509
  __attribute__((cleanup(cleanup_string)))
 
 
6510
    char *tempdir = make_temporary_directory();
 
 
6511
  g_assert_nonnull(tempdir);
 
 
6513
  g_assert_false(assert_add_existing_questions_to_devnull
 
 
6516
                  (buffer[]){{}}, /* password */
 
 
6517
                  &cancelled_filenames,
 
 
6518
                  (mono_microsecs[]){0}, /* current_time */
 
 
6519
                  (bool[]){false},       /* mandos_client_exited */
 
 
6520
                  (bool[]){false},       /* password_is_read */
 
 
6523
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
6525
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
6528
static char *make_question_file_in_directory(const char *const);
 
 
6531
void test_add_existing_questions_one_question(__attribute__((unused))
 
 
6532
                                              test_fixture *fixture,
 
 
6533
                                              __attribute__((unused))
 
 
6536
  __attribute__((cleanup(cleanup_queue)))
 
 
6537
    task_queue *queue = create_queue();
 
 
6538
  g_assert_nonnull(queue);
 
 
6539
  __attribute__((cleanup(cleanup_close)))
 
 
6540
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6541
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6542
  __attribute__((cleanup(cleanup_buffer)))
 
 
6543
    buffer password = {};
 
 
6544
  __attribute__((cleanup(string_set_clear)))
 
 
6545
    string_set cancelled_filenames = {};
 
 
6546
  const mono_microsecs current_time = 0;
 
 
6547
  bool mandos_client_exited = false;
 
 
6548
  bool password_is_read = false;
 
 
6549
  __attribute__((cleanup(cleanup_string)))
 
 
6550
    char *tempdir = make_temporary_directory();
 
 
6551
  g_assert_nonnull(tempdir);
 
 
6552
  __attribute__((cleanup(cleanup_string)))
 
 
6553
    char *question_filename
 
 
6554
    = make_question_file_in_directory(tempdir);
 
 
6555
  g_assert_nonnull(question_filename);
 
 
6557
  g_assert_true(assert_add_existing_questions_to_devnull
 
 
6561
                 &cancelled_filenames,
 
 
6563
                 &mandos_client_exited,
 
 
6567
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
6569
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
6570
        .func=open_and_parse_question,
 
 
6572
        .filename=question_filename,
 
 
6573
        .question_filename=question_filename,
 
 
6574
        .password=&password,
 
 
6575
        .cancelled_filenames=&cancelled_filenames,
 
 
6576
        .current_time=¤t_time,
 
 
6577
        .mandos_client_exited=&mandos_client_exited,
 
 
6578
        .password_is_read=&password_is_read,
 
 
6581
  g_assert_true(queue->next_run == 1);
 
 
6583
  g_assert_cmpint(unlink(question_filename), ==, 0);
 
 
6584
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
6587
static char *make_question_file_in_directory(const char
 
 
6589
  return make_temporary_prefixed_file_in_directory("ask.", dir);
 
 
6593
void test_add_existing_questions_two_questions(__attribute__((unused))
 
 
6594
                                               test_fixture *fixture,
 
 
6595
                                               __attribute__((unused))
 
 
6598
  __attribute__((cleanup(cleanup_queue)))
 
 
6599
    task_queue *queue = create_queue();
 
 
6600
  g_assert_nonnull(queue);
 
 
6601
  __attribute__((cleanup(cleanup_close)))
 
 
6602
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6603
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6604
  __attribute__((cleanup(cleanup_buffer)))
 
 
6605
    buffer password = {};
 
 
6606
  __attribute__((cleanup(string_set_clear)))
 
 
6607
    string_set cancelled_filenames = {};
 
 
6608
  const mono_microsecs current_time = 0;
 
 
6609
  bool mandos_client_exited = false;
 
 
6610
  bool password_is_read = false;
 
 
6611
  __attribute__((cleanup(cleanup_string)))
 
 
6612
    char *tempdir = make_temporary_directory();
 
 
6613
  g_assert_nonnull(tempdir);
 
 
6614
  __attribute__((cleanup(cleanup_string)))
 
 
6615
    char *question_filename1
 
 
6616
    = make_question_file_in_directory(tempdir);
 
 
6617
  g_assert_nonnull(question_filename1);
 
 
6618
  __attribute__((cleanup(cleanup_string)))
 
 
6619
    char *question_filename2
 
 
6620
    = make_question_file_in_directory(tempdir);
 
 
6621
  g_assert_nonnull(question_filename2);
 
 
6623
  g_assert_true(assert_add_existing_questions_to_devnull
 
 
6627
                 &cancelled_filenames,
 
 
6629
                 &mandos_client_exited,
 
 
6633
  g_assert_cmpuint((unsigned int)queue->length, ==, 2);
 
 
6635
  g_assert_true(queue->next_run == 1);
 
 
6637
  __attribute__((cleanup(string_set_clear)))
 
 
6638
    string_set seen_questions = {};
 
 
6640
  bool queue_contains_question_opener(char *const question_filename){
 
 
6641
    return(find_matching_task(queue, (task_context){
 
 
6642
          .func=open_and_parse_question,
 
 
6644
          .question_filename=question_filename,
 
 
6645
          .password=&password,
 
 
6646
          .cancelled_filenames=&cancelled_filenames,
 
 
6647
          .current_time=¤t_time,
 
 
6648
          .mandos_client_exited=&mandos_client_exited,
 
 
6649
          .password_is_read=&password_is_read,
 
 
6653
  g_assert_true(queue_contains_question_opener(question_filename1));
 
 
6654
  g_assert_true(queue_contains_question_opener(question_filename2));
 
 
6656
  g_assert_true(queue->next_run == 1);
 
 
6658
  g_assert_cmpint(unlink(question_filename1), ==, 0);
 
 
6659
  g_assert_cmpint(unlink(question_filename2), ==, 0);
 
 
6660
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
6664
test_add_existing_questions_non_questions(__attribute__((unused))
 
 
6665
                                          test_fixture *fixture,
 
 
6666
                                          __attribute__((unused))
 
 
6667
                                          gconstpointer user_data){
 
 
6668
  __attribute__((cleanup(cleanup_queue)))
 
 
6669
    task_queue *queue = create_queue();
 
 
6670
  g_assert_nonnull(queue);
 
 
6671
  __attribute__((cleanup(cleanup_close)))
 
 
6672
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6673
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6674
  __attribute__((cleanup(string_set_clear)))
 
 
6675
    string_set cancelled_filenames = {};
 
 
6676
  __attribute__((cleanup(cleanup_string)))
 
 
6677
    char *tempdir = make_temporary_directory();
 
 
6678
  g_assert_nonnull(tempdir);
 
 
6679
  __attribute__((cleanup(cleanup_string)))
 
 
6680
    char *question_filename1
 
 
6681
    = make_temporary_file_in_directory(tempdir);
 
 
6682
  g_assert_nonnull(question_filename1);
 
 
6683
  __attribute__((cleanup(cleanup_string)))
 
 
6684
    char *question_filename2
 
 
6685
    = make_temporary_file_in_directory(tempdir);
 
 
6686
  g_assert_nonnull(question_filename2);
 
 
6688
  g_assert_false(assert_add_existing_questions_to_devnull
 
 
6691
                  (buffer[]){{}}, /* password */
 
 
6692
                  &cancelled_filenames,
 
 
6693
                  (mono_microsecs[]){0}, /* current_time */
 
 
6694
                  (bool[]){false},       /* mandos_client_exited */
 
 
6695
                  (bool[]){false},       /* password_is_read */
 
 
6698
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
 
6700
  g_assert_cmpint(unlink(question_filename1), ==, 0);
 
 
6701
  g_assert_cmpint(unlink(question_filename2), ==, 0);
 
 
6702
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
6706
test_add_existing_questions_both_types(__attribute__((unused))
 
 
6707
                                       test_fixture *fixture,
 
 
6708
                                       __attribute__((unused))
 
 
6709
                                       gconstpointer user_data){
 
 
6710
  __attribute__((cleanup(cleanup_queue)))
 
 
6711
    task_queue *queue = create_queue();
 
 
6712
  g_assert_nonnull(queue);
 
 
6713
  __attribute__((cleanup(cleanup_close)))
 
 
6714
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6715
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6716
  __attribute__((cleanup(cleanup_buffer)))
 
 
6717
    buffer password = {};
 
 
6718
  __attribute__((cleanup(string_set_clear)))
 
 
6719
    string_set cancelled_filenames = {};
 
 
6720
  const mono_microsecs current_time = 0;
 
 
6721
  bool mandos_client_exited = false;
 
 
6722
  bool password_is_read = false;
 
 
6723
  __attribute__((cleanup(cleanup_string)))
 
 
6724
    char *tempdir = make_temporary_directory();
 
 
6725
  g_assert_nonnull(tempdir);
 
 
6726
  __attribute__((cleanup(cleanup_string)))
 
 
6727
    char *tempfilename1 = make_temporary_file_in_directory(tempdir);
 
 
6728
  g_assert_nonnull(tempfilename1);
 
 
6729
  __attribute__((cleanup(cleanup_string)))
 
 
6730
    char *tempfilename2 = make_temporary_file_in_directory(tempdir);
 
 
6731
  g_assert_nonnull(tempfilename2);
 
 
6732
  __attribute__((cleanup(cleanup_string)))
 
 
6733
    char *question_filename
 
 
6734
    = make_question_file_in_directory(tempdir);
 
 
6735
  g_assert_nonnull(question_filename);
 
 
6737
  g_assert_true(assert_add_existing_questions_to_devnull
 
 
6741
                 &cancelled_filenames,
 
 
6743
                 &mandos_client_exited,
 
 
6747
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
 
6749
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
 
6750
        .func=open_and_parse_question,
 
 
6752
        .filename=question_filename,
 
 
6753
        .question_filename=question_filename,
 
 
6754
        .password=&password,
 
 
6755
        .cancelled_filenames=&cancelled_filenames,
 
 
6756
        .current_time=¤t_time,
 
 
6757
        .mandos_client_exited=&mandos_client_exited,
 
 
6758
        .password_is_read=&password_is_read,
 
 
6761
  g_assert_true(queue->next_run == 1);
 
 
6763
  g_assert_cmpint(unlink(tempfilename1), ==, 0);
 
 
6764
  g_assert_cmpint(unlink(tempfilename2), ==, 0);
 
 
6765
  g_assert_cmpint(unlink(question_filename), ==, 0);
 
 
6766
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
 
6769
static void test_wait_for_event_timeout(__attribute__((unused))
 
 
6770
                                        test_fixture *fixture,
 
 
6771
                                        __attribute__((unused))
 
 
6772
                                        gconstpointer user_data){
 
 
6773
  __attribute__((cleanup(cleanup_close)))
 
 
6774
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6775
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6777
  g_assert_true(wait_for_event(epoll_fd, 1, 0));
 
 
6780
static void test_wait_for_event_event(__attribute__((unused))
 
 
6781
                                      test_fixture *fixture,
 
 
6782
                                      __attribute__((unused))
 
 
6783
                                      gconstpointer user_data){
 
 
6784
  __attribute__((cleanup(cleanup_close)))
 
 
6785
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6786
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6788
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
6789
  __attribute__((cleanup(cleanup_close)))
 
 
6790
    const int read_pipe = pipefds[0];
 
 
6791
  __attribute__((cleanup(cleanup_close)))
 
 
6792
    const int write_pipe = pipefds[1];
 
 
6793
  g_assert_cmpint(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, read_pipe,
 
 
6794
                            &(struct epoll_event)
 
 
6795
                            { .events=EPOLLIN | EPOLLRDHUP }), ==, 0);
 
 
6796
  g_assert_cmpint((int)write(write_pipe, "x", 1), ==, 1);
 
 
6798
  g_assert_true(wait_for_event(epoll_fd, 0, 0));
 
 
6801
static void test_wait_for_event_sigchld(test_fixture *fixture,
 
 
6802
                                        __attribute__((unused))
 
 
6803
                                        gconstpointer user_data){
 
 
6804
  const pid_t pid = fork();
 
 
6805
  if(pid == 0){         /* Child */
 
 
6806
    if(not restore_signal_handler(&fixture->orig_sigaction)){
 
 
6807
      _exit(EXIT_FAILURE);
 
 
6809
    if(not restore_sigmask(&fixture->orig_sigmask)){
 
 
6810
      _exit(EXIT_FAILURE);
 
 
6814
  g_assert_true(pid != -1);
 
 
6815
  __attribute__((cleanup(cleanup_close)))
 
 
6816
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6817
  g_assert_cmpint(epoll_fd, >=, 0);
 
 
6819
  g_assert_true(wait_for_event(epoll_fd, 0, 0));
 
 
6822
  g_assert_true(waitpid(pid, &status, 0) == pid);
 
 
6823
  g_assert_true(WIFEXITED(status));
 
 
6824
  g_assert_cmpint(WEXITSTATUS(status), ==, EXIT_SUCCESS);
 
 
6827
static void test_run_queue_zeroes_next_run(__attribute__((unused))
 
 
6828
                                           test_fixture *fixture,
 
 
6829
                                           __attribute__((unused))
 
 
6830
                                           gconstpointer user_data){
 
 
6831
  __attribute__((cleanup(cleanup_queue)))
 
 
6832
    task_queue *queue = create_queue();
 
 
6833
  g_assert_nonnull(queue);
 
 
6834
  queue->next_run = 1;
 
 
6835
  __attribute__((cleanup(cleanup_close)))
 
 
6836
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
 
6837
  __attribute__((cleanup(string_set_clear)))
 
 
6838
    string_set cancelled_filenames = {};
 
 
6839
  bool quit_now = false;
 
 
6841
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6842
  g_assert_false(quit_now);
 
 
6843
  g_assert_cmpuint((unsigned int)queue->next_run, ==, 0);
 
 
6847
void test_run_queue_clears_cancelled_filenames(__attribute__((unused))
 
 
6848
                                               test_fixture *fixture,
 
 
6849
                                               __attribute__((unused))
 
 
6852
  __attribute__((cleanup(cleanup_queue)))
 
 
6853
    task_queue *queue = create_queue();
 
 
6854
  g_assert_nonnull(queue);
 
 
6855
  __attribute__((cleanup(string_set_clear)))
 
 
6856
    string_set cancelled_filenames = {};
 
 
6857
  bool quit_now = false;
 
 
6858
  const char question_filename[] = "/nonexistent/question_filename";
 
 
6859
  g_assert_true(string_set_add(&cancelled_filenames,
 
 
6860
                               question_filename));
 
 
6862
  g_assert_true(add_to_queue(queue,
 
 
6863
                             (task_context){ .func=dummy_func }));
 
 
6865
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6866
  g_assert_false(quit_now);
 
 
6867
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
 
6868
  g_assert_false(string_set_contains(cancelled_filenames,
 
 
6869
                                     question_filename));
 
 
6873
void test_run_queue_skips_cancelled_filenames(__attribute__((unused))
 
 
6874
                                              test_fixture *fixture,
 
 
6875
                                              __attribute__((unused))
 
 
6878
  __attribute__((cleanup(cleanup_queue)))
 
 
6879
    task_queue *queue = create_queue();
 
 
6880
  g_assert_nonnull(queue);
 
 
6881
  __attribute__((cleanup(string_set_clear)))
 
 
6882
    string_set cancelled_filenames = {};
 
 
6883
  bool quit_now = false;
 
 
6885
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
6886
  __attribute__((cleanup(cleanup_close)))
 
 
6887
    const int read_pipe = pipefds[0];
 
 
6888
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
 
6889
  const char question_filename[] = "/nonexistent/question_filename";
 
 
6890
  g_assert_true(string_set_add(&cancelled_filenames,
 
 
6891
                               question_filename));
 
 
6892
  __attribute__((nonnull))
 
 
6893
    void quit_func(const task_context task,
 
 
6894
                   __attribute__((unused)) task_queue *const q){
 
 
6895
    g_assert_nonnull(task.quit_now);
 
 
6896
    *task.quit_now = true;
 
 
6898
  task_context task = {
 
 
6900
    .question_filename=strdup(question_filename),
 
 
6901
    .quit_now=&quit_now,
 
 
6904
  g_assert_nonnull(task.question_filename);
 
 
6906
  g_assert_true(add_to_queue(queue, task));
 
 
6908
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6909
  g_assert_false(quit_now);
 
 
6911
  /* read_pipe should be closed already */
 
 
6913
  bool read_pipe_closed = (close(read_pipe) == -1);
 
 
6914
  read_pipe_closed &= (errno == EBADF);
 
 
6915
  g_assert_true(read_pipe_closed);
 
 
6918
static void test_run_queue_one_task(__attribute__((unused))
 
 
6919
                                    test_fixture *fixture,
 
 
6920
                                    __attribute__((unused))
 
 
6921
                                    gconstpointer user_data){
 
 
6922
  __attribute__((cleanup(cleanup_queue)))
 
 
6923
    task_queue *queue = create_queue();
 
 
6924
  g_assert_nonnull(queue);
 
 
6925
  __attribute__((cleanup(string_set_clear)))
 
 
6926
    string_set cancelled_filenames = {};
 
 
6927
  bool quit_now = false;
 
 
6929
  __attribute__((nonnull))
 
 
6930
    void next_run_func(__attribute__((unused))
 
 
6931
                       const task_context task,
 
 
6932
                       task_queue *const q){
 
 
6936
  task_context task = {
 
 
6937
    .func=next_run_func,
 
 
6939
  g_assert_true(add_to_queue(queue, task));
 
 
6941
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6942
  g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
 
 
6943
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
 
6946
static void test_run_queue_two_tasks(__attribute__((unused))
 
 
6947
                                     test_fixture *fixture,
 
 
6948
                                     __attribute__((unused))
 
 
6949
                                     gconstpointer user_data){
 
 
6950
  __attribute__((cleanup(cleanup_queue)))
 
 
6951
    task_queue *queue = create_queue();
 
 
6952
  g_assert_nonnull(queue);
 
 
6953
  queue->next_run = 1;
 
 
6954
  __attribute__((cleanup(string_set_clear)))
 
 
6955
    string_set cancelled_filenames = {};
 
 
6956
  bool quit_now = false;
 
 
6957
  bool mandos_client_exited = false;
 
 
6959
  __attribute__((nonnull))
 
 
6960
    void next_run_func(__attribute__((unused))
 
 
6961
                       const task_context task,
 
 
6962
                       task_queue *const q){
 
 
6966
  __attribute__((nonnull))
 
 
6967
    void exited_func(const task_context task,
 
 
6968
                     __attribute__((unused)) task_queue *const q){
 
 
6969
    *task.mandos_client_exited = true;
 
 
6972
  task_context task1 = {
 
 
6973
    .func=next_run_func,
 
 
6975
  g_assert_true(add_to_queue(queue, task1));
 
 
6977
  task_context task2 = {
 
 
6979
    .mandos_client_exited=&mandos_client_exited,
 
 
6981
  g_assert_true(add_to_queue(queue, task2));
 
 
6983
  g_assert_true(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
6984
  g_assert_false(quit_now);
 
 
6985
  g_assert_cmpuint((unsigned int)(queue->next_run), ==, 1);
 
 
6986
  g_assert_true(mandos_client_exited);
 
 
6987
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
 
6990
static void test_run_queue_two_tasks_quit(__attribute__((unused))
 
 
6991
                                          test_fixture *fixture,
 
 
6992
                                          __attribute__((unused))
 
 
6993
                                          gconstpointer user_data){
 
 
6994
  __attribute__((cleanup(cleanup_queue)))
 
 
6995
    task_queue *queue = create_queue();
 
 
6996
  g_assert_nonnull(queue);
 
 
6997
  __attribute__((cleanup(string_set_clear)))
 
 
6998
    string_set cancelled_filenames = {};
 
 
6999
  bool quit_now = false;
 
 
7000
  bool mandos_client_exited = false;
 
 
7001
  bool password_is_read = false;
 
 
7003
  __attribute__((nonnull))
 
 
7004
    void set_exited_func(const task_context task,
 
 
7005
                         __attribute__((unused)) task_queue *const q){
 
 
7006
    *task.mandos_client_exited = true;
 
 
7007
    *task.quit_now = true;
 
 
7009
  task_context task1 = {
 
 
7010
    .func=set_exited_func,
 
 
7011
    .quit_now=&quit_now,
 
 
7012
    .mandos_client_exited=&mandos_client_exited,
 
 
7014
  g_assert_true(add_to_queue(queue, task1));
 
 
7016
  __attribute__((nonnull))
 
 
7017
    void set_read_func(const task_context task,
 
 
7018
                       __attribute__((unused)) task_queue *const q){
 
 
7019
    *task.quit_now = true;
 
 
7020
    *task.password_is_read = true;
 
 
7022
  task_context task2 = {
 
 
7023
    .func=set_read_func,
 
 
7024
    .quit_now=&quit_now,
 
 
7025
    .password_is_read=&password_is_read,
 
 
7027
  g_assert_true(add_to_queue(queue, task2));
 
 
7029
  g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
7030
  g_assert_true(quit_now);
 
 
7031
  g_assert_true(mandos_client_exited xor password_is_read);
 
 
7032
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
 
7035
static void test_run_queue_two_tasks_cleanup(__attribute__((unused))
 
 
7036
                                             test_fixture *fixture,
 
 
7037
                                             __attribute__((unused))
 
 
7038
                                             gconstpointer user_data){
 
 
7039
  __attribute__((cleanup(cleanup_queue)))
 
 
7040
    task_queue *queue = create_queue();
 
 
7041
  g_assert_nonnull(queue);
 
 
7042
  __attribute__((cleanup(string_set_clear)))
 
 
7043
    string_set cancelled_filenames = {};
 
 
7045
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
 
7046
  __attribute__((cleanup(cleanup_close)))
 
 
7047
    const int read_pipe = pipefds[0];
 
 
7048
  __attribute__((cleanup(cleanup_close)))
 
 
7049
    const int write_pipe = pipefds[1];
 
 
7050
  bool quit_now = false;
 
 
7052
  __attribute__((nonnull))
 
 
7053
    void read_func(const task_context task,
 
 
7054
                   __attribute__((unused)) task_queue *const q){
 
 
7055
    *task.quit_now = true;
 
 
7057
  task_context task1 = {
 
 
7059
    .quit_now=&quit_now,
 
 
7062
  g_assert_true(add_to_queue(queue, task1));
 
 
7064
  __attribute__((nonnull))
 
 
7065
    void write_func(const task_context task,
 
 
7066
                    __attribute__((unused)) task_queue *const q){
 
 
7067
    *task.quit_now = true;
 
 
7069
  task_context task2 = {
 
 
7071
    .quit_now=&quit_now,
 
 
7074
  g_assert_true(add_to_queue(queue, task2));
 
 
7076
  g_assert_false(run_queue(&queue, &cancelled_filenames, &quit_now));
 
 
7077
  g_assert_true(quit_now);
 
 
7079
  /* Either read_pipe or write_pipe should be closed already */
 
 
7081
  bool close_read_pipe = (close(read_pipe) == -1);
 
 
7082
  close_read_pipe &= (errno == EBADF);
 
 
7084
  bool close_write_pipe = (close(write_pipe) == -1);
 
 
7085
  close_write_pipe &= (errno == EBADF);
 
 
7086
  g_assert_true(close_read_pipe xor close_write_pipe);
 
 
7087
  g_assert_cmpuint((unsigned int)(queue->length), ==, 0);
 
 
7090
static void test_setup_signal_handler(__attribute__((unused))
 
 
7091
                                      test_fixture *fixture,
 
 
7092
                                      __attribute__((unused))
 
 
7093
                                      gconstpointer user_data){
 
 
7094
  /* Save current SIGCHLD action, whatever it is */
 
 
7095
  struct sigaction expected_sigchld_action;
 
 
7096
  g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
 
 
7099
  /* Act; i.e. run the setup_signal_handler() function */
 
 
7100
  struct sigaction actual_old_sigchld_action;
 
 
7101
  g_assert_true(setup_signal_handler(&actual_old_sigchld_action));
 
 
7103
  /* Check that the function correctly set "actual_old_sigchld_action"
 
 
7104
     to the same values as the previously saved
 
 
7105
     "expected_sigchld_action" */
 
 
7106
  /* Check member sa_handler */
 
 
7107
  g_assert_true(actual_old_sigchld_action.sa_handler
 
 
7108
                == expected_sigchld_action.sa_handler);
 
 
7109
  /* Check member sa_mask */
 
 
7110
  for(int signum = 1; signum < NSIG; signum++){
 
 
7111
    const int expected_old_block_state
 
 
7112
      = sigismember(&expected_sigchld_action.sa_mask, signum);
 
 
7113
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
 
7114
    const int actual_old_block_state
 
 
7115
      = sigismember(&actual_old_sigchld_action.sa_mask, signum);
 
 
7116
    g_assert_cmpint(actual_old_block_state, >=, 0);
 
 
7117
    g_assert_cmpint(actual_old_block_state,
 
 
7118
                    ==, expected_old_block_state);
 
 
7120
  /* Check member sa_flags */
 
 
7121
  g_assert_true((actual_old_sigchld_action.sa_flags
 
 
7122
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
 
7123
                == (expected_sigchld_action.sa_flags
 
 
7124
                    & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
 
 
7126
  /* Retrieve the current signal handler for SIGCHLD as set by
 
 
7127
     setup_signal_handler() */
 
 
7128
  struct sigaction actual_new_sigchld_action;
 
 
7129
  g_assert_cmpint(sigaction(SIGCHLD, NULL,
 
 
7130
                            &actual_new_sigchld_action), ==, 0);
 
 
7131
  /* Check that the signal handler (member sa_handler) is correctly
 
 
7132
     set to the "handle_sigchld" function */
 
 
7133
  g_assert_true(actual_new_sigchld_action.sa_handler != SIG_DFL);
 
 
7134
  g_assert_true(actual_new_sigchld_action.sa_handler != SIG_IGN);
 
 
7135
  g_assert_true(actual_new_sigchld_action.sa_handler
 
 
7137
  /* Check (in member sa_mask) that at least a handful of signals are
 
 
7138
     actually blocked during the signal handler */
 
 
7139
  for(int signum = 1; signum < NSIG; signum++){
 
 
7140
    int actual_new_block_state;
 
 
7146
      actual_new_block_state
 
 
7147
        = sigismember(&actual_new_sigchld_action.sa_mask, signum);
 
 
7148
      g_assert_cmpint(actual_new_block_state, ==, 1);
 
 
7150
    case SIGKILL:               /* non-blockable */
 
 
7151
    case SIGSTOP:               /* non-blockable */
 
 
7152
    case SIGCHLD:               /* always blocked */
 
 
7157
  /* Check member sa_flags */
 
 
7158
  g_assert_true((actual_new_sigchld_action.sa_flags
 
 
7159
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
 
7160
                == (SA_NOCLDSTOP | SA_RESTART));
 
 
7162
  /* Restore signal handler */
 
 
7163
  g_assert_cmpint(sigaction(SIGCHLD, &expected_sigchld_action, NULL),
 
 
7167
static void test_restore_signal_handler(__attribute__((unused))
 
 
7168
                                        test_fixture *fixture,
 
 
7169
                                        __attribute__((unused))
 
 
7170
                                        gconstpointer user_data){
 
 
7171
  /* Save current SIGCHLD action, whatever it is */
 
 
7172
  struct sigaction expected_sigchld_action;
 
 
7173
  g_assert_cmpint(sigaction(SIGCHLD, NULL, &expected_sigchld_action),
 
 
7175
  /* Since we haven't established a signal handler yet, there should
 
 
7176
     not be one established.  But another test may have relied on
 
 
7177
     restore_signal_handler() to restore the signal handler, and if
 
 
7178
     restore_signal_handler() is buggy (which we should be prepared
 
 
7179
     for in this test) the signal handler may not have been restored
 
 
7180
     properly; check for this: */
 
 
7181
  g_assert_true(expected_sigchld_action.sa_handler != handle_sigchld);
 
 
7183
  /* Establish a signal handler */
 
 
7184
  struct sigaction sigchld_action = {
 
 
7185
    .sa_handler=handle_sigchld,
 
 
7186
    .sa_flags=SA_RESTART | SA_NOCLDSTOP,
 
 
7188
  g_assert_cmpint(sigfillset(&sigchld_action.sa_mask), ==, 0);
 
 
7189
  g_assert_cmpint(sigaction(SIGCHLD, &sigchld_action, NULL), ==, 0);
 
 
7191
  /* Act; i.e. run the restore_signal_handler() function */
 
 
7192
  g_assert_true(restore_signal_handler(&expected_sigchld_action));
 
 
7194
  /* Retrieve the restored signal handler data */
 
 
7195
  struct sigaction actual_restored_sigchld_action;
 
 
7196
  g_assert_cmpint(sigaction(SIGCHLD, NULL,
 
 
7197
                            &actual_restored_sigchld_action), ==, 0);
 
 
7199
  /* Check that the function correctly restored the signal action, as
 
 
7200
     saved in "actual_restored_sigchld_action", to the same values as
 
 
7201
     the previously saved "expected_sigchld_action" */
 
 
7202
  /* Check member sa_handler */
 
 
7203
  g_assert_true(actual_restored_sigchld_action.sa_handler
 
 
7204
                == expected_sigchld_action.sa_handler);
 
 
7205
  /* Check member sa_mask */
 
 
7206
  for(int signum = 1; signum < NSIG; signum++){
 
 
7207
    const int expected_old_block_state
 
 
7208
      = sigismember(&expected_sigchld_action.sa_mask, signum);
 
 
7209
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
 
7210
    const int actual_restored_block_state
 
 
7211
      = sigismember(&actual_restored_sigchld_action.sa_mask, signum);
 
 
7212
    g_assert_cmpint(actual_restored_block_state, >=, 0);
 
 
7213
    g_assert_cmpint(actual_restored_block_state,
 
 
7214
                    ==, expected_old_block_state);
 
 
7216
  /* Check member sa_flags */
 
 
7217
  g_assert_true((actual_restored_sigchld_action.sa_flags
 
 
7218
                 & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART))
 
 
7219
                == (expected_sigchld_action.sa_flags
 
 
7220
                    & (SA_NOCLDSTOP | SA_ONSTACK | SA_RESTART)));
 
 
7223
static void test_block_sigchld(__attribute__((unused))
 
 
7224
                               test_fixture *fixture,
 
 
7225
                               __attribute__((unused))
 
 
7226
                               gconstpointer user_data){
 
 
7227
  /* Save original signal mask */
 
 
7228
  sigset_t expected_sigmask;
 
 
7229
  g_assert_cmpint(pthread_sigmask(-1, NULL, &expected_sigmask),
 
 
7232
  /* Make sure SIGCHLD is unblocked for this test */
 
 
7233
  sigset_t sigchld_sigmask;
 
 
7234
  g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
 
 
7235
  g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
 
 
7236
  g_assert_cmpint(pthread_sigmask(SIG_UNBLOCK, &sigchld_sigmask,
 
 
7239
  /* Act; i.e. run the block_sigchld() function */
 
 
7240
  sigset_t actual_old_sigmask;
 
 
7241
  g_assert_true(block_sigchld(&actual_old_sigmask));
 
 
7243
  /* Check the actual_old_sigmask; it should be the same as the
 
 
7244
     previously saved signal mask "expected_sigmask". */
 
 
7245
  for(int signum = 1; signum < NSIG; signum++){
 
 
7246
    const int expected_old_block_state
 
 
7247
      = sigismember(&expected_sigmask, signum);
 
 
7248
    g_assert_cmpint(expected_old_block_state, >=, 0);
 
 
7249
    const int actual_old_block_state
 
 
7250
      = sigismember(&actual_old_sigmask, signum);
 
 
7251
    g_assert_cmpint(actual_old_block_state, >=, 0);
 
 
7252
    g_assert_cmpint(actual_old_block_state,
 
 
7253
                    ==, expected_old_block_state);
 
 
7256
  /* Retrieve the newly set signal mask */
 
 
7257
  sigset_t actual_sigmask;
 
 
7258
  g_assert_cmpint(pthread_sigmask(-1, NULL, &actual_sigmask), ==, 0);
 
 
7260
  /* SIGCHLD should be blocked */
 
 
7261
  g_assert_cmpint(sigismember(&actual_sigmask, SIGCHLD), ==, 1);
 
 
7263
  /* Restore signal mask */
 
 
7264
  g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &expected_sigmask,
 
 
7268
static void test_restore_sigmask(__attribute__((unused))
 
 
7269
                                 test_fixture *fixture,
 
 
7270
                                 __attribute__((unused))
 
 
7271
                                 gconstpointer user_data){
 
 
7272
  /* Save original signal mask */
 
 
7273
  sigset_t orig_sigmask;
 
 
7274
  g_assert_cmpint(pthread_sigmask(-1, NULL, &orig_sigmask), ==, 0);
 
 
7276
  /* Make sure SIGCHLD is blocked for this test */
 
 
7277
  sigset_t sigchld_sigmask;
 
 
7278
  g_assert_cmpint(sigemptyset(&sigchld_sigmask), ==, 0);
 
 
7279
  g_assert_cmpint(sigaddset(&sigchld_sigmask, SIGCHLD), ==, 0);
 
 
7280
  g_assert_cmpint(pthread_sigmask(SIG_BLOCK, &sigchld_sigmask,
 
 
7283
  /* Act; i.e. run the restore_sigmask() function */
 
 
7284
  g_assert_true(restore_sigmask(&orig_sigmask));
 
 
7286
  /* Retrieve the newly restored signal mask */
 
 
7287
  sigset_t restored_sigmask;
 
 
7288
  g_assert_cmpint(pthread_sigmask(-1, NULL, &restored_sigmask),
 
 
7291
  /* Check the restored_sigmask; it should be the same as the
 
 
7292
     previously saved signal mask "orig_sigmask". */
 
 
7293
  for(int signum = 1; signum < NSIG; signum++){
 
 
7294
    const int orig_block_state = sigismember(&orig_sigmask, signum);
 
 
7295
    g_assert_cmpint(orig_block_state, >=, 0);
 
 
7296
    const int restored_block_state = sigismember(&restored_sigmask,
 
 
7298
    g_assert_cmpint(restored_block_state, >=, 0);
 
 
7299
    g_assert_cmpint(restored_block_state, ==, orig_block_state);
 
 
7302
  /* Restore signal mask */
 
 
7303
  g_assert_cmpint(pthread_sigmask(SIG_SETMASK, &orig_sigmask,
 
 
7307
static void test_parse_arguments_noargs(__attribute__((unused))
 
 
7308
                                        test_fixture *fixture,
 
 
7309
                                        __attribute__((unused))
 
 
7310
                                        gconstpointer user_data){
 
 
7314
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7316
  char *agent_directory = NULL;
 
 
7317
  char *helper_directory = NULL;
 
 
7320
  char *mandos_argz = NULL;
 
 
7321
  size_t mandos_argz_length = 0;
 
 
7323
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7324
                                &helper_directory, &user, &group,
 
 
7325
                                &mandos_argz, &mandos_argz_length));
 
 
7326
  g_assert_null(agent_directory);
 
 
7327
  g_assert_null(helper_directory);
 
 
7328
  g_assert_true(user == 0);
 
 
7329
  g_assert_true(group == 0);
 
 
7330
  g_assert_null(mandos_argz);
 
 
7331
  g_assert_true(mandos_argz_length == 0);
 
 
7333
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7338
__attribute__((nonnull))
 
 
7339
static bool parse_arguments_devnull(int argc, char *argv[],
 
 
7340
                                    const bool exit_failure,
 
 
7341
                                    char **agent_directory,
 
 
7342
                                    char **helper_directory,
 
 
7346
                                    size_t *mandos_argz_length){
 
 
7348
  FILE *real_stderr = stderr;
 
 
7349
  FILE *devnull = fopen("/dev/null", "we");
 
 
7350
  g_assert_nonnull(devnull);
 
 
7353
  const bool ret = parse_arguments(argc, argv, exit_failure,
 
 
7355
                                   helper_directory, user, group,
 
 
7356
                                   mandos_argz, mandos_argz_length);
 
 
7357
  const error_t saved_errno = errno;
 
 
7359
  stderr = real_stderr;
 
 
7360
  g_assert_cmpint(fclose(devnull), ==, 0);
 
 
7362
  errno = saved_errno;
 
 
7367
static void test_parse_arguments_invalid(__attribute__((unused))
 
 
7368
                                         test_fixture *fixture,
 
 
7369
                                         __attribute__((unused))
 
 
7370
                                         gconstpointer user_data){
 
 
7373
    strdup("--invalid"),
 
 
7375
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7377
  char *agent_directory = NULL;
 
 
7378
  char *helper_directory = NULL;
 
 
7381
  char *mandos_argz = NULL;
 
 
7382
  size_t mandos_argz_length = 0;
 
 
7384
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
 
7386
                                         &helper_directory, &user,
 
 
7387
                                         &group, &mandos_argz,
 
 
7388
                                         &mandos_argz_length));
 
 
7390
  g_assert_true(errno == EINVAL);
 
 
7391
  g_assert_null(agent_directory);
 
 
7392
  g_assert_null(helper_directory);
 
 
7393
  g_assert_null(mandos_argz);
 
 
7394
  g_assert_true(mandos_argz_length == 0);
 
 
7396
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7401
static void test_parse_arguments_long_dir(__attribute__((unused))
 
 
7402
                                          test_fixture *fixture,
 
 
7403
                                          __attribute__((unused))
 
 
7404
                                          gconstpointer user_data){
 
 
7407
    strdup("--agent-directory"),
 
 
7410
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7412
  __attribute__((cleanup(cleanup_string)))
 
 
7413
    char *agent_directory = NULL;
 
 
7414
  char *helper_directory = NULL;
 
 
7417
  __attribute__((cleanup(cleanup_string)))
 
 
7418
    char *mandos_argz = NULL;
 
 
7419
  size_t mandos_argz_length = 0;
 
 
7421
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7422
                                &helper_directory, &user, &group,
 
 
7423
                                &mandos_argz, &mandos_argz_length));
 
 
7425
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
 
7426
  g_assert_null(helper_directory);
 
 
7427
  g_assert_true(user == 0);
 
 
7428
  g_assert_true(group == 0);
 
 
7429
  g_assert_null(mandos_argz);
 
 
7430
  g_assert_true(mandos_argz_length == 0);
 
 
7432
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7437
static void test_parse_arguments_short_dir(__attribute__((unused))
 
 
7438
                                           test_fixture *fixture,
 
 
7439
                                           __attribute__((unused))
 
 
7440
                                           gconstpointer user_data){
 
 
7446
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7448
  __attribute__((cleanup(cleanup_string)))
 
 
7449
    char *agent_directory = NULL;
 
 
7450
  char *helper_directory = NULL;
 
 
7453
  __attribute__((cleanup(cleanup_string)))
 
 
7454
    char *mandos_argz = NULL;
 
 
7455
  size_t mandos_argz_length = 0;
 
 
7457
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7458
                                &helper_directory, &user, &group,
 
 
7459
                                &mandos_argz, &mandos_argz_length));
 
 
7461
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
 
7462
  g_assert_null(helper_directory);
 
 
7463
  g_assert_true(user == 0);
 
 
7464
  g_assert_true(group == 0);
 
 
7465
  g_assert_null(mandos_argz);
 
 
7466
  g_assert_true(mandos_argz_length == 0);
 
 
7468
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7474
void test_parse_arguments_helper_directory(__attribute__((unused))
 
 
7475
                                           test_fixture *fixture,
 
 
7476
                                           __attribute__((unused))
 
 
7477
                                           gconstpointer user_data){
 
 
7480
    strdup("--helper-directory"),
 
 
7483
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7485
  char *agent_directory = NULL;
 
 
7486
  __attribute__((cleanup(cleanup_string)))
 
 
7487
    char *helper_directory = NULL;
 
 
7490
  __attribute__((cleanup(cleanup_string)))
 
 
7491
    char *mandos_argz = NULL;
 
 
7492
  size_t mandos_argz_length = 0;
 
 
7494
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7495
                                &helper_directory, &user, &group,
 
 
7496
                                &mandos_argz, &mandos_argz_length));
 
 
7498
  g_assert_cmpstr(helper_directory, ==, "/tmp");
 
 
7499
  g_assert_null(agent_directory);
 
 
7500
  g_assert_true(user == 0);
 
 
7501
  g_assert_true(group == 0);
 
 
7502
  g_assert_null(mandos_argz);
 
 
7503
  g_assert_true(mandos_argz_length == 0);
 
 
7505
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7511
void test_parse_arguments_plugin_helper_dir(__attribute__((unused))
 
 
7512
                                            test_fixture *fixture,
 
 
7513
                                            __attribute__((unused))
 
 
7514
                                            gconstpointer user_data){
 
 
7517
    strdup("--plugin-helper-dir"),
 
 
7520
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7522
  char *agent_directory = NULL;
 
 
7523
  __attribute__((cleanup(cleanup_string)))
 
 
7524
    char *helper_directory = NULL;
 
 
7527
  __attribute__((cleanup(cleanup_string)))
 
 
7528
    char *mandos_argz = NULL;
 
 
7529
  size_t mandos_argz_length = 0;
 
 
7531
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7532
                                &helper_directory, &user, &group,
 
 
7533
                                &mandos_argz, &mandos_argz_length));
 
 
7535
  g_assert_cmpstr(helper_directory, ==, "/tmp");
 
 
7536
  g_assert_null(agent_directory);
 
 
7537
  g_assert_true(user == 0);
 
 
7538
  g_assert_true(group == 0);
 
 
7539
  g_assert_null(mandos_argz);
 
 
7540
  g_assert_true(mandos_argz_length == 0);
 
 
7542
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7547
static void test_parse_arguments_user(__attribute__((unused))
 
 
7548
                                      test_fixture *fixture,
 
 
7549
                                      __attribute__((unused))
 
 
7550
                                      gconstpointer user_data){
 
 
7556
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7558
  char *agent_directory = NULL;
 
 
7559
  __attribute__((cleanup(cleanup_string)))
 
 
7560
    char *helper_directory = NULL;
 
 
7563
  __attribute__((cleanup(cleanup_string)))
 
 
7564
    char *mandos_argz = NULL;
 
 
7565
  size_t mandos_argz_length = 0;
 
 
7567
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7568
                                &helper_directory, &user, &group,
 
 
7569
                                &mandos_argz, &mandos_argz_length));
 
 
7571
  g_assert_null(helper_directory);
 
 
7572
  g_assert_null(agent_directory);
 
 
7573
  g_assert_cmpuint((unsigned int)user, ==, 1000);
 
 
7574
  g_assert_true(group == 0);
 
 
7575
  g_assert_null(mandos_argz);
 
 
7576
  g_assert_true(mandos_argz_length == 0);
 
 
7578
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7583
static void test_parse_arguments_user_invalid(__attribute__((unused))
 
 
7584
                                              test_fixture *fixture,
 
 
7585
                                              __attribute__((unused))
 
 
7593
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7595
  char *agent_directory = NULL;
 
 
7596
  __attribute__((cleanup(cleanup_string)))
 
 
7597
    char *helper_directory = NULL;
 
 
7600
  __attribute__((cleanup(cleanup_string)))
 
 
7601
    char *mandos_argz = NULL;
 
 
7602
  size_t mandos_argz_length = 0;
 
 
7604
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
 
7606
                                         &helper_directory, &user,
 
 
7607
                                         &group, &mandos_argz,
 
 
7608
                                         &mandos_argz_length));
 
 
7610
  g_assert_null(helper_directory);
 
 
7611
  g_assert_null(agent_directory);
 
 
7612
  g_assert_cmpuint((unsigned int)user, ==, 0);
 
 
7613
  g_assert_true(group == 0);
 
 
7614
  g_assert_null(mandos_argz);
 
 
7615
  g_assert_true(mandos_argz_length == 0);
 
 
7617
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7623
void test_parse_arguments_user_zero_invalid(__attribute__((unused))
 
 
7624
                                            test_fixture *fixture,
 
 
7625
                                            __attribute__((unused))
 
 
7626
                                            gconstpointer user_data){
 
 
7632
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7634
  char *agent_directory = NULL;
 
 
7635
  __attribute__((cleanup(cleanup_string)))
 
 
7636
    char *helper_directory = NULL;
 
 
7639
  __attribute__((cleanup(cleanup_string)))
 
 
7640
    char *mandos_argz = NULL;
 
 
7641
  size_t mandos_argz_length = 0;
 
 
7643
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
 
7645
                                         &helper_directory, &user,
 
 
7646
                                         &group, &mandos_argz,
 
 
7647
                                         &mandos_argz_length));
 
 
7649
  g_assert_null(helper_directory);
 
 
7650
  g_assert_null(agent_directory);
 
 
7651
  g_assert_cmpuint((unsigned int)user, ==, 0);
 
 
7652
  g_assert_true(group == 0);
 
 
7653
  g_assert_null(mandos_argz);
 
 
7654
  g_assert_true(mandos_argz_length == 0);
 
 
7656
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7661
static void test_parse_arguments_group(__attribute__((unused))
 
 
7662
                                       test_fixture *fixture,
 
 
7663
                                       __attribute__((unused))
 
 
7664
                                       gconstpointer user_data){
 
 
7670
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7672
  char *agent_directory = NULL;
 
 
7673
  __attribute__((cleanup(cleanup_string)))
 
 
7674
    char *helper_directory = NULL;
 
 
7677
  __attribute__((cleanup(cleanup_string)))
 
 
7678
    char *mandos_argz = NULL;
 
 
7679
  size_t mandos_argz_length = 0;
 
 
7681
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7682
                                &helper_directory, &user, &group,
 
 
7683
                                &mandos_argz, &mandos_argz_length));
 
 
7685
  g_assert_null(helper_directory);
 
 
7686
  g_assert_null(agent_directory);
 
 
7687
  g_assert_true(user == 0);
 
 
7688
  g_assert_cmpuint((unsigned int)group, ==, 1000);
 
 
7689
  g_assert_null(mandos_argz);
 
 
7690
  g_assert_true(mandos_argz_length == 0);
 
 
7692
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7697
static void test_parse_arguments_group_invalid(__attribute__((unused))
 
 
7698
                                               test_fixture *fixture,
 
 
7699
                                               __attribute__((unused))
 
 
7707
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7709
  char *agent_directory = NULL;
 
 
7710
  __attribute__((cleanup(cleanup_string)))
 
 
7711
    char *helper_directory = NULL;
 
 
7714
  __attribute__((cleanup(cleanup_string)))
 
 
7715
    char *mandos_argz = NULL;
 
 
7716
  size_t mandos_argz_length = 0;
 
 
7718
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
 
7720
                                         &helper_directory, &user,
 
 
7721
                                         &group, &mandos_argz,
 
 
7722
                                         &mandos_argz_length));
 
 
7724
  g_assert_null(helper_directory);
 
 
7725
  g_assert_null(agent_directory);
 
 
7726
  g_assert_true(user == 0);
 
 
7727
  g_assert_true(group == 0);
 
 
7728
  g_assert_null(mandos_argz);
 
 
7729
  g_assert_true(mandos_argz_length == 0);
 
 
7731
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7737
void test_parse_arguments_group_zero_invalid(__attribute__((unused))
 
 
7738
                                             test_fixture *fixture,
 
 
7739
                                             __attribute__((unused))
 
 
7740
                                             gconstpointer user_data){
 
 
7746
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7748
  char *agent_directory = NULL;
 
 
7749
  __attribute__((cleanup(cleanup_string)))
 
 
7750
    char *helper_directory = NULL;
 
 
7753
  __attribute__((cleanup(cleanup_string)))
 
 
7754
    char *mandos_argz = NULL;
 
 
7755
  size_t mandos_argz_length = 0;
 
 
7757
  g_assert_false(parse_arguments_devnull(argc, argv, false,
 
 
7759
                                         &helper_directory, &user,
 
 
7760
                                         &group, &mandos_argz,
 
 
7761
                                         &mandos_argz_length));
 
 
7763
  g_assert_null(helper_directory);
 
 
7764
  g_assert_null(agent_directory);
 
 
7765
  g_assert_cmpuint((unsigned int)group, ==, 0);
 
 
7766
  g_assert_true(group == 0);
 
 
7767
  g_assert_null(mandos_argz);
 
 
7768
  g_assert_true(mandos_argz_length == 0);
 
 
7770
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7775
static void test_parse_arguments_mandos_noargs(__attribute__((unused))
 
 
7776
                                               test_fixture *fixture,
 
 
7777
                                               __attribute__((unused))
 
 
7782
    strdup("mandos-client"),
 
 
7784
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7786
  __attribute__((cleanup(cleanup_string)))
 
 
7787
    char *agent_directory = NULL;
 
 
7788
  __attribute__((cleanup(cleanup_string)))
 
 
7789
    char *helper_directory = NULL;
 
 
7792
  __attribute__((cleanup(cleanup_string)))
 
 
7793
    char *mandos_argz = NULL;
 
 
7794
  size_t mandos_argz_length = 0;
 
 
7796
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7797
                                &helper_directory, &user, &group,
 
 
7798
                                &mandos_argz, &mandos_argz_length));
 
 
7800
  g_assert_null(agent_directory);
 
 
7801
  g_assert_null(helper_directory);
 
 
7802
  g_assert_true(user == 0);
 
 
7803
  g_assert_true(group == 0);
 
 
7804
  g_assert_cmpstr(mandos_argz, ==, "mandos-client");
 
 
7805
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
 
7806
                                            mandos_argz_length),
 
 
7809
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7814
static void test_parse_arguments_mandos_args(__attribute__((unused))
 
 
7815
                                             test_fixture *fixture,
 
 
7816
                                             __attribute__((unused))
 
 
7817
                                             gconstpointer user_data){
 
 
7820
    strdup("mandos-client"),
 
 
7825
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7827
  __attribute__((cleanup(cleanup_string)))
 
 
7828
    char *agent_directory = NULL;
 
 
7829
  __attribute__((cleanup(cleanup_string)))
 
 
7830
    char *helper_directory = NULL;
 
 
7833
  __attribute__((cleanup(cleanup_string)))
 
 
7834
    char *mandos_argz = NULL;
 
 
7835
  size_t mandos_argz_length = 0;
 
 
7837
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7838
                                &helper_directory, &user, &group,
 
 
7839
                                &mandos_argz, &mandos_argz_length));
 
 
7841
  g_assert_null(agent_directory);
 
 
7842
  g_assert_null(helper_directory);
 
 
7843
  g_assert_true(user == 0);
 
 
7844
  g_assert_true(group == 0);
 
 
7845
  char *marg = mandos_argz;
 
 
7846
  g_assert_cmpstr(marg, ==, "mandos-client");
 
 
7847
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7848
  g_assert_cmpstr(marg, ==, "one");
 
 
7849
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7850
  g_assert_cmpstr(marg, ==, "two");
 
 
7851
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7852
  g_assert_cmpstr(marg, ==, "three");
 
 
7853
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
 
7854
                                            mandos_argz_length),
 
 
7857
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7862
static void test_parse_arguments_all_args(__attribute__((unused))
 
 
7863
                                          test_fixture *fixture,
 
 
7864
                                          __attribute__((unused))
 
 
7865
                                          gconstpointer user_data){
 
 
7868
    strdup("--agent-directory"),
 
 
7870
    strdup("--helper-directory"),
 
 
7876
    strdup("mandos-client"),
 
 
7881
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7883
  __attribute__((cleanup(cleanup_string)))
 
 
7884
    char *agent_directory = NULL;
 
 
7885
  __attribute__((cleanup(cleanup_string)))
 
 
7886
    char *helper_directory = NULL;
 
 
7889
  __attribute__((cleanup(cleanup_string)))
 
 
7890
    char *mandos_argz = NULL;
 
 
7891
  size_t mandos_argz_length = 0;
 
 
7893
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7894
                                &helper_directory, &user, &group,
 
 
7895
                                &mandos_argz, &mandos_argz_length));
 
 
7897
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
 
7898
  g_assert_cmpstr(helper_directory, ==, "/var/tmp");
 
 
7899
  g_assert_true(user == 1);
 
 
7900
  g_assert_true(group == 2);
 
 
7901
  char *marg = mandos_argz;
 
 
7902
  g_assert_cmpstr(marg, ==, "mandos-client");
 
 
7903
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7904
  g_assert_cmpstr(marg, ==, "one");
 
 
7905
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7906
  g_assert_cmpstr(marg, ==, "two");
 
 
7907
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7908
  g_assert_cmpstr(marg, ==, "three");
 
 
7909
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
 
7910
                                            mandos_argz_length),
 
 
7913
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7918
static void test_parse_arguments_mixed(__attribute__((unused))
 
 
7919
                                       test_fixture *fixture,
 
 
7920
                                       __attribute__((unused))
 
 
7921
                                       gconstpointer user_data){
 
 
7924
    strdup("mandos-client"),
 
 
7928
    strdup("--agent-directory"),
 
 
7932
    strdup("--helper-directory=/var/tmp"),
 
 
7934
  const int argc = (sizeof(argv) / sizeof(char *)) - 1;
 
 
7936
  __attribute__((cleanup(cleanup_string)))
 
 
7937
    char *agent_directory = NULL;
 
 
7938
  __attribute__((cleanup(cleanup_string)))
 
 
7939
    char *helper_directory = NULL;
 
 
7942
  __attribute__((cleanup(cleanup_string)))
 
 
7943
    char *mandos_argz = NULL;
 
 
7944
  size_t mandos_argz_length = 0;
 
 
7946
  g_assert_true(parse_arguments(argc, argv, false, &agent_directory,
 
 
7947
                                &helper_directory, &user, &group,
 
 
7948
                                &mandos_argz, &mandos_argz_length));
 
 
7950
  g_assert_cmpstr(agent_directory, ==, "/tmp");
 
 
7951
  g_assert_cmpstr(helper_directory, ==, "/var/tmp");
 
 
7952
  g_assert_true(user == 1);
 
 
7953
  g_assert_true(group == 0);
 
 
7954
  char *marg = mandos_argz;
 
 
7955
  g_assert_cmpstr(marg, ==, "mandos-client");
 
 
7956
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7957
  g_assert_cmpstr(marg, ==, "one");
 
 
7958
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7959
  g_assert_cmpstr(marg, ==, "two");
 
 
7960
  marg = argz_next(mandos_argz, mandos_argz_length, marg);
 
 
7961
  g_assert_cmpstr(marg, ==, "three");
 
 
7962
  g_assert_cmpuint((unsigned int)argz_count(mandos_argz,
 
 
7963
                                            mandos_argz_length),
 
 
7966
  for(char **arg = argv; *arg != NULL; arg++){
 
 
7971
/* End of tests section */
 
 
7973
/* Test boilerplate section; New tests should be added to the test
 
 
7974
   suite definition here, in the "run_tests" function.
 
 
7976
   Finally, this section also contains the should_only_run_tests()
 
 
7977
   function used by main() for deciding if tests should be run or to
 
 
7980
__attribute__((cold))
 
 
7981
static bool run_tests(int argc, char *argv[]){
 
 
7982
  g_test_init(&argc, &argv, NULL);
 
 
7984
  /* A macro to add a test with no setup or teardown functions */
 
 
7985
#define test_add(testpath, testfunc)                    \
 
 
7987
    g_test_add((testpath), test_fixture, NULL, NULL,    \
 
 
7988
               (testfunc), NULL);                       \
 
 
7991
  /* Test the signal-related functions first, since some other tests
 
 
7992
     depend on these functions in their setups and teardowns */
 
 
7993
  test_add("/signal-handling/setup", test_setup_signal_handler);
 
 
7994
  test_add("/signal-handling/restore", test_restore_signal_handler);
 
 
7995
  test_add("/signal-handling/block", test_block_sigchld);
 
 
7996
  test_add("/signal-handling/restore-sigmask", test_restore_sigmask);
 
 
7998
  /* Regular non-signal-related tests; these use no setups or
 
 
8000
  test_add("/parse_arguments/noargs", test_parse_arguments_noargs);
 
 
8001
  test_add("/parse_arguments/invalid", test_parse_arguments_invalid);
 
 
8002
  test_add("/parse_arguments/long-dir",
 
 
8003
           test_parse_arguments_long_dir);
 
 
8004
  test_add("/parse_arguments/short-dir",
 
 
8005
           test_parse_arguments_short_dir);
 
 
8006
  test_add("/parse_arguments/helper-directory",
 
 
8007
           test_parse_arguments_helper_directory);
 
 
8008
  test_add("/parse_arguments/plugin-helper-dir",
 
 
8009
           test_parse_arguments_plugin_helper_dir);
 
 
8010
  test_add("/parse_arguments/user", test_parse_arguments_user);
 
 
8011
  test_add("/parse_arguments/user-invalid",
 
 
8012
           test_parse_arguments_user_invalid);
 
 
8013
  test_add("/parse_arguments/user-zero-invalid",
 
 
8014
           test_parse_arguments_user_zero_invalid);
 
 
8015
  test_add("/parse_arguments/group", test_parse_arguments_group);
 
 
8016
  test_add("/parse_arguments/group-invalid",
 
 
8017
           test_parse_arguments_group_invalid);
 
 
8018
  test_add("/parse_arguments/group-zero-invalid",
 
 
8019
           test_parse_arguments_group_zero_invalid);
 
 
8020
  test_add("/parse_arguments/mandos-noargs",
 
 
8021
           test_parse_arguments_mandos_noargs);
 
 
8022
  test_add("/parse_arguments/mandos-args",
 
 
8023
           test_parse_arguments_mandos_args);
 
 
8024
  test_add("/parse_arguments/all-args",
 
 
8025
           test_parse_arguments_all_args);
 
 
8026
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
 
 
8027
  test_add("/queue/create", test_create_queue);
 
 
8028
  test_add("/queue/add", test_add_to_queue);
 
 
8029
  test_add("/queue/add/overflow", test_add_to_queue_overflow);
 
 
8030
  test_add("/queue/has_question/empty",
 
 
8031
           test_queue_has_question_empty);
 
 
8032
  test_add("/queue/has_question/false",
 
 
8033
           test_queue_has_question_false);
 
 
8034
  test_add("/queue/has_question/true", test_queue_has_question_true);
 
 
8035
  test_add("/queue/has_question/false2",
 
 
8036
           test_queue_has_question_false2);
 
 
8037
  test_add("/queue/has_question/true2",
 
 
8038
           test_queue_has_question_true2);
 
 
8039
  test_add("/buffer/cleanup", test_cleanup_buffer);
 
 
8040
  test_add("/string_set/net-set-contains-nothing",
 
 
8041
           test_string_set_new_set_contains_nothing);
 
 
8042
  test_add("/string_set/with-added-string-contains-it",
 
 
8043
           test_string_set_with_added_string_contains_it);
 
 
8044
  test_add("/string_set/cleared-does-not-contain-string",
 
 
8045
           test_string_set_cleared_does_not_contain_str);
 
 
8046
  test_add("/string_set/swap/one-with-empty",
 
 
8047
           test_string_set_swap_one_with_empty);
 
 
8048
  test_add("/string_set/swap/empty-with-one",
 
 
8049
           test_string_set_swap_empty_with_one);
 
 
8050
  test_add("/string_set/swap/one-with-one",
 
 
8051
           test_string_set_swap_one_with_one);
 
 
8053
  /* A macro to add a test using the setup and teardown functions */
 
 
8054
#define test_add_st(path, func)                                 \
 
 
8056
    g_test_add((path), test_fixture, NULL, test_setup, (func),  \
 
 
8060
  /* Signal-related tests; these use setups and teardowns which
 
 
8061
     establish, during each test run, a signal handler for, and a
 
 
8062
     signal mask blocking, the SIGCHLD signal, just like main() */
 
 
8063
  test_add_st("/wait_for_event/timeout", test_wait_for_event_timeout);
 
 
8064
  test_add_st("/wait_for_event/event", test_wait_for_event_event);
 
 
8065
  test_add_st("/wait_for_event/sigchld", test_wait_for_event_sigchld);
 
 
8066
  test_add_st("/run_queue/zeroes-next-run",
 
 
8067
              test_run_queue_zeroes_next_run);
 
 
8068
  test_add_st("/run_queue/clears-cancelled_filenames",
 
 
8069
              test_run_queue_clears_cancelled_filenames);
 
 
8070
  test_add_st("/run_queue/skips-cancelled-filenames",
 
 
8071
              test_run_queue_skips_cancelled_filenames);
 
 
8072
  test_add_st("/run_queue/one-task", test_run_queue_one_task);
 
 
8073
  test_add_st("/run_queue/two-tasks", test_run_queue_two_tasks);
 
 
8074
  test_add_st("/run_queue/two-tasks/quit",
 
 
8075
              test_run_queue_two_tasks_quit);
 
 
8076
  test_add_st("/run_queue/two-tasks-cleanup",
 
 
8077
              test_run_queue_two_tasks_cleanup);
 
 
8078
  test_add_st("/task-creators/start_mandos_client",
 
 
8079
              test_start_mandos_client);
 
 
8080
  test_add_st("/task-creators/start_mandos_client/execv",
 
 
8081
              test_start_mandos_client_execv);
 
 
8082
  test_add_st("/task-creators/start_mandos_client/suid/euid",
 
 
8083
              test_start_mandos_client_suid_euid);
 
 
8084
  test_add_st("/task-creators/start_mandos_client/suid/egid",
 
 
8085
              test_start_mandos_client_suid_egid);
 
 
8086
  test_add_st("/task-creators/start_mandos_client/suid/ruid",
 
 
8087
              test_start_mandos_client_suid_ruid);
 
 
8088
  test_add_st("/task-creators/start_mandos_client/suid/rgid",
 
 
8089
              test_start_mandos_client_suid_rgid);
 
 
8090
  test_add_st("/task-creators/start_mandos_client/read",
 
 
8091
              test_start_mandos_client_read);
 
 
8092
  test_add_st("/task-creators/start_mandos_client/helper-directory",
 
 
8093
              test_start_mandos_client_helper_directory);
 
 
8094
  test_add_st("/task-creators/start_mandos_client/sigmask",
 
 
8095
              test_start_mandos_client_sigmask);
 
 
8096
  test_add_st("/task/wait_for_mandos_client_exit/badpid",
 
 
8097
              test_wait_for_mandos_client_exit_badpid);
 
 
8098
  test_add_st("/task/wait_for_mandos_client_exit/noexit",
 
 
8099
              test_wait_for_mandos_client_exit_noexit);
 
 
8100
  test_add_st("/task/wait_for_mandos_client_exit/success",
 
 
8101
              test_wait_for_mandos_client_exit_success);
 
 
8102
  test_add_st("/task/wait_for_mandos_client_exit/failure",
 
 
8103
              test_wait_for_mandos_client_exit_failure);
 
 
8104
  test_add_st("/task/wait_for_mandos_client_exit/killed",
 
 
8105
              test_wait_for_mandos_client_exit_killed);
 
 
8106
  test_add_st("/task/read_mandos_client_output/readerror",
 
 
8107
              test_read_mandos_client_output_readerror);
 
 
8108
  test_add_st("/task/read_mandos_client_output/nodata",
 
 
8109
              test_read_mandos_client_output_nodata);
 
 
8110
  test_add_st("/task/read_mandos_client_output/eof",
 
 
8111
              test_read_mandos_client_output_eof);
 
 
8112
  test_add_st("/task/read_mandos_client_output/once",
 
 
8113
              test_read_mandos_client_output_once);
 
 
8114
  test_add_st("/task/read_mandos_client_output/malloc",
 
 
8115
              test_read_mandos_client_output_malloc);
 
 
8116
  test_add_st("/task/read_mandos_client_output/append",
 
 
8117
              test_read_mandos_client_output_append);
 
 
8118
  test_add_st("/task-creators/add_inotify_dir_watch",
 
 
8119
              test_add_inotify_dir_watch);
 
 
8120
  test_add_st("/task-creators/add_inotify_dir_watch/fail",
 
 
8121
              test_add_inotify_dir_watch_fail);
 
 
8122
  test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory",
 
 
8123
              test_add_inotify_dir_watch_nondir);
 
 
8124
  test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
 
 
8125
              test_add_inotify_dir_watch_EAGAIN);
 
 
8126
  test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
 
 
8127
              test_add_inotify_dir_watch_IN_CLOSE_WRITE);
 
 
8128
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
 
 
8129
              test_add_inotify_dir_watch_IN_MOVED_TO);
 
 
8130
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
 
 
8131
              test_add_inotify_dir_watch_IN_MOVED_FROM);
 
 
8132
  test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
 
 
8133
              test_add_inotify_dir_watch_IN_EXCL_UNLINK);
 
 
8134
  test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
 
 
8135
              test_add_inotify_dir_watch_IN_DELETE);
 
 
8136
  test_add_st("/task/read_inotify_event/readerror",
 
 
8137
              test_read_inotify_event_readerror);
 
 
8138
  test_add_st("/task/read_inotify_event/bad-epoll",
 
 
8139
              test_read_inotify_event_bad_epoll);
 
 
8140
  test_add_st("/task/read_inotify_event/nodata",
 
 
8141
              test_read_inotify_event_nodata);
 
 
8142
  test_add_st("/task/read_inotify_event/eof",
 
 
8143
              test_read_inotify_event_eof);
 
 
8144
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE",
 
 
8145
              test_read_inotify_event_IN_CLOSE_WRITE);
 
 
8146
  test_add_st("/task/read_inotify_event/IN_MOVED_TO",
 
 
8147
              test_read_inotify_event_IN_MOVED_TO);
 
 
8148
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
 
 
8149
              test_read_inotify_event_IN_MOVED_FROM);
 
 
8150
  test_add_st("/task/read_inotify_event/IN_DELETE",
 
 
8151
              test_read_inotify_event_IN_DELETE);
 
 
8152
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
 
 
8153
              test_read_inotify_event_IN_CLOSE_WRITE_badname);
 
 
8154
  test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
 
 
8155
              test_read_inotify_event_IN_MOVED_TO_badname);
 
 
8156
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
 
 
8157
              test_read_inotify_event_IN_MOVED_FROM_badname);
 
 
8158
  test_add_st("/task/read_inotify_event/IN_DELETE/badname",
 
 
8159
              test_read_inotify_event_IN_DELETE_badname);
 
 
8160
  test_add_st("/task/open_and_parse_question/ENOENT",
 
 
8161
              test_open_and_parse_question_ENOENT);
 
 
8162
  test_add_st("/task/open_and_parse_question/EIO",
 
 
8163
              test_open_and_parse_question_EIO);
 
 
8164
  test_add_st("/task/open_and_parse_question/parse-error",
 
 
8165
              test_open_and_parse_question_parse_error);
 
 
8166
  test_add_st("/task/open_and_parse_question/nosocket",
 
 
8167
              test_open_and_parse_question_nosocket);
 
 
8168
  test_add_st("/task/open_and_parse_question/badsocket",
 
 
8169
              test_open_and_parse_question_badsocket);
 
 
8170
  test_add_st("/task/open_and_parse_question/nopid",
 
 
8171
              test_open_and_parse_question_nopid);
 
 
8172
  test_add_st("/task/open_and_parse_question/badpid",
 
 
8173
              test_open_and_parse_question_badpid);
 
 
8174
  test_add_st("/task/open_and_parse_question/noexist_pid",
 
 
8175
              test_open_and_parse_question_noexist_pid);
 
 
8176
  test_add_st("/task/open_and_parse_question/no-notafter",
 
 
8177
              test_open_and_parse_question_no_notafter);
 
 
8178
  test_add_st("/task/open_and_parse_question/bad-notafter",
 
 
8179
              test_open_and_parse_question_bad_notafter);
 
 
8180
  test_add_st("/task/open_and_parse_question/notafter-0",
 
 
8181
              test_open_and_parse_question_notafter_0);
 
 
8182
  test_add_st("/task/open_and_parse_question/notafter-1",
 
 
8183
              test_open_and_parse_question_notafter_1);
 
 
8184
  test_add_st("/task/open_and_parse_question/notafter-1-1",
 
 
8185
              test_open_and_parse_question_notafter_1_1);
 
 
8186
  test_add_st("/task/open_and_parse_question/notafter-1-2",
 
 
8187
              test_open_and_parse_question_notafter_1_2);
 
 
8188
  test_add_st("/task/open_and_parse_question/equal-notafter",
 
 
8189
              test_open_and_parse_question_equal_notafter);
 
 
8190
  test_add_st("/task/open_and_parse_question/late-notafter",
 
 
8191
              test_open_and_parse_question_late_notafter);
 
 
8192
  test_add_st("/task/cancel_old_question/0-1-2",
 
 
8193
              test_cancel_old_question_0_1_2);
 
 
8194
  test_add_st("/task/cancel_old_question/0-2-1",
 
 
8195
              test_cancel_old_question_0_2_1);
 
 
8196
  test_add_st("/task/cancel_old_question/1-2-3",
 
 
8197
              test_cancel_old_question_1_2_3);
 
 
8198
  test_add_st("/task/cancel_old_question/1-3-2",
 
 
8199
              test_cancel_old_question_1_3_2);
 
 
8200
  test_add_st("/task/cancel_old_question/2-1-3",
 
 
8201
              test_cancel_old_question_2_1_3);
 
 
8202
  test_add_st("/task/cancel_old_question/2-3-1",
 
 
8203
              test_cancel_old_question_2_3_1);
 
 
8204
  test_add_st("/task/cancel_old_question/3-1-2",
 
 
8205
              test_cancel_old_question_3_1_2);
 
 
8206
  test_add_st("/task/cancel_old_question/3-2-1",
 
 
8207
              test_cancel_old_question_3_2_1);
 
 
8208
  test_add_st("/task/connect_question_socket/name-too-long",
 
 
8209
              test_connect_question_socket_name_too_long);
 
 
8210
  test_add_st("/task/connect_question_socket/connect-fail",
 
 
8211
              test_connect_question_socket_connect_fail);
 
 
8212
  test_add_st("/task/connect_question_socket/bad-epoll",
 
 
8213
              test_connect_question_socket_bad_epoll);
 
 
8214
  test_add_st("/task/connect_question_socket/usable",
 
 
8215
              test_connect_question_socket_usable);
 
 
8216
  test_add_st("/task/send_password_to_socket/client-not-exited",
 
 
8217
              test_send_password_to_socket_client_not_exited);
 
 
8218
  test_add_st("/task/send_password_to_socket/password-not-read",
 
 
8219
              test_send_password_to_socket_password_not_read);
 
 
8220
  test_add_st("/task/send_password_to_socket/EMSGSIZE",
 
 
8221
              test_send_password_to_socket_EMSGSIZE);
 
 
8222
  test_add_st("/task/send_password_to_socket/retry",
 
 
8223
              test_send_password_to_socket_retry);
 
 
8224
  test_add_st("/task/send_password_to_socket/bad-epoll",
 
 
8225
              test_send_password_to_socket_bad_epoll);
 
 
8226
  test_add_st("/task/send_password_to_socket/null-password",
 
 
8227
              test_send_password_to_socket_null_password);
 
 
8228
  test_add_st("/task/send_password_to_socket/empty-password",
 
 
8229
              test_send_password_to_socket_empty_password);
 
 
8230
  test_add_st("/task/send_password_to_socket/empty-str-password",
 
 
8231
              test_send_password_to_socket_empty_str_pass);
 
 
8232
  test_add_st("/task/send_password_to_socket/text-password",
 
 
8233
              test_send_password_to_socket_text_password);
 
 
8234
  test_add_st("/task/send_password_to_socket/binary-password",
 
 
8235
              test_send_password_to_socket_binary_password);
 
 
8236
  test_add_st("/task/send_password_to_socket/nuls-in-password",
 
 
8237
              test_send_password_to_socket_nuls_in_password);
 
 
8238
  test_add_st("/task-creators/add_existing_questions/ENOENT",
 
 
8239
              test_add_existing_questions_ENOENT);
 
 
8240
  test_add_st("/task-creators/add_existing_questions/no-questions",
 
 
8241
              test_add_existing_questions_no_questions);
 
 
8242
  test_add_st("/task-creators/add_existing_questions/one-question",
 
 
8243
              test_add_existing_questions_one_question);
 
 
8244
  test_add_st("/task-creators/add_existing_questions/two-questions",
 
 
8245
              test_add_existing_questions_two_questions);
 
 
8246
  test_add_st("/task-creators/add_existing_questions/non-questions",
 
 
8247
              test_add_existing_questions_non_questions);
 
 
8248
  test_add_st("/task-creators/add_existing_questions/both-types",
 
 
8249
              test_add_existing_questions_both_types);
 
 
8251
  return g_test_run() == 0;
 
 
8254
static bool should_only_run_tests(int *argc_p, char **argv_p[]){
 
 
8255
  GOptionContext *context = g_option_context_new("");
 
 
8257
  g_option_context_set_help_enabled(context, FALSE);
 
 
8258
  g_option_context_set_ignore_unknown_options(context, TRUE);
 
 
8260
  gboolean should_run_tests = FALSE;
 
 
8261
  GOptionEntry entries[] = {
 
 
8262
    { "test", 0, 0, G_OPTION_ARG_NONE,
 
 
8263
      &should_run_tests, "Run tests", NULL },
 
 
8266
  g_option_context_add_main_entries(context, entries, NULL);
 
 
8268
  GError *error = NULL;
 
 
8270
  if(g_option_context_parse(context, argc_p, argv_p, &error) != TRUE){
 
 
8271
    g_option_context_free(context);
 
 
8272
    g_error("Failed to parse options: %s", error->message);
 
 
8275
  g_option_context_free(context);
 
 
8276
  return should_run_tests != FALSE;
 
 
8283
  (if (not (funcall run-tests-in-test-buffer default-directory))
 
 
8284
      (funcall show-test-buffer-in-test-window)
 
 
8285
    (funcall remove-test-window)))
 
 
8286
run-tests-in-test-buffer:
 
 
8288
  (with-current-buffer (get-buffer-create "*Test*")
 
 
8289
    (setq buffer-read-only nil
 
 
8290
          default-directory dir)
 
 
8293
  (let ((process-result
 
 
8294
         (let ((inhibit-read-only t))
 
 
8295
           (process-file-shell-command
 
 
8296
            (funcall get-command-line) nil "*Test*"))))
 
 
8297
    (and (numberp process-result)
 
 
8298
         (= process-result 0))))
 
 
8303
        (funcall find-build-directory (buffer-file-name)))
 
 
8304
       (local-build-directory
 
 
8305
        (if (fboundp 'file-local-name)
 
 
8306
            (file-local-name build-directory)
 
 
8307
          (or (file-remote-p build-directory 'localname)
 
 
8310
        (file-relative-name (file-name-sans-extension
 
 
8311
                             (buffer-file-name)) build-directory))
 
 
8312
       (qbdir (shell-quote-argument local-build-directory))
 
 
8313
       (qcmd (shell-quote-argument command)))
 
 
8314
    (format (concat "cd %s && CFLAGS=-Werror make --silent %s"
 
 
8315
             " && %s --test --verbose") qbdir qcmd qcmd)))
 
 
8316
find-build-directory:
 
 
8317
(lambda (try-directory &optional base-directory)
 
 
8318
  (let ((base-directory (or base-directory try-directory)))
 
 
8319
    (cond ((equal try-directory "/") base-directory)
 
 
8321
            (concat (file-name-as-directory try-directory)
 
 
8322
                    "Makefile")) try-directory)
 
 
8323
          ((funcall find-build-directory
 
 
8324
                    (directory-file-name (file-name-directory
 
 
8327
show-test-buffer-in-test-window:
 
 
8329
  (when (not (get-buffer-window-list "*Test*"))
 
 
8330
    (setq next-error-last-buffer (get-buffer "*Test*"))
 
 
8331
    (let* ((side (if (>= (window-width) 146) 'right 'bottom))
 
 
8332
           (display-buffer-overriding-action
 
 
8333
            `((display-buffer-in-side-window) (side . ,side)
 
 
8334
              (window-height . fit-window-to-buffer)
 
 
8335
              (window-width . fit-window-to-buffer))))
 
 
8336
      (display-buffer "*Test*"))))
 
 
8339
  (let ((test-window (get-buffer-window "*Test*")))
 
 
8340
    (if test-window (delete-window test-window))))
 
 
8341
eval: (add-hook 'after-save-hook run-tests 90 t)