/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: 2021-02-03 23:10:42 UTC
  • Revision ID: teddy@recompile.se-20210203231042-2z3egrvpo1zt7nej
mandos-ctl: Fix bad test for command.Remove and related minor issues

The test for command.Remove removes all clients from the spy server,
and then loops over all clients, looking for the corresponding Remove
command as recorded by the spy server.  But since since there aren't
any clients left after they were removed, no assertions are made, and
the test therefore does nothing.  Fix this.

In tests for command.Approve and command.Deny, add checks that clients
were not somehow removed by the command (in which case, likewise, no
assertions are made).

Add related checks to TestPropertySetterCmd.runTest; i.e. test that a
sequence is not empty before looping over it and making assertions.

* mandos-ctl (TestBaseCommands.test_Remove): Save a copy of the
  original "clients" dict, and loop over those instead.  Add assertion
  that all clients were indeed removed.  Also fix the code which looks
  for the Remove command, which now needs to actually work.
  (TestBaseCommands.test_Approve, TestBaseCommands.test_Deny): Add
  assertion that there are still clients before looping over them.
  (TestPropertySetterCmd.runTest): Add assertion that the list of
  values to get is not empty before looping over them.  Also add check
  that there are still clients before looping over clients.

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
/*
3
3
 * Mandos password agent - Simple password agent to run Mandos client
4
4
 *
5
 
 * Copyright © 2019 Teddy Hogeborn
6
 
 * Copyright © 2019 Björn Påhlsson
 
5
 * Copyright © 2019-2021 Teddy Hogeborn
 
6
 * Copyright © 2019-2021 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
27
 
#include <inttypes.h>           /* uintmax_t, PRIuMAX, PRIdMAX,
28
 
                                   intmax_t, uint32_t, SCNx32,
29
 
                                   SCNuMAX, SCNxMAX */
30
 
#include <stddef.h>             /* size_t */
 
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 */
31
33
#include <sys/types.h>          /* pid_t, uid_t, gid_t, getuid(),
32
34
                                   getpid() */
33
35
#include <stdbool.h>            /* bool, true, false */
40
42
                                   NSIG, sigismember(), SA_ONSTACK,
41
43
                                   SIG_DFL, SIG_IGN, SIGINT, SIGQUIT,
42
44
                                   SIGHUP, SIGSTOP, SIG_UNBLOCK */
 
45
#include <unistd.h>             /* uid_t, gid_t, close(), pipe2(),
 
46
                                   fork(), _exit(), dup2(),
 
47
                                   STDOUT_FILENO, setresgid(),
 
48
                                   setresuid(), execv(), ssize_t,
 
49
                                   read(), dup3(), getuid(), dup(),
 
50
                                   STDERR_FILENO, pause(), write(),
 
51
                                   rmdir(), unlink(), getpid() */
43
52
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE,
44
 
                                   malloc(), free(), strtoumax(),
45
 
                                   realloc(), setenv(), calloc(),
46
 
                                   mkdtemp(), mkostemp() */
 
53
                                   malloc(), free(), realloc(),
 
54
                                   setenv(), calloc(), mkdtemp(),
 
55
                                   mkostemp() */
47
56
#include <iso646.h>             /* not, or, and, xor */
48
57
#include <error.h>              /* error() */
49
58
#include <sysexits.h>           /* EX_USAGE, EX_OSERR, EX_OSFILE */
50
59
#include <errno.h>              /* errno, error_t, EACCES,
51
 
                                   ENAMETOOLONG, ENOENT, EEXIST,
52
 
                                   ECHILD, EPERM, ENOMEM, EAGAIN,
53
 
                                   EINTR, ENOBUFS, EADDRINUSE,
 
60
                                   ENAMETOOLONG, ENOENT, ENOTDIR,
 
61
                                   ENOMEM, EEXIST, ECHILD, EPERM,
 
62
                                   EAGAIN, EINTR, ENOBUFS, EADDRINUSE,
54
63
                                   ECONNREFUSED, ECONNRESET,
55
64
                                   ETOOMANYREFS, EMSGSIZE, EBADF,
