/mandos/release

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

« back to all changes in this revision

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

  • Committer: Teddy Hogeborn
  • Date: 2019-07-29 16:35:53 UTC
  • mto: This revision was merged to the branch mainline in revision 384.
  • 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
1078
  const ssize_t read_length = read(fd, ievent, ievent_size);
1102
1079
  if(read_length == 0){ /* EOF */
1140
1117
             immediately */
1141
1118
          queue->next_run = 1;
1142
1119
        }
1143
 
      } else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
 
1120
      } else if(ievent->mask & IN_DELETE){
1144
1121
        if(not string_set_add(cancelled_filenames,
1145
1122
                              question_filename)){
1146
1123
          error(0, errno, "Could not add question %s to"
1196
1173
  bool *const password_is_read = task.password_is_read;
1197
1174
 
1198
1175
  /* We use the GLib "Key-value file parser" functions to parse the
1199
 
     question file.  See <https://systemd.io/PASSWORD_AGENTS/> for
1200
 
     specification of contents */
 
1176
     question file.  See <https://www.freedesktop.org/wiki/Software
 
1177
     /systemd/PasswordAgents/> for specification of contents */
1201
1178
  __attribute__((nonnull))
1202
1179
    void cleanup_g_key_file(GKeyFile **key_file){
1203
1180
    if(*key_file != NULL){
1493
1470
         not. You may but don't have to include a final NUL byte in
1494
1471
         your message.
1495
1472
 
1496
 
         — <https://systemd.io/PASSWORD_AGENTS/> (Tue, 15 Sep 2020
1497
 
         14:24:20 GMT)
 
1473
         — <https://www.freedesktop.org/wiki/Software/systemd/
 
1474
         PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
1498
1475
      */
1499
1476
      send_buffer[0] = '+';     /* Prefix with "+" */
1500
1477
      /* Always add an extra NUL */
1505
1482
      errno = 0;
1506
1483
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1507
1484
                           MSG_NOSIGNAL);
1508
 
      const error_t saved_errno = (ssret < 0) ? errno : 0;
 
1485
      const error_t saved_errno = errno;
1509
1486
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1510
1487
      explicit_bzero(send_buffer, send_buffer_length);
1511
1488
#else
1529
1506
          /* Retry, below */
1530
1507
          break;
1531
1508
        case EMSGSIZE:
1532
 
          error(0, saved_errno, "Password of size %" PRIuMAX
1533
 
                " is too big", (uintmax_t)password->length);
 
1509
          error(0, 0, "Password of size %" PRIuMAX " is too big",
 
1510
                (uintmax_t)password->length);
1534
1511
#if __GNUC__ < 7
1535
1512
          /* FALLTHROUGH */
1536
1513
#else
1538
1515
#endif
1539
1516
        case 0:
1540
1517
          if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1541
 
            error(0, 0, "Password only partially sent to socket %s: %"
1542
 
                  PRIuMAX " out of %" PRIuMAX " bytes sent", filename,
1543
 
                  (uintmax_t)ssret, (uintmax_t)send_buffer_length);
 
1518
            error(0, 0, "Password only partially sent to socket");
1544
1519
          }
1545
1520
#if __GNUC__ < 7
1546
1521
          /* FALLTHROUGH */
1902
1877
  g_assert_true(queue->tasks[0].func == dummy_func);
1903
1878
}
1904
1879
 
1905
 
static void test_add_to_queue_overflow(__attribute__((unused))
1906
 
                                       test_fixture *fixture,
1907
 
                                       __attribute__((unused))
1908
 
                                       gconstpointer user_data){
1909
 
  __attribute__((cleanup(cleanup_queue)))
1910
 
    task_queue *queue = create_queue();
1911
 
  g_assert_nonnull(queue);
1912
 
  g_assert_true(queue->length == 0);
1913
 
  queue->length = SIZE_MAX / sizeof(task_context); /* fake max size */
1914
 
 
1915
 
  FILE *real_stderr = stderr;
1916
 
  FILE *devnull = fopen("/dev/null", "we");
1917
 
  g_assert_nonnull(devnull);
1918
 
  stderr = devnull;
1919
 
  const bool ret = add_to_queue(queue,
1920
 
                                (task_context){ .func=dummy_func });
1921
 
  g_assert_true(errno == ENOMEM);
1922
 
  g_assert_false(ret);
1923
 
  stderr = real_stderr;
1924
 
  g_assert_cmpint(fclose(devnull), ==, 0);
1925
 
  queue->length = 0;            /* Restore real size */
1926
 
}
1927
 
 
1928
1880
static void dummy_func(__attribute__((unused))
1929
1881
                       const task_context task,
1930
1882
                       __attribute__((unused))
2201
2153
    }
2202
2154
    exit(EXIT_SUCCESS);
2203
2155
  }
2204
 
  if(pid == -1){
2205
 
    error(EXIT_FAILURE, errno, "Failed to fork()");
2206
 
  }
