/mandos/trunk

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

« back to all changes in this revision

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

  • Committer: Teddy Hogeborn
  • Date: 2019-07-29 16:35:53 UTC
  • Revision ID: teddy@recompile.se-20190729163553-1i442i2cbx64c537
Make tests and man page examples match

Make the tests test_manual_page_example[1-5] match exactly what is
written in the manual page, and add comments to manual page as
reminders to keep tests and manual page examples in sync.

* mandos-ctl (Test_commands_from_options.test_manual_page_example_1):
  Remove "--verbose" option, since the manual does not have it as the
  first example, and change assertion to match.
* mandos-ctl.xml (EXAMPLE): Add comments to all examples documenting
  which test function they correspond to.  Also remove unnecessary
  quotes from option arguments in fourth example, and clarify language
  slightly in fifth example.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* -*- coding: utf-8; lexical-binding: t -*- */
 
1
/* -*- mode: c; coding: utf-8; after-save-hook: (lambda () (let* ((find-build-directory (lambda (try-directory &optional base-directory) (let ((base-directory (or base-directory try-directory))) (cond ((equal try-directory "/") base-directory) ((file-readable-p (concat (file-name-as-directory try-directory) "Makefile")) try-directory) ((funcall find-build-directory (directory-file-name (file-name-directory try-directory)) base-directory)))))) (build-directory (funcall find-build-directory (buffer-file-name))) (local-build-directory (if (fboundp 'file-local-name) (file-local-name build-directory) (or (file-remote-p build-directory 'localname) build-directory))) (command (file-relative-name (file-name-sans-extension (buffer-file-name)) build-directory))) (pcase (progn (if (get-buffer "*Test*") (kill-buffer "*Test*")) (process-file-shell-command (let ((qbdir (shell-quote-argument local-build-directory)) (qcmd (shell-quote-argument command))) (format "cd %s && CFLAGS=-Werror make --silent %s && %s --test --verbose" qbdir qcmd qcmd)) nil "*Test*")) (0 (let ((w (get-buffer-window "*Test*"))) (if w (delete-window w)))) (_ (with-current-buffer "*Test*" (compilation-mode) (cd-absolute build-directory)) (display-buffer "*Test*" '(display-buffer-in-side-window)))))); -*- */
2
2
/*
3
3
 * Mandos password agent - Simple password agent to run Mandos client
4
4
 *
5
 
 * Copyright © 2019-2022 Teddy Hogeborn
6
 
 * Copyright © 2019-2022 Björn Påhlsson
 
5
 * Copyright © 2019 Teddy Hogeborn
 
6
 * Copyright © 2019 Björn Påhlsson
7
7
 * 
8
8
 * This file is part of Mandos.
9
9
 * 
23
23
 * Contact the authors at <mandos@recompile.se>.
24
24
 */
25
25
 
26
 
#define _GNU_SOURCE             /* pipe2(), O_CLOEXEC, setresgid(),
27
 
                                   setresuid(), asprintf(), getline(),
28
 
                                   basename() */
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 */
 
26
#define _GNU_SOURCE
 
27
#include <inttypes.h>           /* uintmax_t, PRIuMAX, PRIdMAX,
 
28
                                   intmax_t, uint32_t, SCNx32,
 
29
                                   SCNuMAX, SCNxMAX */
 
30
#include <stddef.h>             /* size_t */
33
31
#include <sys/types.h>          /* pid_t, uid_t, gid_t, getuid(),
34
32
                                   getpid() */
35
33
#include <stdbool.h>            /* bool, true, false */
42
40
                                   NSIG, sigismember(), SA_ONSTACK,
43
41
                                   SIG_DFL, SIG_IGN, SIGINT, SIGQUIT,
44
42
                                   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
43
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE,
53
 
                                   malloc(), free(), realloc(),
54
 
                                   setenv(), calloc(), mkdtemp(),
55
 
                                   mkostemp() */
 
44
                                   malloc(), free(), strtoumax(),
 
45
                                   realloc(), setenv(), calloc(),
 
46
                                   mkdtemp(), mkostemp() */
56
47
#include <iso646.h>             /* not, or, and, xor */
57
48
#include <error.h>              /* error() */
58
49
#include <sysexits.h>           /* EX_USAGE, EX_OSERR, EX_OSFILE */
59
50
#include <errno.h>              /* errno, error_t, EACCES,
60
 
                                   ENAMETOOLONG, ENOENT, ENOTDIR,
61
 
                                   ENOMEM, EEXIST, ECHILD, EPERM,
62
 
                                   EAGAIN, EINTR, ENOBUFS, EADDRINUSE,
 
51
                                   ENAMETOOLONG, ENOENT, EEXIST,
 
52
                                   ECHILD, EPERM, ENOMEM, EAGAIN,
 
53
                                   EINTR, ENOBUFS, EADDRINUSE,
63
54
                                   ECONNREFUSED, ECONNRESET,
64
55
                                   ETOOMANYREFS, EMSGSIZE, EBADF,
65
56
                                   EINVAL */
66
57
#include <string.h>             /* strdup(), memcpy(),
67
58
                                   explicit_bzero(), memset(),
68
59
                                   strcmp(), strlen(), strncpy(),
69
 
                                   memcmp(), basename(), strerror() */
 
60
                                   memcmp(), basename() */
70
61
#include <argz.h>               /* argz_create(), argz_count(),
71
62
                                   argz_extract(), argz_next(),
72
63
                                   argz_add() */
82
73
                                   ARGP_ERR_UNKNOWN, ARGP_KEY_ARGS,
83
74
                                   struct argp, argp_parse(),
84
75
                                   ARGP_NO_EXIT */
85
 
#include <stdint.h>             /* SIZE_MAX, uint32_t */
 
76
#include <unistd.h>             /* uid_t, gid_t, close(), pipe2(),
 
77
                                   fork(), _exit(), dup2(),
 
78
                                   STDOUT_FILENO, setresgid(),
 
79
                                   setresuid(), execv(), ssize_t,
 
80
                                   read(), dup3(), getuid(), dup(),
 
81
                                   STDERR_FILENO, pause(), write(),
 
82
                                   rmdir(), unlink(), getpid() */
86
83
#include <sys/mman.h>           /* munlock(), mlock() */
87
84
#include <fcntl.h>              /* O_CLOEXEC, O_NONBLOCK, fcntl(),
88
85
                                   F_GETFD, F_GETFL, FD_CLOEXEC,
89
 
                                   open(), O_WRONLY, O_NOCTTY,
90
 
                                   O_RDONLY, O_NOFOLLOW */
 
86
                                   open(), O_WRONLY, O_RDONLY */
91
87
#include <sys/wait.h>           /* waitpid(), WNOHANG, WIFEXITED(),
92
88
                                   WEXITSTATUS() */
93
89
#include <limits.h>             /* PIPE_BUF, NAME_MAX, INT_MAX */
94
90
#include <sys/inotify.h>        /* inotify_init1(), IN_NONBLOCK,
95
91
                                   IN_CLOEXEC, inotify_add_watch(),