56
65
                                   EINVAL */
57
66
#include <string.h>             /* strdup(), memcpy(),
58
67
                                   explicit_bzero(), memset(),
59
68
                                   strcmp(), strlen(), strncpy(),
60
 
                                   memcmp(), basename() */
 
69
                                   memcmp(), basename(), strerror() */
61
70
#include <argz.h>               /* argz_create(), argz_count(),
62
71
                                   argz_extract(), argz_next(),
63
72
                                   argz_add() */
73
82
                                   ARGP_ERR_UNKNOWN, ARGP_KEY_ARGS,
74
83
                                   struct argp, argp_parse(),
75
84
                                   ARGP_NO_EXIT */
76
 
#include <unistd.h>             /* uid_t, gid_t, close(), pipe2(),
77
 
                                   fork(), _exit(), dup2(),
78
 
                                   STDOUT_FILENO, setresgid(),
79
 
                                   setresuid(), execv(), ssize_t,
80
 
                                   read(), dup3(), getuid(), dup(),
81
 
                                   STDERR_FILENO, pause(), write(),
82
 
                                   rmdir(), unlink(), getpid() */
 
85
#include <stdint.h>             /* SIZE_MAX, uint32_t */
83
86
#include <sys/mman.h>           /* munlock(), mlock() */
84
87
#include <fcntl.h>              /* O_CLOEXEC, O_NONBLOCK, fcntl(),
85
88
                                   F_GETFD, F_GETFL, FD_CLOEXEC,
86
 
                                   open(), O_WRONLY, O_RDONLY */
 
89
                                   open(), O_WRONLY, O_NOCTTY,
 
90
                                   O_RDONLY, O_NOFOLLOW */
87
91
#include <sys/wait.h>           /* waitpid(), WNOHANG, WIFEXITED(),
88
92
                                   WEXITSTATUS() */
89
93
#include <limits.h>             /* PIPE_BUF, NAME_MAX, INT_MAX */
90
94
#include <sys/inotify.h>        /* inotify_init1(), IN_NONBLOCK,
91
95
                                   IN_CLOEXEC, inotify_add_watch(),
92
96
                                   IN_CLOSE_WRITE, IN_MOVED_TO,
93
 
                                   IN_DELETE, struct inotify_event */
 
97
                                   IN_MOVED_FROM, IN_DELETE,
 
98
                                   IN_EXCL_UNLINK, IN_ONLYDIR,
 
99
                                   struct inotify_event */
94
100
#include <fnmatch.h>            /* fnmatch(), FNM_FILE_NAME */
95
 
#include <stdio.h>              /* asprintf(), FILE, fopen(),
96
 
                                   getline(), sscanf(), feof(),
97
 
                                   ferror(), fclose(), stderr,
98
 
                                   rename(), fdopen(), fprintf(),
99
 
                                   fscanf() */
 
101
#include <stdio.h>              /* asprintf(), FILE, stderr, fopen(),
 
102
                                   fclose(), getline(), sscanf(),
 
103
                                   feof(), ferror(), rename(),
 
104
                                   fdopen(), fprintf(), fscanf() */
100
105
#include <glib.h>    /* GKeyFile, g_key_file_free(), g_key_file_new(),
101
106
                        GError, g_key_file_load_from_file(),
102
107
                        G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
107
112
                        g_assert_null(), g_assert_false(),
108
113
                        g_assert_cmpint(), g_assert_cmpuint(),
109
114
                        g_test_skip(), g_assert_cmpstr(),
110
 
                        g_test_init(), g_test_add(), g_test_run(),
111
 
                        GOptionContext, g_option_context_new(),
 
115
                        g_test_message(), g_test_init(), g_test_add(),
 
116
                        g_test_run(), GOptionContext,
 
117
                        g_option_context_new(),
112
118
                        g_option_context_set_help_enabled(), FALSE,
113
119
                        g_option_context_set_ignore_unknown_options(),
114
120
                        gboolean, GOptionEntry, G_OPTION_ARG_NONE,