2207
 
 
2208
2156
  int status;
2209
2157
  waitpid(pid, &status, 0);
2210
2158
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2272
2220
 
2273
2221
  {
2274
2222
    __attribute__((cleanup(cleanup_close)))
2275
 
      const int devnull_fd = open("/dev/null",
2276
 
                                  O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2223
      const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
2277
2224
    g_assert_cmpint(devnull_fd, >=, 0);
2278
2225
    __attribute__((cleanup(cleanup_close)))
2279
2226
      const int real_stderr_fd = dup(STDERR_FILENO);
2303
2250
    {
2304
2251
      __attribute__((cleanup(cleanup_close)))
2305
2252
        const int devnull_fd = open("/dev/null",
2306
 
                                    O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2253
                                    O_WRONLY | O_CLOEXEC);
2307
2254
      g_assert_cmpint(devnull_fd, >=, 0);
2308
2255
      __attribute__((cleanup(cleanup_close)))
2309
2256
        const int real_stderr_fd = dup(STDERR_FILENO);
2954
2901
 
2955
2902
  __attribute__((cleanup(cleanup_close)))
2956
2903
    const int devnull_fd = open("/dev/null",
2957
 
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2904
                                O_WRONLY | O_CLOEXEC);
2958
2905
  g_assert_cmpint(devnull_fd, >=, 0);
2959
2906
  __attribute__((cleanup(cleanup_close)))
2960
2907
    const int real_stderr_fd = dup(STDERR_FILENO);
3025
2972
 
3026
2973
  __attribute__((cleanup(cleanup_close)))
3027
2974
    const int devnull_fd = open("/dev/null",
3028
 
                                O_WRONLY | O_CLOEXEC, O_NOCTTY);
 
2975
                                O_WRONLY | O_CLOEXEC);
3029
2976
  g_assert_cmpint(devnull_fd, >=, 0);
3030
2977
  __attribute__((cleanup(cleanup_close)))
3031
2978
    const int real_stderr_fd = dup(STDERR_FILENO);
3069
3016
    buffer password = {};
3070
3017
 
3071
3018
  /* Reading /proc/self/mem from offset 0 will always give EIO */
3072
 
  const int fd = open("/proc/self/mem",
3073
 
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
 
3019
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
3074
3020
 
3075
3021
  bool password_is_read = false;
3076
3022
  bool quit_now = false;
3504
3450
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3505
3451
}
3506
3452
 
3507
 
static void test_add_inotify_dir_watch_nondir(__attribute__((unused))
3508
 
                                              test_fixture *fixture,
3509
 
                                            __attribute__((unused))
3510
 
                                              gconstpointer
3511
 
                                              user_data){
3512
 
  __attribute__((cleanup(cleanup_close)))
3513
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3514
 
  g_assert_cmpint(epoll_fd, >=, 0);
3515
 
  __attribute__((cleanup(cleanup_queue)))
3516
 
    task_queue *queue = create_queue();
3517
 
  g_assert_nonnull(queue);
3518
 
  __attribute__((cleanup(string_set_clear)))
3519
 
    string_set cancelled_filenames = {};
3520
 
  const mono_microsecs current_time = 0;
3521
 
 
3522
 
  bool quit_now = false;
3523
 
  buffer password = {};
3524
 
  bool mandos_client_exited = false;
3525
 
  bool password_is_read = false;
3526
 
 
3527
 
  const char not_a_directory[] = "/dev/tty";
3528
 
 
3529
 
  FILE *real_stderr = stderr;
3530
 
  FILE *devnull = fopen("/dev/null", "we");
3531
 
  g_assert_nonnull(devnull);
3532
 
  stderr = devnull;
3533
 
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3534
 
                                       &password, not_a_directory,
3535
 
                                       &cancelled_filenames,
3536
 
                                       &current_time,
3537
 
                                       &mandos_client_exited,
3538
 
                                       &password_is_read));
3539
 
  stderr = real_stderr;
3540
 
  g_assert_cmpint(fclose(devnull), ==, 0);
3541
 
 
3542
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3543
 
}
3544
 
 
3545
3453
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3546
3454
                                              test_fixture *fixture,
3547
3455
                                              __attribute__((unused))
3762
3670
}
3763
3671
 
3764
3672
static
3765
 
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
3766
 
                                              test_fixture *fixture,
3767
 
                                              __attribute__((unused))
3768
 
                                              gconstpointer