96
92
                                   IN_CLOSE_WRITE, IN_MOVED_TO,
97
 
                                   IN_MOVED_FROM, IN_DELETE,
98
 
                                   IN_EXCL_UNLINK, IN_ONLYDIR,
99
 
                                   struct inotify_event */
 
93
                                   IN_DELETE, struct inotify_event */
100
94
#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() */
 
95
#include <stdio.h>              /* asprintf(), FILE, fopen(),
 
96
                                   getline(), sscanf(), feof(),
 
97
                                   ferror(), fclose(), stderr,
 
98
                                   rename(), fdopen(), fprintf(),
 
99
                                   fscanf() */
105
100
#include <glib.h>    /* GKeyFile, g_key_file_free(), g_key_file_new(),
106
101
                        GError, g_key_file_load_from_file(),
107
102
                        G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
112
107
                        g_assert_null(), g_assert_false(),
113
108
                        g_assert_cmpint(), g_assert_cmpuint(),
114
109
                        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(),
 
110
                        g_test_init(), g_test_add(), g_test_run(),
 
111
                        GOptionContext, g_option_context_new(),
118
112
                        g_option_context_set_help_enabled(), FALSE,
119
113
                        g_option_context_set_ignore_unknown_options(),
120
114
                        gboolean, GOptionEntry, G_OPTION_ARG_NONE,
151
145
  mono_microsecs next_run;
152
146
} __attribute__((designated_init)) task_queue;
153
147
 
154
 
/* "task_func" - A function type for task functions
 
148
/* "func_type" - A function type for task functions
155
149
 
156
150
   I.e. functions for the code which runs when a task is run, all have
157
151
   this type */
437
431
    case EACCES:
438
432
    case ENAMETOOLONG:
439
433
    case ENOENT:
440
 
    case ENOTDIR:
441
434
      return EX_OSFILE;
442
435
    default:
443
436
      return EX_OSERR;
654
647
 
655
648
__attribute__((nonnull, warn_unused_result))
656
649
bool add_to_queue(task_queue *const queue, const task_context task){
657
 
  if((queue->length + 1) > (SIZE_MAX / sizeof(task_context))){
658
 
    /* overflow */
659
 
    error(0, ENOMEM, "Failed to allocate %" PRIuMAX
660
 
          " tasks for queue->tasks", (uintmax_t)(queue->length + 1));
661
 
    errno = ENOMEM;
662
 
    return false;
663
 
  }
664
650
  const size_t needed_size = sizeof(task_context)*(queue->length + 1);
665
651
  if(needed_size > (queue->allocated)){
666
652
    task_context *const new_tasks = realloc(queue->tasks,
871
857
  }
872
858
  close(pipefds[1]);
873
859
 
874
 
  if(pid == -1){
875
 
    error(0, errno, "Failed to fork()");
876
 
    close(pipefds[0]);
877
 
    return false;
878
 
  }
879
 
 
880
860
  if(not add_to_queue(queue, (task_context){
881
861
        .func=wait_for_mandos_client_exit,
882
862
        .pid=pid,
1037
1017
    return false;
1038
1018
  }
1039
1019
 
1040
 
  if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO
1041
 
                       | IN_MOVED_FROM| IN_DELETE | IN_EXCL_UNLINK
1042
 
                       | IN_ONLYDIR)
 
1020
  if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE
 
1021
                       | IN_MOVED_TO | IN_DELETE)
1043
1022
     == -1){
1044
1023
    error(0, errno, "Failed to create inotify watch on %s", dir);
1045
1024
    return false;
1092
1071
  /* "sufficient to read at least one event." - inotify(7) */
1093
1072
  const size_t ievent_size = (sizeof(struct inotify_event)
1094
1073
                              + NAME_MAX + 1);
1095
 
  struct {
1096
 
    struct inotify_event event;
1097
 
    char name_buffer[NAME_MAX + 1];
1098
 
  } ievent_buffer;
1099
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
1074
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
1075
  struct inotify_event *ievent = ((struct inotify_event *)
 
1076
                                  ievent_buffer);
1100
1077
 
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"
1105
 
#endif
1106
1078
  const ssize_t read_length = read(fd, ievent, ievent_size);
1107
 
#if defined(__GNUC__) and __GNUC__ >= 7
1108
 
#pragma GCC diagnostic pop
1109
 
#endif
1110
1079
  if(read_length == 0){ /* EOF */
1111
1080
    error(0, 0, "Got EOF from inotify fd for directory %s", filename);
1112
1081
    *quit_now = true;
1148
1117
             immediately */
1149
1118
          queue->next_run = 1;
1150
1119
        }
1151
 
      } else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
 
1120
      } else if(ievent->mask & IN_DELETE){
1152
1121
        if(not string_set_add(cancelled_filenames,
1153
1122
                              question_filename)){
1154
1123
          error(0, errno, "Could not add question %s to"
1204
1173
  bool *const password_is_read = task.password_is_read;
1205
1174
 
1206
1175
  /* 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 */
 
1176
     question file.  See <https://www.freedesktop.org/wiki/Software
 
1177
     /systemd/PasswordAgents/> for specification of contents */
1209
1178
  __attribute__((nonnull))
1210
1179
    void cleanup_g_key_file(GKeyFile **key_file){
1211
1180
    if(*key_file != NULL){
1501
1470
         not. You may but don't have to include a final NUL byte in
1502
1471
         your message.
1503
1472
 
1504
 
         — <https://systemd.io/PASSWORD_AGENTS/> (Tue, 15 Sep 2020
1505
 
         14:24:20 GMT)
 
1473
         — <https://www.freedesktop.org/wiki/Software/systemd/
 
1474
         PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
1506
1475
      */
1507
1476
      send_buffer[0] = '+';     /* Prefix with "+" */
1508
1477
      /* Always add an extra NUL */
1513
1482
      errno = 0;
1514
1483
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1515
1484
                           MSG_NOSIGNAL);
1516
 
      const error_t saved_errno = (ssret < 0) ? errno : 0;
 
1485
      const error_t saved_errno = errno;
1517
1486
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1518
1487
      explicit_bzero(send_buffer, send_buffer_length);
1519
1488
#else
1537
1506
          /* Retry, below */
1538
1507
          break;
1539
1508
        case EMSGSIZE:
1540
 
          error(0, saved_errno, "Password of size %" PRIuMAX
1541
 
                " is too big", (uintmax_t)password->length);
 
1509
          error(0, 0, "Password of size %" PRIuMAX " is too big",
 
1510
                (uintmax_t)password->length);
1542
1511
#if __GNUC__ < 7
1543
1512
          /* FALLTHROUGH */
1544
1513
#else
1546
1515
#endif
1547
1516
        case 0:
1548
1517
          if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1549
 
            error(0, 0, "Password only partially sent to socket %s: %"
1550
 
                  PRIuMAX " out of %" PRIuMAX " bytes sent", filename,
1551
 
                  (uintmax_t)ssret, (uintmax_t)send_buffer_length);
 
1518
            error(0, 0, "Password only partially sent to socket");
1552
1519
          }
1553
1520
#if __GNUC__ < 7
1554
1521
          /* FALLTHROUGH */
1910
1877
  g_assert_true(queue->tasks[0].func == dummy_func);
1911
1878
}
1912
1879
 
1913
 
static void test_add_to_queue_overflow(__attribute__((unused))
1914
 
                                       test_fixture *fixture,
1915
 
                                       __attribute__((unused))
1916
 
                                       gconstpointer user_data){
1917
 
  __attribute__((cleanup(cleanup_queue)))
1918
 
    task_queue *queue = create_queue();
1919
 
  g_assert_nonnull(queue);
1920
 
  g_assert_true(queue->length == 0);
1921
 
  queue->length = SIZE_MAX / sizeof(task_context); /* fake max size */
1922
 
 
1923
 
  FILE *real_stderr = stderr;
1924
 
  FILE *devnull = fopen("/dev/null", "we");
1925
 
  g_assert_nonnull(devnull);
1926
 
  stderr = devnull;
1927
 
  const bool ret = add_to_queue(queue,
1928
 
                                (task_context){ .func=dummy_func });
1929
 
  g_assert_true(errno == ENOMEM);
1930
 
  g_assert_false(ret);
1931
 
  stderr = real_stderr;
1932
 
  g_assert_cmpint(fclose(devnull), ==, 0);
1933
 
  queue->length = 0;            /* Restore real size */
1934
 
}
1935
 
 
1936
1880
static void dummy_func(__attribute__((unused))
1937
1881
                       const task_context task,
1938
1882
                       __attribute__((unused))
2209
2153
    }
2210
2154
    exit(EXIT_SUCCESS);
2211
2155
  }