145
151
  mono_microsecs next_run;
146
152
} __attribute__((designated_init)) task_queue;
147
153
 
148
 
/* "func_type" - A function type for task functions
 
154
/* "task_func" - A function type for task functions
149
155
 
150
156
   I.e. functions for the code which runs when a task is run, all have
151
157
   this type */
431
437
    case EACCES:
432
438
    case ENAMETOOLONG:
433
439
    case ENOENT:
 
440
    case ENOTDIR:
434
441
      return EX_OSFILE;
435
442
    default:
436
443
      return EX_OSERR;
647
654
 
648
655
__attribute__((nonnull, warn_unused_result))
649
656
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
  }
650
664
  const size_t needed_size = sizeof(task_context)*(queue->length + 1);
651
665
  if(needed_size > (queue->allocated)){
652
666
    task_context *const new_tasks = realloc(queue->tasks,
857
871
  }
858
872
  close(pipefds[1]);
859
873
 
 
874
  if(pid == -1){
 
875
    error(0, errno, "Failed to fork()");
 
876
    close(pipefds[0]);
 
877
    return false;
 
878
  }
 
879
 
860
880
  if(not add_to_queue(queue, (task_context){
861
881
        .func=wait_for_mandos_client_exit,
862
882
        .pid=pid,
1017
1037
    return false;
1018
1038
  }
1019
1039
 
1020
 
  if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE
1021
 
                       | IN_MOVED_TO | IN_DELETE)
 
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)
1022
1043
     == -1){
1023
1044
    error(0, errno, "Failed to create inotify watch on %s", dir);
1024
1045
    return false;
1071
1092
  /* "sufficient to read at least one event." - inotify(7) */
1072
1093
  const size_t ievent_size = (sizeof(struct inotify_event)
1073
1094
                              + NAME_MAX + 1);
1074
 
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
1075
 
  struct inotify_event *ievent = ((struct inotify_event *)
1076
 
                                  ievent_buffer);
 
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;
1077
1100
 
1078
1101
  const ssize_t read_length = read(fd, ievent, ievent_size);
1079
1102
  if(read_length == 0){ /* EOF */
1117
1140
             immediately */
1118
1141
          queue->next_run = 1;
1119
1142
        }
1120
 
      } else if(ievent->mask & IN_DELETE){
 
1143
      } else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
1121
1144
        if(not string_set_add(cancelled_filenames,
1122
1145
                              question_filename)){
1123
1146
          error(0, errno, "Could not add question %s to"
1173
1196
  bool *const password_is_read = task.password_is_read;
1174
1197
 
1175
1198
  /* We use the GLib "Key-value file parser" functions to parse the
1176
 
     question file.  See <https://www.freedesktop.org/wiki/Software
1177
 
     /systemd/PasswordAgents/> for specification of contents */
 
1199
     question file.  See <https://systemd.io/PASSWORD_AGENTS/> for
 
1200
     specification of contents */
1178
1201
  __attribute__((nonnull))
1179
1202
    void cleanup_g_key_file(GKeyFile **key_file){
1180
1203
    if(*key_file != NULL){
1470
1493
         not. You may but don't have to include a final NUL byte in
1471
1494
         your message.
1472
1495
 
1473
 
         — <https://www.freedesktop.org/wiki/Software/systemd/
1474
 
         PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
 
1496
         — <https://systemd.io/PASSWORD_AGENTS/> (Tue, 15 Sep 2020
 
1497
         14:24:20 GMT)
1475
1498
      */
1476
1499
      send_buffer[0] = '+';     /* Prefix with "+" */
1477
1500
      /* Always add an extra NUL */
1482
1505
      errno = 0;
1483
1506
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1484
1507
                           MSG_NOSIGNAL);
1485
 
      const error_t saved_errno = errno;
 
1508
      const error_t saved_errno = (ssret < 0) ? errno : 0;
1486
1509
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1487
1510
      explicit_bzero(send_buffer, send_buffer_length);
1488
1511
#else
1506
1529
          /* Retry, below */
1507
1530
          break;
1508
1531
        case EMSGSIZE:
1509
 
          error(0, 0, "Password of size %" PRIuMAX " is too big",
1510
 
                (uintmax_t)password->length);
 
1532
          error(0, saved_errno, "Password of size %" PRIuMAX
 
1533
                " is too big", (uintmax_t)password->length);
1511
1534
#if __GNUC__ < 7
1512
1535
          /* FALLTHROUGH */
1513
1536
#else
1515
1538
#endif
1516
1539
        case 0:
1517
1540
          if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1518
 
            error(0, 0, "Password only partially sent to socket");
 
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);
1519
1544
          }