3769
 
                                              user_data){
3770
 
  __attribute__((cleanup(cleanup_close)))
3771
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3772
 
  g_assert_cmpint(epoll_fd, >=, 0);
3773
 
  __attribute__((cleanup(cleanup_queue)))
3774
 
    task_queue *queue = create_queue();
3775
 
  g_assert_nonnull(queue);
3776
 
  __attribute__((cleanup(string_set_clear)))
3777
 
    string_set cancelled_filenames = {};
3778
 
  const mono_microsecs current_time = 0;
3779
 
 
3780
 
  bool quit_now = false;
3781
 
  buffer password = {};
3782
 
  bool mandos_client_exited = false;
3783
 
  bool password_is_read = false;
3784
 
 
3785
 
  __attribute__((cleanup(cleanup_string)))
3786
 
    char *tempdir = make_temporary_directory();
3787
 
  g_assert_nonnull(tempdir);
3788
 
 
3789
 
  __attribute__((cleanup(cleanup_string)))
3790
 
    char *tempfilename = make_temporary_file_in_directory(tempdir);
3791
 
  g_assert_nonnull(tempfilename);
3792
 
 
3793
 
  __attribute__((cleanup(cleanup_string)))
3794
 
    char *targetdir = make_temporary_directory();
3795
 
  g_assert_nonnull(targetdir);
3796
 
 
3797
 
  __attribute__((cleanup(cleanup_string)))
3798
 
    char *targetfilename = NULL;
3799
 
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
3800
 
                           basename(tempfilename)), >, 0);
3801
 
  g_assert_nonnull(targetfilename);
3802
 
 
3803
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3804
 
                                      &password, tempdir,
3805
 
                                      &cancelled_filenames,
3806
 
                                      &current_time,
3807
 
                                      &mandos_client_exited,
3808
 
                                      &password_is_read));
3809
 
 
3810
 
  g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
3811
 
 
3812
 
  const task_context *const added_read_task
3813
 
    = find_matching_task(queue,
3814
 
                         (task_context){ .func=read_inotify_event });
3815
 
  g_assert_nonnull(added_read_task);
3816
 
 
3817
 
  /* "sufficient to read at least one event." - inotify(7) */
3818
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3819
 
                              + NAME_MAX + 1);
3820
 
  struct inotify_event *ievent = malloc(ievent_size);
3821
 
  g_assert_nonnull(ievent);
3822
 
 
3823
 
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3824
 
 
3825
 
  g_assert_cmpint((int)read_size, >, 0);
3826
 
  g_assert_true(ievent->mask & IN_MOVED_FROM);
3827
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
3828
 
 
3829
 
  free(ievent);
3830
 
 
3831
 
  g_assert_cmpint(unlink(targetfilename), ==, 0);
3832
 
  g_assert_cmpint(rmdir(targetdir), ==, 0);
3833
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3834
 
}
3835
 
 
3836
 
static
3837
3673
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3838
3674
                                          test_fixture *fixture,
3839
3675
                                          __attribute__((unused))
3897
3733
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3898
3734
}
3899
3735
 
3900
 
static
3901
 
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
3902
 
                                               test_fixture *fixture,
3903
 
                                               __attribute__((unused))
3904
 
                                               gconstpointer
3905
 
                                               user_data){
3906
 
  __attribute__((cleanup(cleanup_close)))
3907
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3908
 
  g_assert_cmpint(epoll_fd, >=, 0);
3909
 
  __attribute__((cleanup(cleanup_queue)))
3910
 
    task_queue *queue = create_queue();
3911
 
  g_assert_nonnull(queue);
3912
 
  __attribute__((cleanup(string_set_clear)))
3913
 
    string_set cancelled_filenames = {};
3914
 
  const mono_microsecs current_time = 0;
3915
 
 
3916
 
  bool quit_now = false;
3917
 
  buffer password = {};
3918
 
  bool mandos_client_exited = false;
3919
 
  bool password_is_read = false;
3920
 
 
3921
 
  __attribute__((cleanup(cleanup_string)))
3922
 
    char *tempdir = make_temporary_directory();
3923
 
  g_assert_nonnull(tempdir);
3924
 
 
3925
 
  __attribute__((cleanup(cleanup_string)))
3926
 
    char *tempfile = make_temporary_file_in_directory(tempdir);
3927
 
  g_assert_nonnull(tempfile);
3928
 
  int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
3929
 
                         | O_NOFOLLOW);
3930
 
  g_assert_cmpint(tempfile_fd, >, 2);
3931
 
 
3932
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3933
 
                                      &password, tempdir,
3934
 
                                      &cancelled_filenames,
3935
 
                                      &current_time,
3936
 
                                      &mandos_client_exited,
3937
 
                                      &password_is_read));
3938
 
  g_assert_cmpint(unlink(tempfile), ==, 0);
3939
 
 
3940
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
3941
 
 
3942
 
  const task_context *const added_read_task
3943
 
    = find_matching_task(queue,
3944
 
                         (task_context){ .func=read_inotify_event });
3945
 
  g_assert_nonnull(added_read_task);
3946
 
 
3947
 
  g_assert_cmpint(added_read_task->fd, >, 2);
3948
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3949
 
 
3950
 
  /* "sufficient to read at least one event." - inotify(7) */
3951
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3952
 
                              + NAME_MAX + 1);
3953
 
  struct inotify_event *ievent = malloc(ievent_size);
3954
 
  g_assert_nonnull(ievent);