2212
 
  if(pid == -1){
2213
 
    error(EXIT_FAILURE, errno, "Failed to fork()");
2214
 
  }
2215
 
 
2216
2156
  int status;
2217
2157
  waitpid(pid, &status, 0);
2218
2158
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2280
2220
 
2281
2221
  {
2282
2222
    __attribute__((cleanup(cleanup_close)))
2283
 
      const int devnull_fd = open("/dev/null",
2284
 
                                  O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2223
      const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
2285
2224
    g_assert_cmpint(devnull_fd, >=, 0);
2286
2225
    __attribute__((cleanup(cleanup_close)))
2287
2226
      const int real_stderr_fd = dup(STDERR_FILENO);
2311
2250
    {
2312
2251
      __attribute__((cleanup(cleanup_close)))
2313
2252
        const int devnull_fd = open("/dev/null",
2314
 
                                    O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2253
                                    O_WRONLY | O_CLOEXEC);
2315
2254
      g_assert_cmpint(devnull_fd, >=, 0);
2316
2255
      __attribute__((cleanup(cleanup_close)))
2317
2256
        const int real_stderr_fd = dup(STDERR_FILENO);
2661
2600
  bool password_is_read = false;
2662
2601
  const char helper_directory[] = "/nonexistent";
2663
2602
  const char *const argv[] = { "/bin/sh", "-c",
2664
 
    "printf %s \"${MANDOSPLUGINHELPERDIR}\"", NULL };
 
2603
    "echo -n ${MANDOSPLUGINHELPERDIR}", NULL };
2665
2604
 
2666
2605
  const bool success = start_mandos_client(queue, epoll_fd,
2667
2606
                                           &mandos_client_exited,
2962
2901
 
2963
2902
  __attribute__((cleanup(cleanup_close)))
2964
2903
    const int devnull_fd = open("/dev/null",
2965
 
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2904
                                O_WRONLY | O_CLOEXEC);
2966
2905
  g_assert_cmpint(devnull_fd, >=, 0);
2967
2906
  __attribute__((cleanup(cleanup_close)))
2968
2907
    const int real_stderr_fd = dup(STDERR_FILENO);
3033
2972
 
3034
2973
  __attribute__((cleanup(cleanup_close)))
3035
2974
    const int devnull_fd = open("/dev/null",
3036
 
                                O_WRONLY | O_CLOEXEC, O_NOCTTY);
 
2975
                                O_WRONLY | O_CLOEXEC);
3037
2976
  g_assert_cmpint(devnull_fd, >=, 0);
3038
2977
  __attribute__((cleanup(cleanup_close)))
3039
2978
    const int real_stderr_fd = dup(STDERR_FILENO);
3077
3016
    buffer password = {};
3078
3017
 
3079
3018
  /* Reading /proc/self/mem from offset 0 will always give EIO */
3080
 
  const int fd = open("/proc/self/mem",
3081
 
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
 
3019
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
3082
3020
 
3083
3021
  bool password_is_read = false;
3084
3022
  bool quit_now = false;
3512
3450
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3513
3451
}
3514
3452
 
3515
 
static void test_add_inotify_dir_watch_nondir(__attribute__((unused))
3516
 
                                              test_fixture *fixture,
3517
 
                                            __attribute__((unused))
3518
 
                                              gconstpointer
3519
 
                                              user_data){
3520
 
  __attribute__((cleanup(cleanup_close)))
3521
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3522
 
  g_assert_cmpint(epoll_fd, >=, 0);
3523
 
  __attribute__((cleanup(cleanup_queue)))
3524
 
    task_queue *queue = create_queue();
3525
 
  g_assert_nonnull(queue);
3526
 
  __attribute__((cleanup(string_set_clear)))
3527
 
    string_set cancelled_filenames = {};
3528
 
  const mono_microsecs current_time = 0;
3529
 
 
3530
 
  bool quit_now = false;
3531
 
  buffer password = {};
3532
 
  bool mandos_client_exited = false;
3533
 
  bool password_is_read = false;
3534
 
 
3535
 
  const char not_a_directory[] = "/dev/tty";
3536
 
 
3537
 
  FILE *real_stderr = stderr;
3538
 
  FILE *devnull = fopen("/dev/null", "we");
3539
 
  g_assert_nonnull(devnull);
3540
 
  stderr = devnull;
3541
 
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3542
 
                                       &password, not_a_directory,
3543
 
                                       &cancelled_filenames,
3544
 
                                       &current_time,
3545
 
                                       &mandos_client_exited,
3546
 
                                       &password_is_read));
3547
 
  stderr = real_stderr;
3548
 
  g_assert_cmpint(fclose(devnull), ==, 0);
3549
 
 
3550
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3551
 
}
3552
 
 
3553
3453
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3554
3454
                                              test_fixture *fixture,
3555
3455
                                              __attribute__((unused))
3770
3670
}
3771
3671
 
3772
3672
static
3773
 
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
3774
 
                                              test_fixture *fixture,
3775
 
                                              __attribute__((unused))
3776
 
                                              gconstpointer