1520
1545
#if __GNUC__ < 7
1521
1546
          /* FALLTHROUGH */
1877
1902
  g_assert_true(queue->tasks[0].func == dummy_func);
1878
1903
}
1879
1904
 
 
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
 
1880
1928
static void dummy_func(__attribute__((unused))
1881
1929
                       const task_context task,
1882
1930
                       __attribute__((unused))
2153
2201
    }
2154
2202
    exit(EXIT_SUCCESS);
2155
2203
  }
 
2204
  if(pid == -1){
 
2205
    error(EXIT_FAILURE, errno, "Failed to fork()");
 
2206
  }
 
2207
 
2156
2208
  int status;
2157
2209
  waitpid(pid, &status, 0);
2158
2210
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2220
2272
 
2221
2273
  {
2222
2274
    __attribute__((cleanup(cleanup_close)))
2223
 
      const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
 
2275
      const int devnull_fd = open("/dev/null",
 
2276
                                  O_WRONLY | O_CLOEXEC | O_NOCTTY);
2224
2277
    g_assert_cmpint(devnull_fd, >=, 0);
2225
2278
    __attribute__((cleanup(cleanup_close)))
2226
2279
      const int real_stderr_fd = dup(STDERR_FILENO);
2250
2303
    {
2251
2304
      __attribute__((cleanup(cleanup_close)))
2252
2305
        const int devnull_fd = open("/dev/null",
2253
 
                                    O_WRONLY | O_CLOEXEC);
 
2306
                                    O_WRONLY | O_CLOEXEC | O_NOCTTY);
2254
2307
      g_assert_cmpint(devnull_fd, >=, 0);
2255
2308
      __attribute__((cleanup(cleanup_close)))
2256
2309
        const int real_stderr_fd = dup(STDERR_FILENO);
2901
2954
 
2902
2955
  __attribute__((cleanup(cleanup_close)))
2903
2956
    const int devnull_fd = open("/dev/null",
2904
 
                                O_WRONLY | O_CLOEXEC);
 
2957
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
2905
2958
  g_assert_cmpint(devnull_fd, >=, 0);
2906
2959
  __attribute__((cleanup(cleanup_close)))
2907
2960
    const int real_stderr_fd = dup(STDERR_FILENO);
2972
3025
 
2973
3026
  __attribute__((cleanup(cleanup_close)))
2974
3027
    const int devnull_fd = open("/dev/null",
2975
 
                                O_WRONLY | O_CLOEXEC);
 
3028
                                O_WRONLY | O_CLOEXEC, O_NOCTTY);
2976
3029
  g_assert_cmpint(devnull_fd, >=, 0);
2977
3030
  __attribute__((cleanup(cleanup_close)))
2978
3031
    const int real_stderr_fd = dup(STDERR_FILENO);
3016
3069
    buffer password = {};
3017
3070
 
3018
3071
  /* Reading /proc/self/mem from offset 0 will always give EIO */
3019
 
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
 
3072
  const int fd = open("/proc/self/mem",
 
3073
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
3020
3074
 
3021
3075
  bool password_is_read = false;
3022
3076
  bool quit_now = false;
3450
3504
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3451
3505
}
3452
3506
 
 
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
 
3453
3545
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3454
3546
                                              test_fixture *fixture,
3455
3547
                                              __attribute__((unused))
3670
3762
}
3671
3763
 
3672
3764
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
3673
3837
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3674
3838
                                          test_fixture *fixture,