3955
 
 
3956
 
  ssize_t read_size = 0;
3957
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
3958
 
 
3959
 
  g_assert_cmpint((int)read_size, >, 0);
3960
 
  g_assert_true(ievent->mask & IN_DELETE);
3961
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3962
 
 
3963
 
  g_assert_cmpint(close(tempfile_fd), ==, 0);
3964
 
 
3965
 
  /* IN_EXCL_UNLINK should make the closing of the previously unlinked
3966
 
     file not appear as an ievent, so we should not see it now. */
3967
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
3968
 
  g_assert_cmpint((int)read_size, ==, -1);
3969
 
  g_assert_true(errno == EAGAIN);
3970
 
 
3971
 
  free(ievent);
3972
 
 
3973
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3974
 
}
3975
 
 
3976
3736
static void test_read_inotify_event_readerror(__attribute__((unused))
3977
3737
                                              test_fixture *fixture,
3978
3738
                                              __attribute__((unused))
3984
3744
  const mono_microsecs current_time = 0;
3985
3745
 
3986
3746
  /* Reading /proc/self/mem from offset 0 will always result in EIO */
3987
 
  const int fd = open("/proc/self/mem",
3988
 
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
 
3747
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
3989
3748
 
3990
3749
  bool quit_now = false;
3991
3750
  __attribute__((cleanup(cleanup_queue)))
4170
3929
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4171
3930
                                  + NAME_MAX + 1);
4172
3931
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4173
 
  struct {
4174
 
    struct inotify_event event;
4175
 
    char name_buffer[NAME_MAX + 1];
4176
 
  } ievent_buffer;
4177
 
  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);
4178
3935
 
4179
3936
  const char dummy_file_name[] = "ask.dummy_file_name";
4180
3937
  ievent->mask = IN_CLOSE_WRITE;
4182
3939
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4183
3940
  const size_t ievent_size = (sizeof(struct inotify_event)
4184
3941
                              + sizeof(dummy_file_name));
4185
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
3942
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
4186
3943
                  ==, ievent_size);
4187
3944
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4188
3945
 
4265
4022
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4266
4023
                                  + NAME_MAX + 1);
4267
4024
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4268
 
  struct {
4269
 
    struct inotify_event event;
4270
 
    char name_buffer[NAME_MAX + 1];
4271
 
  } ievent_buffer;
4272
 
  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);
4273
4028
 
4274
4029
  const char dummy_file_name[] = "ask.dummy_file_name";
4275
4030
  ievent->mask = IN_MOVED_TO;
4277
4032
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4278
4033
  const size_t ievent_size = (sizeof(struct inotify_event)
4279
4034
                              + sizeof(dummy_file_name));
4280
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4035
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
4281
4036
                  ==, ievent_size);
4282
4037
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4283
4038
 
4343
4098
      }));
4344
4099
}
4345
4100
 
4346
 
static
4347
 
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
4348
 
                                           test_fixture *fixture,
4349
 
                                           __attribute__((unused))
4350
 
                                           gconstpointer user_data){
4351
 
  __attribute__((cleanup(cleanup_close)))
4352
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4353
 
  g_assert_cmpint(epoll_fd, >=, 0);
4354
 
  __attribute__((cleanup(string_set_clear)))
4355
 
    string_set cancelled_filenames = {};
4356
 
  const mono_microsecs current_time = 0;
4357
 
 
4358
 
  int pipefds[2];
4359
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4360
 
 
4361
 
  /* "sufficient to read at least one event." - inotify(7) */
4362
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4363
 
                                  + NAME_MAX + 1);
4364
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4365
 
  struct {
4366
 
    struct inotify_event event;
4367
 
    char name_buffer[NAME_MAX + 1];
4368
 
  } ievent_buffer;
4369
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4370
 
 
4371
 
  const char dummy_file_name[] = "ask.dummy_file_name";
4372
 
  ievent->mask = IN_MOVED_FROM;
4373
 
  ievent->len = sizeof(dummy_file_name);
4374
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4375
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4376
 
                              + sizeof(dummy_file_name));
4377
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4378
 
                  ==, ievent_size);
4379
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4380
 
 
4381
 
  bool quit_now = false;
4382
 
  buffer password = {};
4383
 
  bool mandos_client_exited = false;
4384
 
  bool password_is_read = false;
4385
 
  __attribute__((cleanup(cleanup_queue)))
4386
 
    task_queue *queue = create_queue();
4387
 
  g_assert_nonnull(queue);
4388
 
 
4389
 
  task_context task = {
4390
 
    .func=read_inotify_event,
4391
 
    .epoll_fd=epoll_fd,
4392
 
    .fd=pipefds[0],
4393
 
    .quit_now=&quit_now,
4394
 
    .password=&password,
4395
 
    .filename=strdup("/nonexistent"),
4396
 
    .cancelled_filenames=&cancelled_filenames,
4397
 
    .current_time=&current_time,
4398
 
    .mandos_client_exited=&mandos_client_exited,
4399
 
    .password_is_read=&password_is_read,
4400
 
  };