3777
 
                                              user_data){
3778
 
  __attribute__((cleanup(cleanup_close)))
3779
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3780
 
  g_assert_cmpint(epoll_fd, >=, 0);
3781
 
  __attribute__((cleanup(cleanup_queue)))
3782
 
    task_queue *queue = create_queue();
3783
 
  g_assert_nonnull(queue);
3784
 
  __attribute__((cleanup(string_set_clear)))
3785
 
    string_set cancelled_filenames = {};
3786
 
  const mono_microsecs current_time = 0;
3787
 
 
3788
 
  bool quit_now = false;
3789
 
  buffer password = {};
3790
 
  bool mandos_client_exited = false;
3791
 
  bool password_is_read = false;
3792
 
 
3793
 
  __attribute__((cleanup(cleanup_string)))
3794
 
    char *tempdir = make_temporary_directory();
3795
 
  g_assert_nonnull(tempdir);
3796
 
 
3797
 
  __attribute__((cleanup(cleanup_string)))
3798
 
    char *tempfilename = make_temporary_file_in_directory(tempdir);
3799
 
  g_assert_nonnull(tempfilename);
3800
 
 
3801
 
  __attribute__((cleanup(cleanup_string)))
3802
 
    char *targetdir = make_temporary_directory();
3803
 
  g_assert_nonnull(targetdir);
3804
 
 
3805
 
  __attribute__((cleanup(cleanup_string)))
3806
 
    char *targetfilename = NULL;
3807
 
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
3808
 
                           basename(tempfilename)), >, 0);
3809
 
  g_assert_nonnull(targetfilename);
3810
 
 
3811
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3812
 
                                      &password, tempdir,
3813
 
                                      &cancelled_filenames,
3814
 
                                      &current_time,
3815
 
                                      &mandos_client_exited,
3816
 
                                      &password_is_read));
3817
 
 
3818
 
  g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
3819
 
 
3820
 
  const task_context *const added_read_task
3821
 
    = find_matching_task(queue,
3822
 
                         (task_context){ .func=read_inotify_event });
3823
 
  g_assert_nonnull(added_read_task);
3824
 
 
3825
 
  /* "sufficient to read at least one event." - inotify(7) */
3826
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3827
 
                              + NAME_MAX + 1);
3828
 
  struct inotify_event *ievent = malloc(ievent_size);
3829
 
  g_assert_nonnull(ievent);
3830
 
 
3831
 
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3832
 
 
3833
 
  g_assert_cmpint((int)read_size, >, 0);
3834
 
  g_assert_true(ievent->mask & IN_MOVED_FROM);
3835
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
3836
 
 
3837
 
  free(ievent);
3838
 
 
3839
 
  g_assert_cmpint(unlink(targetfilename), ==, 0);
3840
 
  g_assert_cmpint(rmdir(targetdir), ==, 0);
3841
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3842
 
}
3843
 
 
3844
 
static
3845
3673
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3846
3674
                                          test_fixture *fixture,
3847
3675
                                          __attribute__((unused))
3905
3733
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3906
3734
}
3907
3735
 
3908
 
static
3909
 
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
3910
 
                                               test_fixture *fixture,
3911
 
                                               __attribute__((unused))
3912
 
                                               gconstpointer
3913
 
                                               user_data){
3914
 
  __attribute__((cleanup(cleanup_close)))
3915
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3916
 
  g_assert_cmpint(epoll_fd, >=, 0);
3917
 
  __attribute__((cleanup(cleanup_queue)))
3918
 
    task_queue *queue = create_queue();
3919
 
  g_assert_nonnull(queue);
3920
 
  __attribute__((cleanup(string_set_clear)))
3921
 
    string_set cancelled_filenames = {};
3922
 
  const mono_microsecs current_time = 0;
3923
 
 
3924
 
  bool quit_now = false;
3925
 
  buffer password = {};
3926
 
  bool mandos_client_exited = false;
3927
 
  bool password_is_read = false;
3928
 
 
3929
 
  __attribute__((cleanup(cleanup_string)))
3930
 
    char *tempdir = make_temporary_directory();
3931
 
  g_assert_nonnull(tempdir);
3932
 
 
3933
 
  __attribute__((cleanup(cleanup_string)))
3934
 
    char *tempfile = make_temporary_file_in_directory(tempdir);
3935
 
  g_assert_nonnull(tempfile);
3936
 
  int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
3937
 
                         | O_NOFOLLOW);
3938
 
  g_assert_cmpint(tempfile_fd, >, 2);
3939
 
 
3940
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3941
 
                                      &password, tempdir,
3942
 
                                      &cancelled_filenames,
3943
 
                                      &current_time,
3944
 
                                      &mandos_client_exited,
3945
 
                                      &password_is_read));
3946
 
  g_assert_cmpint(unlink(tempfile), ==, 0);
3947
 
 
3948
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
3949
 
 
3950
 
  const task_context *const added_read_task
3951
 
    = find_matching_task(queue,
3952
 
                         (task_context){ .func=read_inotify_event });
3953
 
  g_assert_nonnull(added_read_task);
3954
 
 
3955
 
  g_assert_cmpint(added_read_task->fd, >, 2);
3956
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3957
 
 
3958
 
  /* "sufficient to read at least one event." - inotify(7) */
3959
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3960
 
                              + NAME_MAX + 1);
3961
 
  struct inotify_event *ievent = malloc(ievent_size);
3962
 
  g_assert_nonnull(ievent);
3963
 
 
3964
 
  ssize_t read_size = 0;
3965
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
3966
 
 
3967
 
  g_assert_cmpint((int)read_size, >, 0);
3968
 
  g_assert_true(ievent->mask & IN_DELETE);
3969
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3970
 
 
3971
 
  g_assert_cmpint(close(tempfile_fd), ==, 0);
3972
 
 
3973
 
  /* IN_EXCL_UNLINK should make the closing of the previously unlinked
3974
 
     file not appear as an ievent, so we should not see it now. */
3975
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
3976
 
  g_assert_cmpint((int)read_size, ==, -1);
3977
 
  g_assert_true(errno == EAGAIN);
3978
 
 
3979
 
  free(ievent);