3675
3839
                                          __attribute__((unused))
3733
3897
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3734
3898
}
3735
3899
 
 
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
 
3736
3976
static void test_read_inotify_event_readerror(__attribute__((unused))
3737
3977
                                              test_fixture *fixture,
3738
3978
                                              __attribute__((unused))
3744
3984
  const mono_microsecs current_time = 0;
3745
3985
 
3746
3986
  /* Reading /proc/self/mem from offset 0 will always result in EIO */
3747
 
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
 
3987
  const int fd = open("/proc/self/mem",
 
3988
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
3748
3989
 
3749
3990
  bool quit_now = false;
3750
3991
  __attribute__((cleanup(cleanup_queue)))
3929
4170
  const size_t ievent_max_size = (sizeof(struct inotify_event)
3930
4171
                                  + NAME_MAX + 1);
3931
4172
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
3932
 
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
3933
 
  struct inotify_event *ievent = ((struct inotify_event *)
3934
 
                                  ievent_buffer);
 
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;
3935
4178
 
3936
4179
  const char dummy_file_name[] = "ask.dummy_file_name";
3937
4180
  ievent->mask = IN_CLOSE_WRITE;
3939
4182
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
3940
4183
  const size_t ievent_size = (sizeof(struct inotify_event)
3941
4184
                              + sizeof(dummy_file_name));
3942
 
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
 
4185
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
3943
4186
                  ==, ievent_size);
3944
4187
  g_assert_cmpint(close(pipefds[1]), ==, 0);
3945
4188
 
4022
4265
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4023
4266
                                  + NAME_MAX + 1);
4024
4267
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4025
 
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
4026
 
  struct inotify_event *ievent = ((struct inotify_event *)
4027
 
                                  ievent_buffer);
 
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;
4028
4273
 
4029
4274
  const char dummy_file_name[] = "ask.dummy_file_name";
4030
4275
  ievent->mask = IN_MOVED_TO;
4032
4277
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4033
4278
  const size_t ievent_size = (sizeof(struct inotify_event)
4034
4279
                              + sizeof(dummy_file_name));
4035
 
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
 
4280
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4036
4281
                  ==, ievent_size);
4037
4282
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4038
4283
 
4098
4343
      }));
4099
4344
}
4100
4345
 
 
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
 
4101
4431
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4102
4432
                                              test_fixture *fixture,
4103
4433
                                              __attribute__((unused))
4117
4447
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4118
4448
                                  + NAME_MAX + 1);
4119
4449
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4120
 
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
4121
 
  struct inotify_event *ievent = ((struct inotify_event *)
4122
 
                                  ievent_buffer);
 
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;
4123
4455
 
4124
4456
  const char dummy_file_name[] = "ask.dummy_file_name";
4125
4457
  ievent->mask = IN_DELETE;
4127
4459
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4128
4460
  const size_t ievent_size = (sizeof(struct inotify_event)
4129
4461
                              + sizeof(dummy_file_name));
4130
 
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
 
4462
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4131
4463
                  ==, ievent_size);
4132
4464
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4133
4465
 
4199
4531
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4200
4532
                                  + NAME_MAX + 1);
4201
4533
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4202
 
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
4203
 
  struct inotify_event *ievent = ((struct inotify_event *)
4204
 
                                  ievent_buffer);
 
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;
4205
4539
 
4206
4540
  const char dummy_file_name[] = "ignored.dummy_file_name";
4207
4541
  ievent->mask = IN_CLOSE_WRITE;
4209
4543
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4210
4544
  const size_t ievent_size = (sizeof(struct inotify_event)
4211
4545
                              + sizeof(dummy_file_name));
4212
 
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
 
4546
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4213
4547
                  ==, ievent_size);
4214
4548
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4215
4549
 
4273
4607
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4274
4608
                                  + NAME_MAX + 1);
4275
4609
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4276
 
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
4277
 
  struct inotify_event *ievent = ((struct inotify_event *)
4278
 
                                  ievent_buffer);
 
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;
4279
4615
 