4401
 
  task.func(task, queue);
4402
 
  g_assert_false(quit_now);
4403
 
  g_assert_true(queue->next_run == 0);
4404
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4405
 
 
4406
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4407
 
        .func=read_inotify_event,
4408
 
        .epoll_fd=epoll_fd,
4409
 
        .fd=pipefds[0],
4410
 
        .quit_now=&quit_now,
4411
 
        .password=&password,
4412
 
        .filename=task.filename,
4413
 
        .cancelled_filenames=&cancelled_filenames,
4414
 
        .current_time=&current_time,
4415
 
        .mandos_client_exited=&mandos_client_exited,
4416
 
        .password_is_read=&password_is_read,
4417
 
      }));
4418
 
 
4419
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4420
 
                                   EPOLLIN | EPOLLRDHUP));
4421
 
 
4422
 
  __attribute__((cleanup(cleanup_string)))
4423
 
    char *filename = NULL;
4424
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4425
 
                           dummy_file_name), >, 0);
4426
 
  g_assert_nonnull(filename);
4427
 
  g_assert_true(string_set_contains(*task.cancelled_filenames,
4428
 
                                    filename));
4429
 
}
4430
 
 
4431
4101
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4432
4102
                                              test_fixture *fixture,
4433
4103
                                              __attribute__((unused))
4447
4117
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4448
4118
                                  + NAME_MAX + 1);
4449
4119
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4450
 
  struct {
4451
 
    struct inotify_event event;
4452
 
    char name_buffer[NAME_MAX + 1];
4453
 
  } ievent_buffer;
4454
 
  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);
4455
4123
 
4456
4124
  const char dummy_file_name[] = "ask.dummy_file_name";
4457
4125
  ievent->mask = IN_DELETE;
4459
4127
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4460
4128
  const size_t ievent_size = (sizeof(struct inotify_event)
4461
4129
                              + sizeof(dummy_file_name));
4462
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4130
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
4463
4131
                  ==, ievent_size);
4464
4132
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4465
4133
 
4531
4199
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4532
4200
                                  + NAME_MAX + 1);
4533
4201
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4534
 
  struct {
4535
 
    struct inotify_event event;
4536
 
    char name_buffer[NAME_MAX + 1];
4537
 
  } ievent_buffer;
4538
 
  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);
4539
4205
 
4540
4206
  const char dummy_file_name[] = "ignored.dummy_file_name";
4541
4207
  ievent->mask = IN_CLOSE_WRITE;
4543
4209
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4544
4210
  const size_t ievent_size = (sizeof(struct inotify_event)
4545
4211
                              + sizeof(dummy_file_name));
4546
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4212
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
4547
4213
                  ==, ievent_size);
4548
4214
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4549
4215
 
4607
4273
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4608
4274
                                  + NAME_MAX + 1);
4609
4275
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4610
 
  struct {
4611
 
    struct inotify_event event;
4612
 
    char name_buffer[NAME_MAX + 1];
4613
 
  } ievent_buffer;
4614
 
  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);
4615
4279
 
4616
4280
  const char dummy_file_name[] = "ignored.dummy_file_name";
4617
4281
  ievent->mask = IN_MOVED_TO;
4619
4283
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4620
4284
  const size_t ievent_size = (sizeof(struct inotify_event)
4621
4285
                              + sizeof(dummy_file_name));
4622
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4286
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
4623
4287
                  ==, ievent_size);
4624
4288
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4625
4289
 
4666
4330
                                   EPOLLIN | EPOLLRDHUP));
4667
4331
}
4668
4332
 
4669
 
static void
4670
 
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
4671
 
                                              test_fixture *fixture,
4672
 
                                              __attribute__((unused))
4673
 
                                              gconstpointer
4674
 
                                              user_data){
4675
 
  __attribute__((cleanup(cleanup_close)))
4676
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4677
 
  g_assert_cmpint(epoll_fd, >=, 0);
4678
 
  __attribute__((cleanup(string_set_clear)))
4679
 
    string_set cancelled_filenames = {};
4680
 
  const mono_microsecs current_time = 0;
4681
 
 
4682
 
  int pipefds[2];
4683
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4684
 
 
4685
 
  /* "sufficient to read at least one event." - inotify(7) */
4686
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4687
 
                                  + NAME_MAX + 1);
4688
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4689
 
  struct {
4690
 
    struct inotify_event event;
4691
 
    char name_buffer[NAME_MAX + 1];
4692
 
  } ievent_buffer;
4693
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4694
 
 
4695
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
4696
 
  ievent->mask = IN_MOVED_FROM;
4697
 
  ievent->len = sizeof(dummy_file_name);
4698
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4699
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4700
 
                              + sizeof(dummy_file_name));
4701
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4702
 
                  ==, ievent_size);
4703
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4704
 
 
4705
 
  bool quit_now = false;