3980
 
 
3981
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3982
 
}
3983
 
 
3984
3736
static void test_read_inotify_event_readerror(__attribute__((unused))
3985
3737
                                              test_fixture *fixture,
3986
3738
                                              __attribute__((unused))
3992
3744
  const mono_microsecs current_time = 0;
3993
3745
 
3994
3746
  /* Reading /proc/self/mem from offset 0 will always result in EIO */
3995
 
  const int fd = open("/proc/self/mem",
3996
 
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
 
3747
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
3997
3748
 
3998
3749
  bool quit_now = false;
3999
3750
  __attribute__((cleanup(cleanup_queue)))
4178
3929
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4179
3930
                                  + NAME_MAX + 1);
4180
3931
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4181
 
  struct {
4182
 
    struct inotify_event event;
4183
 
    char name_buffer[NAME_MAX + 1];
4184
 
  } ievent_buffer;
4185
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
3932
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
3933
  struct inotify_event *ievent = ((struct inotify_event *)
 
3934
                                  ievent_buffer);
4186
3935
 
4187
3936
  const char dummy_file_name[] = "ask.dummy_file_name";
4188
3937
  ievent->mask = IN_CLOSE_WRITE;
4190
3939
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4191
3940
  const size_t ievent_size = (sizeof(struct inotify_event)
4192
3941
                              + sizeof(dummy_file_name));
4193
 
#if defined(__GNUC__) and __GNUC__ >= 11
4194
 
#pragma GCC diagnostic push
4195
 
  /* ievent is pointing into a struct which is of sufficient size */
4196
 
#pragma GCC diagnostic ignored "-Wstringop-overread"
4197
 
#endif
4198
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
3942
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
4199
3943
                  ==, ievent_size);
4200
 
#if defined(__GNUC__) and __GNUC__ >= 11
4201
 
#pragma GCC diagnostic pop
4202
 
#endif
4203
3944
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4204
3945
 
4205
3946
  bool quit_now = false;
4281
4022
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4282
4023
                                  + NAME_MAX + 1);
4283
4024
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4284
 
  struct {
4285
 
    struct inotify_event event;
4286
 
    char name_buffer[NAME_MAX + 1];
4287
 
  } ievent_buffer;
4288
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4025
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
4026
  struct inotify_event *ievent = ((struct inotify_event *)
 
4027
                                  ievent_buffer);
4289
4028
 
4290
4029
  const char dummy_file_name[] = "ask.dummy_file_name";
4291
4030
  ievent->mask = IN_MOVED_TO;
4293
4032
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4294
4033
  const size_t ievent_size = (sizeof(struct inotify_event)
4295
4034
                              + sizeof(dummy_file_name));
4296
 
#if defined(__GNUC__) and __GNUC__ >= 11
4297
 
#pragma GCC diagnostic push
4298
 
  /* ievent is pointing into a struct which is of sufficient size */
4299
 
#pragma GCC diagnostic ignored "-Wstringop-overread"
4300
 
#endif
4301
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4035
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
4302
4036
                  ==, ievent_size);
4303
 
#if defined(__GNUC__) and __GNUC__ >= 11
4304
 
#pragma GCC diagnostic pop
4305
 
#endif
4306
4037
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4307
4038
 
4308
4039
  bool quit_now = false;
4367
4098
      }));
4368
4099
}
4369
4100
 
4370
 
static
4371
 
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
4372
 
                                           test_fixture *fixture,
4373
 
                                           __attribute__((unused))
4374
 
                                           gconstpointer user_data){
4375
 
  __attribute__((cleanup(cleanup_close)))
4376
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4377
 
  g_assert_cmpint(epoll_fd, >=, 0);
4378
 
  __attribute__((cleanup(string_set_clear)))
4379
 
    string_set cancelled_filenames = {};
4380
 
  const mono_microsecs current_time = 0;
4381
 
 
4382
 
  int pipefds[2];
4383
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4384
 
 
4385
 
  /* "sufficient to read at least one event." - inotify(7) */
4386
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4387
 
                                  + NAME_MAX + 1);
4388
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4389
 
  struct {
4390
 
    struct inotify_event event;
4391
 
    char name_buffer[NAME_MAX + 1];
4392
 
  } ievent_buffer;
4393
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4394
 
 
4395
 
  const char dummy_file_name[] = "ask.dummy_file_name";
4396
 
  ievent->mask = IN_MOVED_FROM;
4397
 
  ievent->len = sizeof(dummy_file_name);
4398
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4399
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4400
 
                              + sizeof(dummy_file_name));
4401
 
#if defined(__GNUC__) and __GNUC__ >= 11
4402
 
#pragma GCC diagnostic push
4403
 
  /* ievent is pointing into a struct which is of sufficient size */
4404
 
#pragma GCC diagnostic ignored "-Wstringop-overread"
4405
 
#endif
4406
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4407
 
                  ==, ievent_size);
4408
 
#if defined(__GNUC__) and __GNUC__ >= 11
4409
 
#pragma GCC diagnostic pop
4410
 
#endif
4411
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4412
 
 
4413
 
  bool quit_now = false;
4414
 
  buffer password = {};
4415
 
  bool mandos_client_exited = false;
4416
 
  bool password_is_read = false;
4417
 
  __attribute__((cleanup(cleanup_queue)))
4418
 
    task_queue *queue = create_queue();
4419
 
  g_assert_nonnull(queue);
4420
 
 
4421
 
  task_context task = {
4422
 
    .func=read_inotify_event,
4423
 
    .epoll_fd=epoll_fd,
4424
 
    .fd=pipefds[0],
4425
 
    .quit_now=&quit_now,
4426
 
    .password=&password,
4427
 
    .filename=strdup("/nonexistent"),
4428
 
    .cancelled_filenames=&cancelled_filenames,
4429
 
    .current_time=&current_time,
4430
 
    .mandos_client_exited=&mandos_client_exited,
4431
 
    .password_is_read=&password_is_read,
4432
 
  };
4433
 
  task.func(task, queue);
4434
 
  g_assert_false(quit_now);
4435
 
  g_assert_true(queue->next_run == 0);
4436
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4437
 
 
4438
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4439
 
        .func=read_inotify_event,
4440
 
        .epoll_fd=epoll_fd,
4441
 
        .fd=pipefds[0],
4442
 
        .quit_now=&quit_now,
4443
 
        .password=&password,
4444
 
        .filename=task.filename,
4445
 
        .cancelled_filenames=&cancelled_filenames,
4446
 
        .current_time=&current_time,
4447
 
        .mandos_client_exited=&mandos_client_exited,
4448
 
        .password_is_read=&password_is_read,
4449
 
      }));
4450
 
 
4451
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4452
 
                                   EPOLLIN | EPOLLRDHUP));
4453
 
 
4454
 
  __attribute__((cleanup(cleanup_string)))
4455
 
    char *filename = NULL;
4456
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4457
 
                           dummy_file_name), >, 0);
4458
 
  g_assert_nonnull(filename);
4459
 
  g_assert_true(string_set_contains(*task.cancelled_filenames,
4460
 
                                    filename));
4461
 
}
4462
 
 
4463
4101
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4464
4102
                                              test_fixture *fixture,
4465
4103
                                              __attribute__((unused))
4479
4117
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4480
4118
                                  + NAME_MAX + 1);
4481
4119
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4482
 
  struct {
4483
 
    struct inotify_event event;
4484
 
    char name_buffer[NAME_MAX + 1];
4485
 
  } ievent_buffer;
4486
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4120
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
4121
  struct inotify_event *ievent = ((struct inotify_event *)
 
4122
                                  ievent_buffer);
4487
4123
 
4488
4124
  const char dummy_file_name[] = "ask.dummy_file_name";
4489
4125
  ievent->mask = IN_DELETE;