4280
4616
  const char dummy_file_name[] = "ignored.dummy_file_name";
4281
4617
  ievent->mask = IN_MOVED_TO;
4283
4619
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4284
4620
  const size_t ievent_size = (sizeof(struct inotify_event)
4285
4621
                              + sizeof(dummy_file_name));
4286
 
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
 
4622
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4287
4623
                  ==, ievent_size);
4288
4624
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4289
4625
 
4330
4666
                                   EPOLLIN | EPOLLRDHUP));
4331
4667
}
4332
4668
 
 
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
 
4333
4754
static
4334
4755
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4335
4756
                                               test_fixture *fixture,
4350
4771
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4351
4772
                                  + NAME_MAX + 1);
4352
4773
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4353
 
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
4354
 
  struct inotify_event *ievent = ((struct inotify_event *)
4355
 
                                  ievent_buffer);
 
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;
4356
4779
 
4357
4780
  const char dummy_file_name[] = "ignored.dummy_file_name";
4358
4781
  ievent->mask = IN_DELETE;
4360
4783
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4361
4784
  const size_t ievent_size = (sizeof(struct inotify_event)
4362
4785
                              + sizeof(dummy_file_name));
4363
 
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
 
4786
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4364
4787
                  ==, ievent_size);
4365
4788
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4366
4789
 
5253
5676
                                            __attribute__((unused))
5254
5677
                                            gconstpointer user_data){
5255
5678
  __attribute__((cleanup(cleanup_close)))
5256
 
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
 
5679
    const int epoll_fd = open("/dev/null",
 
5680
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
5257
5681
  __attribute__((cleanup(cleanup_string)))
5258
5682
    char *const question_filename = strdup("/nonexistent/question");
5259
5683
  g_assert_nonnull(question_filename);
5388
5812
  char write_data[PIPE_BUF];
5389
5813
  {
5390
5814
    /* Construct test password buffer */
5391
 
    /* Start with + since that is what the real procotol uses */
 
5815
    /* Start with + since that is what the real protocol uses */
5392
5816
    write_data[0] = '+';
5393
5817
    /* Set a special character at string end just to mark the end */
5394
5818
    write_data[sizeof(write_data)-2] = 'y';
5546
5970
  char *const filename = strdup("/nonexistent/socket");
5547
5971
  __attribute__((cleanup(string_set_clear)))
5548
5972
    string_set cancelled_filenames = {};
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,
 
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(): %s", strerror(saved_errno));
 
6009
        return;
 
6010
      }
 
6011
      break;
 
6012
    } else if(ssret != (ssize_t)message_size){
 
6013
      g_test_skip("Skipping EMSGSIZE test");
 
6014
      g_test_message("Partial send(): %" PRIuMAX " of %" PRIdMAX
 
6015
                     " bytes", (uintmax_t)ssret,
 
6016
                     (intmax_t)message_size);
 
6017
      return;
 
6018
    }
 
6019
  }
 
6020
  g_test_message("EMSGSIZE triggered by %" PRIdMAX " bytes",
 
6021
                 (intmax_t)message_size);
 
6022
 
 
6023
  buffer password = {
 
6024
    .data=message_buffer,
 
6025
    .length=message_size - 2,   /* Compensate for added '+' and NUL */
 
6026
    .allocated=message_size,
5555
6027
  };
5556
 
  g_assert_nonnull(password.data);
5557
6028
  if(mlock(password.data, password.allocated) != 0){
5558
6029
    g_assert_true(errno == EPERM or errno == ENOMEM);
5559
6030
  }
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);
5569
6031
 
5570
6032
  __attribute__((cleanup(cleanup_queue)))
5571
6033
    task_queue *queue = create_queue();
5572
6034
  g_assert_nonnull(queue);
5573
 
  int socketfds[2];
5574
6035
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5575
6036
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5576
6037
                             socketfds), ==, 0);
5663
6124
                                            __attribute__((unused))