4706
 
  buffer password = {};
4707
 
  bool mandos_client_exited = false;
4708
 
  bool password_is_read = false;
4709
 
  __attribute__((cleanup(cleanup_queue)))
4710
 
    task_queue *queue = create_queue();
4711
 
  g_assert_nonnull(queue);
4712
 
 
4713
 
  task_context task = {
4714
 
    .func=read_inotify_event,
4715
 
    .epoll_fd=epoll_fd,
4716
 
    .fd=pipefds[0],
4717
 
    .quit_now=&quit_now,
4718
 
    .password=&password,
4719
 
    .filename=strdup("/nonexistent"),
4720
 
    .cancelled_filenames=&cancelled_filenames,
4721
 
    .current_time=&current_time,
4722
 
    .mandos_client_exited=&mandos_client_exited,
4723
 
    .password_is_read=&password_is_read,
4724
 
  };
4725
 
  task.func(task, queue);
4726
 
  g_assert_false(quit_now);
4727
 
  g_assert_true(queue->next_run == 0);
4728
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4729
 
 
4730
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4731
 
        .func=read_inotify_event,
4732
 
        .epoll_fd=epoll_fd,
4733
 
        .fd=pipefds[0],
4734
 
        .quit_now=&quit_now,
4735
 
        .password=&password,
4736
 
        .filename=task.filename,
4737
 
        .cancelled_filenames=&cancelled_filenames,
4738
 
        .current_time=&current_time,
4739
 
        .mandos_client_exited=&mandos_client_exited,
4740
 
        .password_is_read=&password_is_read,
4741
 
      }));
4742
 
 
4743
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4744
 
                                   EPOLLIN | EPOLLRDHUP));
4745
 
 
4746
 
  __attribute__((cleanup(cleanup_string)))
4747
 
    char *filename = NULL;
4748
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4749
 
                           dummy_file_name), >, 0);
4750
 
  g_assert_nonnull(filename);
4751
 
  g_assert_false(string_set_contains(cancelled_filenames, filename));
4752
 
}
4753
 
 
4754
4333
static
4755
4334
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4756
4335
                                               test_fixture *fixture,
4771
4350
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4772
4351
                                  + NAME_MAX + 1);
4773
4352
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4774
 
  struct {
4775
 
    struct inotify_event event;
4776
 
    char name_buffer[NAME_MAX + 1];
4777
 
  } ievent_buffer;
4778
 
  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);
4779
4356
 
4780
4357
  const char dummy_file_name[] = "ignored.dummy_file_name";
4781
4358
  ievent->mask = IN_DELETE;
4783
4360
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4784
4361
  const size_t ievent_size = (sizeof(struct inotify_event)
4785
4362
                              + sizeof(dummy_file_name));
4786
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4363
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
4787
4364
                  ==, ievent_size);
4788
4365
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4789
4366
 
5676
5253
                                            __attribute__((unused))