4491
4127
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4492
4128
  const size_t ievent_size = (sizeof(struct inotify_event)
4493
4129
                              + sizeof(dummy_file_name));
4494
 
#if defined(__GNUC__) and __GNUC__ >= 11
4495
 
#pragma GCC diagnostic push
4496
 
  /* ievent is pointing into a struct which is of sufficient size */
4497
 
#pragma GCC diagnostic ignored "-Wstringop-overread"
4498
 
#endif
4499
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4130
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
4500
4131
                  ==, ievent_size);
4501
 
#if defined(__GNUC__) and __GNUC__ >= 11
4502
 
#pragma GCC diagnostic pop
4503
 
#endif
4504
4132
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4505
4133
 
4506
4134
  bool quit_now = false;
4571
4199
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4572
4200
                                  + NAME_MAX + 1);
4573
4201
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4574
 
  struct {
4575
 
    struct inotify_event event;
4576
 
    char name_buffer[NAME_MAX + 1];
4577
 
  } ievent_buffer;
4578
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4202
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
4203
  struct inotify_event *ievent = ((struct inotify_event *)
 
4204
                                  ievent_buffer);
4579
4205
 
4580
4206
  const char dummy_file_name[] = "ignored.dummy_file_name";
4581
4207
  ievent->mask = IN_CLOSE_WRITE;
4583
4209
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4584
4210
  const size_t ievent_size = (sizeof(struct inotify_event)
4585
4211
                              + sizeof(dummy_file_name));
4586
 
#if defined(__GNUC__) and __GNUC__ >= 11
4587
 
#pragma GCC diagnostic push
4588
 
  /* ievent is pointing into a struct which is of sufficient size */
4589
 
#pragma GCC diagnostic ignored "-Wstringop-overread"
4590
 
#endif
4591
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4212
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
4592
4213
                  ==, ievent_size);
4593
 
#if defined(__GNUC__) and __GNUC__ >= 11
4594
 
#pragma GCC diagnostic pop
4595
 
#endif
4596
4214
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4597
4215
 
4598
4216
  bool quit_now = false;
4655
4273
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4656
4274
                                  + NAME_MAX + 1);
4657
4275
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4658
 
  struct {
4659
 
    struct inotify_event event;
4660
 
    char name_buffer[NAME_MAX + 1];
4661
 
  } ievent_buffer;
4662
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4276
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
4277
  struct inotify_event *ievent = ((struct inotify_event *)
 
4278
                                  ievent_buffer);
4663
4279
 
4664
4280
  const char dummy_file_name[] = "ignored.dummy_file_name";
4665
4281
  ievent->mask = IN_MOVED_TO;
4667
4283
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4668
4284
  const size_t ievent_size = (sizeof(struct inotify_event)
4669
4285
                              + sizeof(dummy_file_name));
4670
 
#if defined(__GNUC__) and __GNUC__ >= 11
4671
 
#pragma GCC diagnostic push
4672
 
  /* ievent is pointing into a struct which is of sufficient size */
4673
 
#pragma GCC diagnostic ignored "-Wstringop-overread"
4674
 
#endif
4675
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4286
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
4676
4287
                  ==, ievent_size);
4677
 
#if defined(__GNUC__) and __GNUC__ >= 11
4678
 
#pragma GCC diagnostic pop
4679
 
#endif
4680
4288
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4681
4289
 
4682
4290
  bool quit_now = false;
4722
4330
                                   EPOLLIN | EPOLLRDHUP));
4723
4331
}
4724
4332
 
4725
 
static void
4726
 
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
4727
 
                                              test_fixture *fixture,
4728
 
                                              __attribute__((unused))
4729
 
                                              gconstpointer
4730
 
                                              user_data){
4731
 
  __attribute__((cleanup(cleanup_close)))
4732
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4733
 
  g_assert_cmpint(epoll_fd, >=, 0);
4734
 
  __attribute__((cleanup(string_set_clear)))
4735
 
    string_set cancelled_filenames = {};
4736
 
  const mono_microsecs current_time = 0;
4737
 
 
4738
 
  int pipefds[2];
4739
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4740
 
 
4741
 
  /* "sufficient to read at least one event." - inotify(7) */
4742
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4743
 
                                  + NAME_MAX + 1);
4744
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4745
 
  struct {
4746
 
    struct inotify_event event;
4747
 
    char name_buffer[NAME_MAX + 1];
4748
 
  } ievent_buffer;
4749
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4750
 
 
4751
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
4752
 
  ievent->mask = IN_MOVED_FROM;
4753
 
  ievent->len = sizeof(dummy_file_name);
4754
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4755
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4756
 
                              + sizeof(dummy_file_name));
4757
 
#if defined(__GNUC__) and __GNUC__ >= 11
4758
 
#pragma GCC diagnostic push
4759
 
  /* ievent is pointing into a struct which is of sufficient size */
4760
 
#pragma GCC diagnostic ignored "-Wstringop-overread"
4761
 
#endif
4762
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4763
 
                  ==, ievent_size);
4764
 
#if defined(__GNUC__) and __GNUC__ >= 11
4765
 
#pragma GCC diagnostic pop
4766
 
#endif
4767
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4768
 
 
4769
 
  bool quit_now = false;
4770
 
  buffer password = {};
4771
 
  bool mandos_client_exited = false;
4772
 
  bool password_is_read = false;
4773
 
  __attribute__((cleanup(cleanup_queue)))
4774
 
    task_queue *queue = create_queue();
4775
 
  g_assert_nonnull(queue);
4776
 
 
4777
 
  task_context task = {
4778
 
    .func=read_inotify_event,
4779
 
    .epoll_fd=epoll_fd,
4780
 
    .fd=pipefds[0],
4781
 
    .quit_now=&quit_now,
4782
 
    .password=&password,
4783
 
    .filename=strdup("/nonexistent"),
4784
 
    .cancelled_filenames=&cancelled_filenames,
4785
 
    .current_time=&current_time,
4786
 
    .mandos_client_exited=&mandos_client_exited,
4787
 
    .password_is_read=&password_is_read,
4788
 
  };
4789
 
  task.func(task, queue);
4790
 
  g_assert_false(quit_now);
4791
 
  g_assert_true(queue->next_run == 0);
4792
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4793
 
 
4794
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4795
 
        .func=read_inotify_event,
4796
 
        .epoll_fd=epoll_fd,
4797
 
        .fd=pipefds[0],
4798
 
        .quit_now=&quit_now,
4799
 
        .password=&password,
4800
 
        .filename=task.filename,
4801
 
        .cancelled_filenames=&cancelled_filenames,
4802
 
        .current_time=&current_time,
4803
 
        .mandos_client_exited=&mandos_client_exited,
4804
 
        .password_is_read=&password_is_read,
4805
 
      }));
4806
 
 
4807
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4808
 
                                   EPOLLIN | EPOLLRDHUP));
4809
 
 
4810
 
  __attribute__((cleanup(cleanup_string)))