5664
6125
                                            gconstpointer user_data){
5665
6126
  __attribute__((cleanup(cleanup_close)))
5666
 
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
 
6127
    const int epoll_fd = open("/dev/null",
 
6128
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
5667
6129
  __attribute__((cleanup(cleanup_string)))
5668
6130
    char *const question_filename = strdup("/nonexistent/question");
5669
6131
  g_assert_nonnull(question_filename);
5932
6394
                                              const char *const
5933
6395
                                              dirname){
5934
6396
  __attribute__((cleanup(cleanup_close)))
5935
 
    const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
 
6397
    const int devnull_fd = open("/dev/null",
 
6398
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
5936
6399
  g_assert_cmpint(devnull_fd, >=, 0);
5937
6400
  __attribute__((cleanup(cleanup_close)))
5938
6401
    const int real_stderr_fd = dup(STDERR_FILENO);
7481
7944
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7482
7945
  test_add("/queue/create", test_create_queue);
7483
7946
  test_add("/queue/add", test_add_to_queue);
 
7947
  test_add("/queue/add/overflow", test_add_to_queue_overflow);
7484
7948
  test_add("/queue/has_question/empty",
7485
7949
           test_queue_has_question_empty);
7486
7950
  test_add("/queue/has_question/false",
7573
8037
              test_add_inotify_dir_watch);
7574
8038
  test_add_st("/task-creators/add_inotify_dir_watch/fail",
7575
8039
              test_add_inotify_dir_watch_fail);
 
8040
  test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory",
 
8041
              test_add_inotify_dir_watch_nondir);
7576
8042
  test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
7577
8043
              test_add_inotify_dir_watch_EAGAIN);
7578
8044
  test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
7579
8045
              test_add_inotify_dir_watch_IN_CLOSE_WRITE);
7580
8046
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
7581
8047
              test_add_inotify_dir_watch_IN_MOVED_TO);
 
8048
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
 
8049
              test_add_inotify_dir_watch_IN_MOVED_FROM);
 
8050
  test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
 
8051
              test_add_inotify_dir_watch_IN_EXCL_UNLINK);
7582
8052
  test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
7583
8053
              test_add_inotify_dir_watch_IN_DELETE);
7584
8054
  test_add_st("/task/read_inotify_event/readerror",
7593
8063
              test_read_inotify_event_IN_CLOSE_WRITE);
7594
8064
  test_add_st("/task/read_inotify_event/IN_MOVED_TO",
7595
8065
              test_read_inotify_event_IN_MOVED_TO);
 
8066
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
 
8067
              test_read_inotify_event_IN_MOVED_FROM);
7596
8068
  test_add_st("/task/read_inotify_event/IN_DELETE",
7597
8069
              test_read_inotify_event_IN_DELETE);
7598
8070
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
7599
8071
              test_read_inotify_event_IN_CLOSE_WRITE_badname);
7600
8072
  test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
7601
8073
              test_read_inotify_event_IN_MOVED_TO_badname);
 
8074
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
 
8075
              test_read_inotify_event_IN_MOVED_FROM_badname);
7602
8076
  test_add_st("/task/read_inotify_event/IN_DELETE/badname",
7603
8077
              test_read_inotify_event_IN_DELETE_badname);
7604
8078
  test_add_st("/task/open_and_parse_question/ENOENT",
7701
8175
  g_option_context_set_help_enabled(context, FALSE);
7702
8176
  g_option_context_set_ignore_unknown_options(context, TRUE);
7703
8177
 
7704
 
  gboolean run_tests = FALSE;
 
8178
  gboolean should_run_tests = FALSE;
7705
8179
  GOptionEntry entries[] = {
7706
8180
    { "test", 0, 0, G_OPTION_ARG_NONE,
7707
 
      &run_tests, "Run tests", NULL },
 
8181
      &should_run_tests, "Run tests", NULL },
7708
8182
    { NULL }
7709
8183
  };
7710
8184
  g_option_context_add_main_entries(context, entries, NULL);
7717
8191
  }
7718
8192
 
7719
8193
  g_option_context_free(context);
7720
 
  return run_tests != FALSE;
 
8194
  return should_run_tests != FALSE;
7721
8195
}