5677
5254
                                            gconstpointer user_data){
5678
5255
  __attribute__((cleanup(cleanup_close)))
5679
 
    const int epoll_fd = open("/dev/null",
5680
 
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
5256
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
5681
5257
  __attribute__((cleanup(cleanup_string)))
5682
5258
    char *const question_filename = strdup("/nonexistent/question");
5683
5259
  g_assert_nonnull(question_filename);
5812
5388
  char write_data[PIPE_BUF];
5813
5389
  {
5814
5390
    /* Construct test password buffer */
5815
 
    /* Start with + since that is what the real protocol uses */
 
5391
    /* Start with + since that is what the real procotol uses */
5816
5392
    write_data[0] = '+';
5817
5393
    /* Set a special character at string end just to mark the end */
5818
5394
    write_data[sizeof(write_data)-2] = 'y';
5970
5546
  char *const filename = strdup("/nonexistent/socket");
5971
5547
  __attribute__((cleanup(string_set_clear)))
5972
5548
    string_set cancelled_filenames = {};
5973
 
  int socketfds[2];
5974
 
 
5975
 
  /* Find a message size which triggers EMSGSIZE */
5976
 
  __attribute__((cleanup(cleanup_string)))
5977
 
    char *message_buffer = NULL;
5978
 
  size_t message_size = PIPE_BUF + 1;
5979
 
  for(ssize_t ssret = 0; ssret >= 0; message_size += 1024){
5980
 
    if(message_size >= 1024*1024*1024){ /* 1 GiB */
5981
 
      g_test_skip("Skipping EMSGSIZE test: Will not try 1GiB");
5982
 
      return;
5983
 
    }
5984
 
    message_buffer = realloc(message_buffer, message_size);
5985
 
    if(message_buffer == NULL){
5986
 
      g_test_skip("Skipping EMSGSIZE test");
5987
 
      g_test_message("Failed to malloc() %" PRIuMAX " bytes",
5988
 
                     (uintmax_t)message_size);
5989
 
      return;
5990
 
    }
5991
 
    /* Fill buffer with 'x' */
5992
 
    memset(message_buffer, 'x', message_size);
5993
 
    /* Create a new socketpair for each message size to avoid having
5994
 
       to empty the pipe by reading the message to a separate buffer
5995
 
    */
5996
 
    g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5997
 
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5998
 
                               socketfds), ==, 0);
5999
 
    ssret = send(socketfds[1], message_buffer, message_size,
6000
 
                 MSG_NOSIGNAL);
6001
 
    error_t saved_errno = errno;
6002
 
    g_assert_cmpint(close(socketfds[0]), ==, 0);
6003
 
    g_assert_cmpint(close(socketfds[1]), ==, 0);
6004
 
 
6005
 
    if(ssret < 0){
6006
 
      if(saved_errno != EMSGSIZE) {
6007
 
        g_test_skip("Skipping EMSGSIZE test");
6008
 
        g_test_message("Error on send(%" PRIuMAX " bytes): %s",
6009
 
                       (uintmax_t)message_size,
6010
 
                       strerror(saved_errno));
6011
 
        return;
6012
 
      }
6013
 
      break;
6014
 
    } else if(ssret != (ssize_t)message_size){
6015
 
      g_test_skip("Skipping EMSGSIZE test");
6016
 
      g_test_message("Partial send(): %" PRIuMAX " of %" PRIdMAX
6017
 
                     " bytes", (uintmax_t)ssret,
6018
 
                     (intmax_t)message_size);
6019
 
      return;
6020
 
    }
6021
 
  }
6022
 
  g_test_message("EMSGSIZE triggered by %" PRIdMAX " bytes",
6023
 
                 (intmax_t)message_size);
6024
 
 
6025
 
  buffer password = {
6026
 
    .data=message_buffer,
6027
 
    .length=message_size - 2,   /* Compensate for added '+' and NUL */
6028
 
    .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,
6029
5555
  };
 
5556
  g_assert_nonnull(password.data);
6030
5557
  if(mlock(password.data, password.allocated) != 0){
6031
5558
    g_assert_true(errno == EPERM or errno == ENOMEM);
6032
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);
6033
5569
 
6034
5570
  __attribute__((cleanup(cleanup_queue)))
6035
5571
    task_queue *queue = create_queue();
6036
5572
  g_assert_nonnull(queue);
 
5573
  int socketfds[2];
6037
5574
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6038
5575
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6039
5576
                             socketfds), ==, 0);
6126
5663
                                            __attribute__((unused))
6127
5664
                                            gconstpointer user_data){
6128
5665
  __attribute__((cleanup(cleanup_close)))
6129
 
    const int epoll_fd = open("/dev/null",
6130
 
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
5666
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
6131
5667
  __attribute__((cleanup(cleanup_string)))
6132
5668
    char *const question_filename = strdup("/nonexistent/question");
6133
5669
  g_assert_nonnull(question_filename);
6396
5932
                                              const char *const
6397
5933
                                              dirname){
6398
5934
  __attribute__((cleanup(cleanup_close)))
6399
 
    const int devnull_fd = open("/dev/null",
6400
 
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
5935
    const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
6401
5936
  g_assert_cmpint(devnull_fd, >=, 0);
6402
5937
  __attribute__((cleanup(cleanup_close)))
6403
5938
    const int real_stderr_fd = dup(STDERR_FILENO);
7946
7481
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7947
7482
  test_add("/queue/create", test_create_queue);
7948
7483
  test_add("/queue/add", test_add_to_queue);
7949
 
  test_add("/queue/add/overflow", test_add_to_queue_overflow);
7950
7484
  test_add("/queue/has_question/empty",
7951
7485
           test_queue_has_question_empty);
7952
7486
  test_add("/queue/has_question/false",
8039
7573
              test_add_inotify_dir_watch);
8040
7574
  test_add_st("/task-creators/add_inotify_dir_watch/fail",
8041
7575
              test_add_inotify_dir_watch_fail);
8042
 
  test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory",
8043
 
              test_add_inotify_dir_watch_nondir);
8044
7576
  test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
8045
7577
              test_add_inotify_dir_watch_EAGAIN);
8046
7578
  test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
8047
7579
              test_add_inotify_dir_watch_IN_CLOSE_WRITE);
8048
7580
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
8049
7581
              test_add_inotify_dir_watch_IN_MOVED_TO);
8050
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
8051
 
              test_add_inotify_dir_watch_IN_MOVED_FROM);
8052
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
8053
 
              test_add_inotify_dir_watch_IN_EXCL_UNLINK);
8054
7582
  test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
8055
7583
              test_add_inotify_dir_watch_IN_DELETE);
8056
7584
  test_add_st("/task/read_inotify_event/readerror",
8065
7593
              test_read_inotify_event_IN_CLOSE_WRITE);
8066
7594
  test_add_st("/task/read_inotify_event/IN_MOVED_TO",
8067
7595
              test_read_inotify_event_IN_MOVED_TO);