4811
 
    char *filename = NULL;
4812
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4813
 
                           dummy_file_name), >, 0);
4814
 
  g_assert_nonnull(filename);
4815
 
  g_assert_false(string_set_contains(cancelled_filenames, filename));
4816
 
}
4817
 
 
4818
4333
static
4819
4334
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4820
4335
                                               test_fixture *fixture,
4835
4350
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4836
4351
                                  + NAME_MAX + 1);
4837
4352
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4838
 
  struct {
4839
 
    struct inotify_event event;
4840
 
    char name_buffer[NAME_MAX + 1];
4841
 
  } ievent_buffer;
4842
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4353
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
4354
  struct inotify_event *ievent = ((struct inotify_event *)
 
4355
                                  ievent_buffer);
4843
4356
 
4844
4357
  const char dummy_file_name[] = "ignored.dummy_file_name";
4845
4358
  ievent->mask = IN_DELETE;
4847
4360
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4848
4361
  const size_t ievent_size = (sizeof(struct inotify_event)
4849
4362
                              + sizeof(dummy_file_name));
4850
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4363
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
4851
4364
                  ==, ievent_size);
4852
4365
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4853
4366
 
5740
5253
                                            __attribute__((unused))
5741
5254
                                            gconstpointer user_data){
5742
5255
  __attribute__((cleanup(cleanup_close)))
5743
 
    const int epoll_fd = open("/dev/null",
5744
 
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
5256
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
5745
5257
  __attribute__((cleanup(cleanup_string)))
5746
5258
    char *const question_filename = strdup("/nonexistent/question");
5747
5259
  g_assert_nonnull(question_filename);
5876
5388
  char write_data[PIPE_BUF];
5877
5389
  {
5878
5390
    /* Construct test password buffer */
5879
 
    /* Start with + since that is what the real protocol uses */
 
5391
    /* Start with + since that is what the real procotol uses */
5880
5392
    write_data[0] = '+';
5881
5393
    /* Set a special character at string end just to mark the end */
5882
5394
    write_data[sizeof(write_data)-2] = 'y';
6034
5546
  char *const filename = strdup("/nonexistent/socket");
6035
5547
  __attribute__((cleanup(string_set_clear)))
6036
5548
    string_set cancelled_filenames = {};
6037
 
  int socketfds[2];
6038
 
 
6039
 
  /* Find a message size which triggers EMSGSIZE */
6040
 
  __attribute__((cleanup(cleanup_string)))
6041
 
    char *message_buffer = NULL;
6042
 
  size_t message_size = PIPE_BUF + 1;
6043
 
  for(ssize_t ssret = 0; ssret >= 0; message_size += 1024){
6044
 
    if(message_size >= 1024*1024*1024){ /* 1 GiB */
6045
 
      g_test_skip("Skipping EMSGSIZE test: Will not try 1GiB");
6046
 
      return;
6047
 
    }
6048
 
    message_buffer = realloc(message_buffer, message_size);
6049
 
    if(message_buffer == NULL){
6050
 
      g_test_skip("Skipping EMSGSIZE test");
6051
 
      g_test_message("Failed to malloc() %" PRIuMAX " bytes",
6052
 
                     (uintmax_t)message_size);
6053
 
      return;
6054
 
    }
6055
 
    /* Fill buffer with 'x' */
6056
 
    memset(message_buffer, 'x', message_size);
6057
 
    /* Create a new socketpair for each message size to avoid having
6058
 
       to empty the pipe by reading the message to a separate buffer
6059
 
    */
6060
 
    g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6061
 
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6062
 
                               socketfds), ==, 0);
6063
 
    ssret = send(socketfds[1], message_buffer, message_size,
6064
 
                 MSG_NOSIGNAL);
6065
 
    error_t saved_errno = errno;
6066
 
    g_assert_cmpint(close(socketfds[0]), ==, 0);
6067
 
    g_assert_cmpint(close(socketfds[1]), ==, 0);
6068
 
 
6069
 
    if(ssret < 0){
6070
 
      if(saved_errno != EMSGSIZE) {
6071
 
        g_test_skip("Skipping EMSGSIZE test");
6072
 
        g_test_message("Error on send(%" PRIuMAX " bytes): %s",
6073
 
                       (uintmax_t)message_size,
6074
 
                       strerror(saved_errno));
6075
 
        return;
6076
 
      }
6077
 
      break;
6078
 
    } else if(ssret != (ssize_t)message_size){
6079
 
      g_test_skip("Skipping EMSGSIZE test");
6080
 
      g_test_message("Partial send(): %" PRIuMAX " of %" PRIdMAX
6081
 
                     " bytes", (uintmax_t)ssret,
6082
 
                     (intmax_t)message_size);
6083
 
      return;
6084
 
    }
6085
 
  }
6086
 
  g_test_message("EMSGSIZE triggered by %" PRIdMAX " bytes",
6087
 
                 (intmax_t)message_size);
6088
 
 
6089
 
  buffer password = {
6090
 
    .data=message_buffer,
6091
 
    .length=message_size - 2,   /* Compensate for added '+' and NUL */
6092
 
    .allocated=message_size,
 
5549
  const size_t oversized = 1024*1024; /* Limit seems to be 212960 */
 
5550
  __attribute__((cleanup(cleanup_buffer)))
 
5551
    buffer password = {
 
5552
    .data=malloc(oversized),
 
5553
    .length=oversized,
 
5554
    .allocated=oversized,
6093
5555
  };
 
5556
  g_assert_nonnull(password.data);
6094
5557
  if(mlock(password.data, password.allocated) != 0){
6095
5558
    g_assert_true(errno == EPERM or errno == ENOMEM);
6096
5559
  }
 
5560
  /* Construct test password buffer */
 
5561
  /* Start with + since that is what the real procotol uses */
 
5562
  password.data[0] = '+';
 
5563
  /* Set a special character at string end just to mark the end */
 
5564
  password.data[oversized-3] = 'y';
 
5565
  /* Set NUL at buffer end, as suggested by the protocol */
 
5566
  password.data[oversized-2] = '\0';
 
5567
  /* Fill rest of password with 'x' */
 
5568
  memset(password.data+1, 'x', oversized-3);
6097
5569
 
6098
5570
  __attribute__((cleanup(cleanup_queue)))
6099
5571
    task_queue *queue = create_queue();
6100
5572
  g_assert_nonnull(queue);
 
5573
  int socketfds[2];
6101
5574
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6102
5575
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6103
5576
                             socketfds), ==, 0);
6190
5663
                                            __attribute__((unused))
6191
5664
                                            gconstpointer user_data){
6192
5665
  __attribute__((cleanup(cleanup_close)))
6193
 
    const int epoll_fd = open("/dev/null",
6194
 
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
5666
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
6195
5667
  __attribute__((cleanup(cleanup_string)))
6196
5668
    char *const question_filename = strdup("/nonexistent/question");
6197
5669
  g_assert_nonnull(question_filename);
6460
5932
                                              const char *const
6461
5933
                                              dirname){
6462
5934
  __attribute__((cleanup(cleanup_close)))
6463
 
    const int devnull_fd = open("/dev/null",
6464
 
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
5935
    const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
6465
5936
  g_assert_cmpint(devnull_fd, >=, 0);
6466
5937
  __attribute__((cleanup(cleanup_close)))
6467
5938
    const int real_stderr_fd = dup(STDERR_FILENO);
8010
7481
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
8011
7482
  test_add("/queue/create", test_create_queue);
8012
7483
  test_add("/queue/add", test_add_to_queue);
8013
 
  test_add("/queue/add/overflow", test_add_to_queue_overflow);
8014
7484
  test_add("/queue/has_question/empty",
8015
7485
           test_queue_has_question_empty);
8016
7486
  test_add("/queue/has_question/false",
8103
7573
              test_add_inotify_dir_watch);
8104
7574
  test_add_st("/task-creators/add_inotify_dir_watch/fail",
8105
7575
              test_add_inotify_dir_watch_fail);
8106
 
  test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory",
8107
 
              test_add_inotify_dir_watch_nondir);
8108
7576
  test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
8109
7577
              test_add_inotify_dir_watch_EAGAIN);
8110
7578
  test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
8111
7579
              test_add_inotify_dir_watch_IN_CLOSE_WRITE);
8112
7580
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
8113
7581
              test_add_inotify_dir_watch_IN_MOVED_TO);
8114
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
8115
 
              test_add_inotify_dir_watch_IN_MOVED_FROM);
8116
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
8117
 
              test_add_inotify_dir_watch_IN_EXCL_UNLINK);
8118
7582
  test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
8119
7583
              test_add_inotify_dir_watch_IN_DELETE);
8120
7584
  test_add_st("/task/read_inotify_event/readerror",
8129
7593
              test_read_inotify_event_IN_CLOSE_WRITE);
8130
7594
  test_add_st("/task/read_inotify_event/IN_MOVED_TO",
8131
7595
              test_read_inotify_event_IN_MOVED_TO);
8132
 
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
8133
 
              test_read_inotify_event_IN_MOVED_FROM);
8134
7596
  test_add_st("/task/read_inotify_event/IN_DELETE",
8135
7597
              test_read_inotify_event_IN_DELETE);
8136
7598
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
8137
7599
              test_read_inotify_event_IN_CLOSE_WRITE_badname);
8138
7600
  test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
8139
7601
              test_read_inotify_event_IN_MOVED_TO_badname);
8140
 
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
8141
 
              test_read_inotify_event_IN_MOVED_FROM_badname);
8142
7602
  test_add_st("/task/read_inotify_event/IN_DELETE/badname",
8143
7603
              test_read_inotify_event_IN_DELETE_badname);
8144
7604
  test_add_st("/task/open_and_parse_question/ENOENT",
8241
7701
  g_option_context_set_help_enabled(context, FALSE);
8242
7702
  g_option_context_set_ignore_unknown_options(context, TRUE);
8243
7703
 
8244
 
  gboolean should_run_tests = FALSE;
 
7704
  gboolean run_tests = FALSE;
8245
7705
  GOptionEntry entries[] = {
8246
7706
    { "test", 0, 0, G_OPTION_ARG_NONE,
8247
 
      &should_run_tests, "Run tests", NULL },
 
7707
      &run_tests, "Run tests", NULL },
8248
7708
    { NULL }
8249
7709
  };
8250
7710
  g_option_context_add_main_entries(context, entries, NULL);
8257
7717
  }
8258
7718
 
8259
7719
  g_option_context_free(context);
8260
 
  return should_run_tests != FALSE;
 
7720
  return run_tests != FALSE;
8261
7721
}
8262
 
 
8263
 
/*
8264
 
Local Variables:
8265
 
run-tests:
8266
 
(lambda ()
8267
 
  (if (not (funcall run-tests-in-test-buffer default-directory))
8268
 
      (funcall show-test-buffer-in-test-window)
8269
 
    (funcall remove-test-window)))
8270
 
run-tests-in-test-buffer:
8271
 
(lambda (dir)
8272
 
  (with-current-buffer (get-buffer-create "*Test*")
8273
 
    (setq buffer-read-only nil
8274
 
          default-directory dir)
8275
 
    (erase-buffer)
8276
 
    (compilation-mode))
8277
 
  (let ((process-result
8278
 
         (let ((inhibit-read-only t))
8279
 
           (process-file-shell-command
8280
 
            (funcall get-command-line) nil "*Test*"))))
8281
 
    (and (numberp process-result)
8282
 
         (= process-result 0))))
8283
 
get-command-line:
8284
 
(lambda ()
8285
 
  (let*
8286
 
      ((build-directory
8287
 
        (funcall find-build-directory (buffer-file-name)))
8288
 
       (local-build-directory
8289
 
        (if (fboundp 'file-local-name)
8290
 
            (file-local-name build-directory)
8291
 
          (or (file-remote-p build-directory 'localname)
8292
 
              build-directory)))
8293
 
       (command
8294
 
        (file-relative-name (file-name-sans-extension
8295
 
                             (buffer-file-name)) build-directory))
8296
 
       (qbdir (shell-quote-argument local-build-directory))
8297
 
       (qcmd (shell-quote-argument command)))
8298
 
    (format (concat "cd %s && CFLAGS=-Werror make --silent %s"
8299
 
             " && %s --test --verbose") qbdir qcmd qcmd)))
8300
 
find-build-directory:
8301
 
(lambda (try-directory &optional base-directory)
8302
 
  (let ((base-directory (or base-directory try-directory)))
8303
 
    (cond ((equal try-directory "/") base-directory)
8304
 
          ((file-readable-p
8305
 
            (concat (file-name-as-directory try-directory)
8306
 
                    "Makefile")) try-directory)
8307
 
          ((funcall find-build-directory
8308
 
                    (directory-file-name (file-name-directory
8309
 
                                          try-directory))
8310
 
                    base-directory)))))
8311
 
show-test-buffer-in-test-window:
8312
 
(lambda ()
8313
 
  (when (not (get-buffer-window-list "*Test*"))
8314
 
    (setq next-error-last-buffer (get-buffer "*Test*"))
8315
 
    (let* ((side (if (>= (window-width) 146) 'right 'bottom))
8316
 
           (display-buffer-overriding-action
8317
 
            `((display-buffer-in-side-window) (side . ,side)
8318
 
              (window-height . fit-window-to-buffer)
8319
 
              (window-width . fit-window-to-buffer))))
8320
 
      (display-buffer "*Test*"))))
8321
 
remove-test-window:
8322
 
(lambda ()
8323
 
  (let ((test-window (get-buffer-window "*Test*")))
8324
 
    (if test-window (delete-window test-window))))
8325
 
eval: (add-hook 'after-save-hook run-tests 90 t)
8326
 
End:
8327
 
*/