8068
 
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
8069
 
              test_read_inotify_event_IN_MOVED_FROM);
8070
7596
  test_add_st("/task/read_inotify_event/IN_DELETE",
8071
7597
              test_read_inotify_event_IN_DELETE);
8072
7598
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
8073
7599
              test_read_inotify_event_IN_CLOSE_WRITE_badname);
8074
7600
  test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
8075
7601
              test_read_inotify_event_IN_MOVED_TO_badname);
8076
 
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
8077
 
              test_read_inotify_event_IN_MOVED_FROM_badname);
8078
7602
  test_add_st("/task/read_inotify_event/IN_DELETE/badname",
8079
7603
              test_read_inotify_event_IN_DELETE_badname);
8080
7604
  test_add_st("/task/open_and_parse_question/ENOENT",
8177
7701
  g_option_context_set_help_enabled(context, FALSE);
8178
7702
  g_option_context_set_ignore_unknown_options(context, TRUE);
8179
7703
 
8180
 
  gboolean should_run_tests = FALSE;
 
7704
  gboolean run_tests = FALSE;
8181
7705
  GOptionEntry entries[] = {
8182
7706
    { "test", 0, 0, G_OPTION_ARG_NONE,
8183
 
      &should_run_tests, "Run tests", NULL },
 
7707
      &run_tests, "Run tests", NULL },
8184
7708
    { NULL }
8185
7709
  };
8186
7710
  g_option_context_add_main_entries(context, entries, NULL);
8193
7717
  }
8194
7718
 
8195
7719
  g_option_context_free(context);
8196
 
  return should_run_tests != FALSE;
 
7720
  return run_tests != FALSE;
8197
7721
}
8198
 
 
8199
 
/*
8200
 
Local Variables:
8201
 
run-tests:
8202
 
(lambda ()
8203
 
  (if (not (funcall run-tests-in-test-buffer default-directory))
8204
 
      (funcall show-test-buffer-in-test-window)
8205
 
    (funcall remove-test-window)))
8206
 
run-tests-in-test-buffer:
8207
 
(lambda (dir)
8208
 
  (with-current-buffer (get-buffer-create "*Test*")
8209
 
    (setq buffer-read-only nil
8210
 
          default-directory dir)
8211
 
    (erase-buffer)
8212
 
    (compilation-mode))
8213
 
  (let ((process-result
8214
 
         (let ((inhibit-read-only t))
8215
 
           (process-file-shell-command
8216
 
            (funcall get-command-line) nil "*Test*"))))
8217
 
    (and (numberp process-result)
8218
 
         (= process-result 0))))
8219
 
get-command-line:
8220
 
(lambda ()
8221
 
  (let*
8222
 
      ((build-directory
8223
 
        (funcall find-build-directory (buffer-file-name)))
8224
 
       (local-build-directory
8225
 
        (if (fboundp 'file-local-name)
8226
 
            (file-local-name build-directory)
8227
 
          (or (file-remote-p build-directory 'localname)
8228
 
              build-directory)))
8229
 
       (command
8230
 
        (file-relative-name (file-name-sans-extension
8231
 
                             (buffer-file-name)) build-directory))
8232
 
       (qbdir (shell-quote-argument local-build-directory))
8233
 
       (qcmd (shell-quote-argument command)))
8234
 
    (format (concat "cd %s && CFLAGS=-Werror make --silent %s"
8235
 
             " && %s --test --verbose") qbdir qcmd qcmd)))
8236
 
find-build-directory:
8237
 
(lambda (try-directory &optional base-directory)
8238
 
  (let ((base-directory (or base-directory try-directory)))
8239
 
    (cond ((equal try-directory "/") base-directory)
8240
 
          ((file-readable-p
8241
 
            (concat (file-name-as-directory try-directory)
8242
 
                    "Makefile")) try-directory)
8243
 
          ((funcall find-build-directory
8244
 
                    (directory-file-name (file-name-directory
8245
 
                                          try-directory))
8246
 
                    base-directory)))))
8247
 
show-test-buffer-in-test-window:
8248
 
(lambda ()
8249
 
  (when (not (get-buffer-window-list "*Test*"))
8250
 
    (setq next-error-last-buffer (get-buffer "*Test*"))
8251
 
    (let* ((side (if (>= (window-width) 146) 'right 'bottom))
8252
 
           (display-buffer-overriding-action
8253
 
            `((display-buffer-in-side-window) (side . ,side)
8254
 
              (window-height . fit-window-to-buffer)
8255
 
              (window-width . fit-window-to-buffer))))
8256
 
      (display-buffer "*Test*"))))
8257
 
remove-test-window:
8258
 
(lambda ()
8259
 
  (let ((test-window (get-buffer-window "*Test*")))
8260
 
    (if test-window (delete-window test-window))))
8261
 
eval: (add-hook 'after-save-hook run-tests 90 t)
8262
 
End:
8263
 
*/