/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 at recompile
  • Date: 2020-12-03 20:30:45 UTC
  • Revision ID: teddy@recompile.se-20201203203045-iqd6nq9y5nwalh1x
Minor fix of a test function

In dracut-module/password-agent, the test function
test_send_password_to_socket_EMSGSIZE() (which tests that the
send_password_to_socket() task function aborts properly when getting
EMSGSIZE when writing to the password socket), part of the test code
is supposed to find a message size which definitely does trigger
EMSGSIZE when send()ing to a socket.  Without a "break" in the proper
place, however, the size given is always exactly 1024 bytes too large.

This is very probably not a problem, since a too large message will
still be too large if it is increased by 1024 bytes, and send(2) in
practice checks the size before reading the buffer.  The biggest issue
would be if some version of send(2) would try to look at the last 1024
bytes of the message buffer before checking the message size; this
would then lead to a buffer over-read when running this test function.
(But even then there would be no security implications since the tests
are not run in the normal operation of the program.)

* dracut-module/password-agent.c
  (test_send_password_to_socket_EMSGSIZE): Break out early when ssret
  < 0 and errno == EMSGSIZE; don't allow loop to increase message_size
  again.

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-2020 Teddy Hogeborn
 
6
 * Copyright © 2019-2020 Björn Påhlsson
7
7
 * 
8
8
 * This file is part of Mandos.
9
9
 * 
48
48
#include <error.h>              /* error() */
49
49
#include <sysexits.h>           /* EX_USAGE, EX_OSERR, EX_OSFILE */
50
50
#include <errno.h>              /* errno, error_t, EACCES,
51
 
                                   ENAMETOOLONG, ENOENT, EEXIST,
52
 
                                   ECHILD, EPERM, ENOMEM, EAGAIN,
53
 
                                   EINTR, ENOBUFS, EADDRINUSE,
 
51
                                   ENAMETOOLONG, ENOENT, ENOTDIR,
 
52
                                   ENOMEM, EEXIST, ECHILD, EPERM,
 
53
                                   EAGAIN, EINTR, ENOBUFS, EADDRINUSE,
54
54
                                   ECONNREFUSED, ECONNRESET,
55
55
                                   ETOOMANYREFS, EMSGSIZE, EBADF,
56
56
                                   EINVAL */
73
73
                                   ARGP_ERR_UNKNOWN, ARGP_KEY_ARGS,
74
74
                                   struct argp, argp_parse(),
75
75
                                   ARGP_NO_EXIT */
 
76
#include <stdint.h>             /* SIZE_MAX */
76
77
#include <unistd.h>             /* uid_t, gid_t, close(), pipe2(),
77
78
                                   fork(), _exit(), dup2(),
78
79
                                   STDOUT_FILENO, setresgid(),
83
84
#include <sys/mman.h>           /* munlock(), mlock() */
84
85
#include <fcntl.h>              /* O_CLOEXEC, O_NONBLOCK, fcntl(),
85
86
                                   F_GETFD, F_GETFL, FD_CLOEXEC,
86
 
                                   open(), O_WRONLY, O_RDONLY */
 
87
                                   open(), O_WRONLY, O_NOCTTY,
 
88
                                   O_RDONLY, O_NOFOLLOW */
87
89
#include <sys/wait.h>           /* waitpid(), WNOHANG, WIFEXITED(),
88
90
                                   WEXITSTATUS() */
89
91
#include <limits.h>             /* PIPE_BUF, NAME_MAX, INT_MAX */
90
92
#include <sys/inotify.h>        /* inotify_init1(), IN_NONBLOCK,
91
93
                                   IN_CLOEXEC, inotify_add_watch(),
92
94
                                   IN_CLOSE_WRITE, IN_MOVED_TO,
93
 
                                   IN_DELETE, struct inotify_event */
 
95
                                   IN_MOVED_FROM, IN_DELETE,
 
96
                                   IN_EXCL_UNLINK, IN_ONLYDIR,
 
97
                                   struct inotify_event */
94
98
#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() */
 
99
#include <stdio.h>              /* asprintf(), FILE, stderr, fopen(),
 
100
                                   fclose(), getline(), sscanf(),
 
101
                                   feof(), ferror(), rename(),
 
102
                                   fdopen(), fprintf(), fscanf() */
100
103
#include <glib.h>    /* GKeyFile, g_key_file_free(), g_key_file_new(),
101
104
                        GError, g_key_file_load_from_file(),
102
105
                        G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
145
148
  mono_microsecs next_run;
146
149
} __attribute__((designated_init)) task_queue;
147
150
 
148
 
/* "func_type" - A function type for task functions
 
151
/* "task_func" - A function type for task functions
149
152
 
150
153
   I.e. functions for the code which runs when a task is run, all have
151
154
   this type */
431
434
    case EACCES:
432
435
    case ENAMETOOLONG:
433
436
    case ENOENT:
 
437
    case ENOTDIR:
434
438
      return EX_OSFILE;
435
439
    default:
436
440
      return EX_OSERR;
647
651
 
648
652
__attribute__((nonnull, warn_unused_result))
649
653
bool add_to_queue(task_queue *const queue, const task_context task){
 
654
  if((queue->length + 1) > (SIZE_MAX / sizeof(task_context))){
 
655
    /* overflow */
 
656
    error(0, ENOMEM, "Failed to allocate %" PRIuMAX
 
657
          " tasks for queue->tasks", (uintmax_t)(queue->length + 1));
 
658
    errno = ENOMEM;
 
659
    return false;
 
660
  }
650
661
  const size_t needed_size = sizeof(task_context)*(queue->length + 1);
651
662
  if(needed_size > (queue->allocated)){
652
663
    task_context *const new_tasks = realloc(queue->tasks,
857
868
  }
858
869
  close(pipefds[1]);
859
870
 
 
871
  if(pid == -1){
 
872
    error(0, errno, "Failed to fork()");
 
873
    close(pipefds[0]);
 
874
    return false;
 
875
  }
 
876
 
860
877
  if(not add_to_queue(queue, (task_context){
861
878
        .func=wait_for_mandos_client_exit,
862
879
        .pid=pid,
1017
1034
    return false;
1018
1035
  }
1019
1036
 
1020
 
  if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE
1021
 
                       | IN_MOVED_TO | IN_DELETE)
 
1037
  if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO
 
1038
                       | IN_MOVED_FROM| IN_DELETE | IN_EXCL_UNLINK
 
1039
                       | IN_ONLYDIR)
1022
1040
     == -1){
1023
1041
    error(0, errno, "Failed to create inotify watch on %s", dir);
1024
1042
    return false;
1071
1089
  /* "sufficient to read at least one event." - inotify(7) */
1072
1090
  const size_t ievent_size = (sizeof(struct inotify_event)
1073
1091
                              + 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);
 
1092
  struct {
 
1093
    struct inotify_event event;
 
1094
    char name_buffer[NAME_MAX + 1];
 
1095
  } ievent_buffer;
 
1096
  struct inotify_event *const ievent = &ievent_buffer.event;
1077
1097
 
1078
1098
  const ssize_t read_length = read(fd, ievent, ievent_size);
1079
1099
  if(read_length == 0){ /* EOF */
1117
1137
             immediately */
1118
1138
          queue->next_run = 1;
1119
1139
        }
1120
 
      } else if(ievent->mask & IN_DELETE){
 
1140
      } else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
1121
1141
        if(not string_set_add(cancelled_filenames,
1122
1142
                              question_filename)){
1123
1143
          error(0, errno, "Could not add question %s to"
1173
1193
  bool *const password_is_read = task.password_is_read;
1174
1194
 
1175
1195
  /* 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 */
 
1196
     question file.  See <https://systemd.io/PASSWORD_AGENTS/> for
 
1197
     specification of contents */
1178
1198
  __attribute__((nonnull))
1179
1199
    void cleanup_g_key_file(GKeyFile **key_file){
1180
1200
    if(*key_file != NULL){
1470
1490
         not. You may but don't have to include a final NUL byte in
1471
1491
         your message.
1472
1492
 
1473
 
         — <https://www.freedesktop.org/wiki/Software/systemd/
1474
 
         PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
 
1493
         — <https://systemd.io/PASSWORD_AGENTS/> (Tue, 15 Sep 2020
 
1494
         14:24:20 GMT)
1475
1495
      */
1476
1496
      send_buffer[0] = '+';     /* Prefix with "+" */
1477
1497
      /* Always add an extra NUL */
1482
1502
      errno = 0;
1483
1503
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1484
1504
                           MSG_NOSIGNAL);
1485
 
      const error_t saved_errno = errno;
 
1505
      const error_t saved_errno = (ssret < 0) ? errno : 0;
1486
1506
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1487
1507
      explicit_bzero(send_buffer, send_buffer_length);
1488
1508
#else
1506
1526
          /* Retry, below */
1507
1527
          break;
1508
1528
        case EMSGSIZE:
1509
 
          error(0, 0, "Password of size %" PRIuMAX " is too big",
1510
 
                (uintmax_t)password->length);
 
1529
          error(0, saved_errno, "Password of size %" PRIuMAX
 
1530
                " is too big", (uintmax_t)password->length);
1511
1531
#if __GNUC__ < 7
1512
1532
          /* FALLTHROUGH */
1513
1533
#else
1515
1535
#endif
1516
1536
        case 0:
1517
1537
          if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1518
 
            error(0, 0, "Password only partially sent to socket");
 
1538
            error(0, 0, "Password only partially sent to socket %s: %"
 
1539
                  PRIuMAX " out of %" PRIuMAX " bytes sent", filename,
 
1540
                  (uintmax_t)ssret, (uintmax_t)send_buffer_length);
1519
1541
          }
1520
1542
#if __GNUC__ < 7
1521
1543
          /* FALLTHROUGH */
1877
1899
  g_assert_true(queue->tasks[0].func == dummy_func);
1878
1900
}
1879
1901
 
 
1902
static void test_add_to_queue_overflow(__attribute__((unused))
 
1903
                                       test_fixture *fixture,
 
1904
                                       __attribute__((unused))
 
1905
                                       gconstpointer user_data){
 
1906
  __attribute__((cleanup(cleanup_queue)))
 
1907
    task_queue *queue = create_queue();
 
1908
  g_assert_nonnull(queue);
 
1909
  g_assert_true(queue->length == 0);
 
1910
  queue->length = SIZE_MAX / sizeof(task_context); /* fake max size */
 
1911
 
 
1912
  FILE *real_stderr = stderr;
 
1913
  FILE *devnull = fopen("/dev/null", "we");
 
1914
  g_assert_nonnull(devnull);
 
1915
  stderr = devnull;
 
1916
  const bool ret = add_to_queue(queue,
 
1917
                                (task_context){ .func=dummy_func });
 
1918
  g_assert_true(errno == ENOMEM);
 
1919
  g_assert_false(ret);
 
1920
  stderr = real_stderr;
 
1921
  g_assert_cmpint(fclose(devnull), ==, 0);
 
1922
  queue->length = 0;            /* Restore real size */
 
1923
}
 
1924
 
1880
1925
static void dummy_func(__attribute__((unused))
1881
1926
                       const task_context task,
1882
1927
                       __attribute__((unused))
2153
2198
    }
2154
2199
    exit(EXIT_SUCCESS);
2155
2200
  }
 
2201
  if(pid == -1){
 
2202
    error(EXIT_FAILURE, errno, "Failed to fork()");
 
2203
  }
 
2204
 
2156
2205
  int status;
2157
2206
  waitpid(pid, &status, 0);
2158
2207
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2220
2269
 
2221
2270
  {
2222
2271
    __attribute__((cleanup(cleanup_close)))
2223
 
      const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
 
2272
      const int devnull_fd = open("/dev/null",
 
2273
                                  O_WRONLY | O_CLOEXEC | O_NOCTTY);
2224
2274
    g_assert_cmpint(devnull_fd, >=, 0);
2225
2275
    __attribute__((cleanup(cleanup_close)))
2226
2276
      const int real_stderr_fd = dup(STDERR_FILENO);
2250
2300
    {
2251
2301
      __attribute__((cleanup(cleanup_close)))
2252
2302
        const int devnull_fd = open("/dev/null",
2253
 
                                    O_WRONLY | O_CLOEXEC);
 
2303
                                    O_WRONLY | O_CLOEXEC | O_NOCTTY);
2254
2304
      g_assert_cmpint(devnull_fd, >=, 0);
2255
2305
      __attribute__((cleanup(cleanup_close)))
2256
2306
        const int real_stderr_fd = dup(STDERR_FILENO);
2901
2951
 
2902
2952
  __attribute__((cleanup(cleanup_close)))
2903
2953
    const int devnull_fd = open("/dev/null",
2904
 
                                O_WRONLY | O_CLOEXEC);
 
2954
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
2905
2955
  g_assert_cmpint(devnull_fd, >=, 0);
2906
2956
  __attribute__((cleanup(cleanup_close)))
2907
2957
    const int real_stderr_fd = dup(STDERR_FILENO);
2972
3022
 
2973
3023
  __attribute__((cleanup(cleanup_close)))
2974
3024
    const int devnull_fd = open("/dev/null",
2975
 
                                O_WRONLY | O_CLOEXEC);
 
3025
                                O_WRONLY | O_CLOEXEC, O_NOCTTY);
2976
3026
  g_assert_cmpint(devnull_fd, >=, 0);
2977
3027
  __attribute__((cleanup(cleanup_close)))
2978
3028
    const int real_stderr_fd = dup(STDERR_FILENO);
3016
3066
    buffer password = {};
3017
3067
 
3018
3068
  /* Reading /proc/self/mem from offset 0 will always give EIO */
3019
 
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
 
3069
  const int fd = open("/proc/self/mem",
 
3070
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
3020
3071
 
3021
3072
  bool password_is_read = false;
3022
3073
  bool quit_now = false;
3450
3501
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3451
3502
}
3452
3503
 
 
3504
static void test_add_inotify_dir_watch_nondir(__attribute__((unused))
 
3505
                                              test_fixture *fixture,
 
3506
                                            __attribute__((unused))
 
3507
                                              gconstpointer
 
3508
                                              user_data){
 
3509
  __attribute__((cleanup(cleanup_close)))
 
3510
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3511
  g_assert_cmpint(epoll_fd, >=, 0);
 
3512
  __attribute__((cleanup(cleanup_queue)))
 
3513
    task_queue *queue = create_queue();
 
3514
  g_assert_nonnull(queue);
 
3515
  __attribute__((cleanup(string_set_clear)))
 
3516
    string_set cancelled_filenames = {};
 
3517
  const mono_microsecs current_time = 0;
 
3518
 
 
3519
  bool quit_now = false;
 
3520
  buffer password = {};
 
3521
  bool mandos_client_exited = false;
 
3522
  bool password_is_read = false;
 
3523
 
 
3524
  const char not_a_directory[] = "/dev/tty";
 
3525
 
 
3526
  FILE *real_stderr = stderr;
 
3527
  FILE *devnull = fopen("/dev/null", "we");
 
3528
  g_assert_nonnull(devnull);
 
3529
  stderr = devnull;
 
3530
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3531
                                       &password, not_a_directory,
 
3532
                                       &cancelled_filenames,
 
3533
                                       &current_time,
 
3534
                                       &mandos_client_exited,
 
3535
                                       &password_is_read));
 
3536
  stderr = real_stderr;
 
3537
  g_assert_cmpint(fclose(devnull), ==, 0);
 
3538
 
 
3539
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
 
3540
}
 
3541
 
3453
3542
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3454
3543
                                              test_fixture *fixture,
3455
3544
                                              __attribute__((unused))
3670
3759
}
3671
3760
 
3672
3761
static
 
3762
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
 
3763
                                              test_fixture *fixture,
 
3764
                                              __attribute__((unused))
 
3765
                                              gconstpointer
 
3766
                                              user_data){
 
3767
  __attribute__((cleanup(cleanup_close)))
 
3768
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3769
  g_assert_cmpint(epoll_fd, >=, 0);
 
3770
  __attribute__((cleanup(cleanup_queue)))
 
3771
    task_queue *queue = create_queue();
 
3772
  g_assert_nonnull(queue);
 
3773
  __attribute__((cleanup(string_set_clear)))
 
3774
    string_set cancelled_filenames = {};
 
3775
  const mono_microsecs current_time = 0;
 
3776
 
 
3777
  bool quit_now = false;
 
3778
  buffer password = {};
 
3779
  bool mandos_client_exited = false;
 
3780
  bool password_is_read = false;
 
3781
 
 
3782
  __attribute__((cleanup(cleanup_string)))
 
3783
    char *tempdir = make_temporary_directory();
 
3784
  g_assert_nonnull(tempdir);
 
3785
 
 
3786
  __attribute__((cleanup(cleanup_string)))
 
3787
    char *tempfilename = make_temporary_file_in_directory(tempdir);
 
3788
  g_assert_nonnull(tempfilename);
 
3789
 
 
3790
  __attribute__((cleanup(cleanup_string)))
 
3791
    char *targetdir = make_temporary_directory();
 
3792
  g_assert_nonnull(targetdir);
 
3793
 
 
3794
  __attribute__((cleanup(cleanup_string)))
 
3795
    char *targetfilename = NULL;
 
3796
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
 
3797
                           basename(tempfilename)), >, 0);
 
3798
  g_assert_nonnull(targetfilename);
 
3799
 
 
3800
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3801
                                      &password, tempdir,
 
3802
                                      &cancelled_filenames,
 
3803
                                      &current_time,
 
3804
                                      &mandos_client_exited,
 
3805
                                      &password_is_read));
 
3806
 
 
3807
  g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
 
3808
 
 
3809
  const task_context *const added_read_task
 
3810
    = find_matching_task(queue,
 
3811
                         (task_context){ .func=read_inotify_event });
 
3812
  g_assert_nonnull(added_read_task);
 
3813
 
 
3814
  /* "sufficient to read at least one event." - inotify(7) */
 
3815
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3816
                              + NAME_MAX + 1);
 
3817
  struct inotify_event *ievent = malloc(ievent_size);
 
3818
  g_assert_nonnull(ievent);
 
3819
 
 
3820
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
 
3821
 
 
3822
  g_assert_cmpint((int)read_size, >, 0);
 
3823
  g_assert_true(ievent->mask & IN_MOVED_FROM);
 
3824
  g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
 
3825
 
 
3826
  free(ievent);
 
3827
 
 
3828
  g_assert_cmpint(unlink(targetfilename), ==, 0);
 
3829
  g_assert_cmpint(rmdir(targetdir), ==, 0);
 
3830
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
3831
}
 
3832
 
 
3833
static
3673
3834
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3674
3835
                                          test_fixture *fixture,
3675
3836
                                          __attribute__((unused))
3733
3894
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3734
3895
}
3735
3896
 
 
3897
static
 
3898
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
 
3899
                                               test_fixture *fixture,
 
3900
                                               __attribute__((unused))
 
3901
                                               gconstpointer
 
3902
                                               user_data){
 
3903
  __attribute__((cleanup(cleanup_close)))
 
3904
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
3905
  g_assert_cmpint(epoll_fd, >=, 0);
 
3906
  __attribute__((cleanup(cleanup_queue)))
 
3907
    task_queue *queue = create_queue();
 
3908
  g_assert_nonnull(queue);
 
3909
  __attribute__((cleanup(string_set_clear)))
 
3910
    string_set cancelled_filenames = {};
 
3911
  const mono_microsecs current_time = 0;
 
3912
 
 
3913
  bool quit_now = false;
 
3914
  buffer password = {};
 
3915
  bool mandos_client_exited = false;
 
3916
  bool password_is_read = false;
 
3917
 
 
3918
  __attribute__((cleanup(cleanup_string)))
 
3919
    char *tempdir = make_temporary_directory();
 
3920
  g_assert_nonnull(tempdir);
 
3921
 
 
3922
  __attribute__((cleanup(cleanup_string)))
 
3923
    char *tempfile = make_temporary_file_in_directory(tempdir);
 
3924
  g_assert_nonnull(tempfile);
 
3925
  int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
 
3926
                         | O_NOFOLLOW);
 
3927
  g_assert_cmpint(tempfile_fd, >, 2);
 
3928
 
 
3929
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
 
3930
                                      &password, tempdir,
 
3931
                                      &cancelled_filenames,
 
3932
                                      &current_time,
 
3933
                                      &mandos_client_exited,
 
3934
                                      &password_is_read));
 
3935
  g_assert_cmpint(unlink(tempfile), ==, 0);
 
3936
 
 
3937
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
 
3938
 
 
3939
  const task_context *const added_read_task
 
3940
    = find_matching_task(queue,
 
3941
                         (task_context){ .func=read_inotify_event });
 
3942
  g_assert_nonnull(added_read_task);
 
3943
 
 
3944
  g_assert_cmpint(added_read_task->fd, >, 2);
 
3945
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
 
3946
 
 
3947
  /* "sufficient to read at least one event." - inotify(7) */
 
3948
  const size_t ievent_size = (sizeof(struct inotify_event)
 
3949
                              + NAME_MAX + 1);
 
3950
  struct inotify_event *ievent = malloc(ievent_size);
 
3951
  g_assert_nonnull(ievent);
 
3952
 
 
3953
  ssize_t read_size = 0;
 
3954
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
3955
 
 
3956
  g_assert_cmpint((int)read_size, >, 0);
 
3957
  g_assert_true(ievent->mask & IN_DELETE);
 
3958
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
 
3959
 
 
3960
  g_assert_cmpint(close(tempfile_fd), ==, 0);
 
3961
 
 
3962
  /* IN_EXCL_UNLINK should make the closing of the previously unlinked
 
3963
     file not appear as an ievent, so we should not see it now. */
 
3964
  read_size = read(added_read_task->fd, ievent, ievent_size);
 
3965
  g_assert_cmpint((int)read_size, ==, -1);
 
3966
  g_assert_true(errno == EAGAIN);
 
3967
 
 
3968
  free(ievent);
 
3969
 
 
3970
  g_assert_cmpint(rmdir(tempdir), ==, 0);
 
3971
}
 
3972
 
3736
3973
static void test_read_inotify_event_readerror(__attribute__((unused))
3737
3974
                                              test_fixture *fixture,
3738
3975
                                              __attribute__((unused))
3744
3981
  const mono_microsecs current_time = 0;
3745
3982
 
3746
3983
  /* Reading /proc/self/mem from offset 0 will always result in EIO */
3747
 
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
 
3984
  const int fd = open("/proc/self/mem",
 
3985
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
3748
3986
 
3749
3987
  bool quit_now = false;
3750
3988
  __attribute__((cleanup(cleanup_queue)))
3929
4167
  const size_t ievent_max_size = (sizeof(struct inotify_event)
3930
4168
                                  + NAME_MAX + 1);
3931
4169
  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);
 
4170
  struct {
 
4171
    struct inotify_event event;
 
4172
    char name_buffer[NAME_MAX + 1];
 
4173
  } ievent_buffer;
 
4174
  struct inotify_event *const ievent = &ievent_buffer.event;
3935
4175
 
3936
4176
  const char dummy_file_name[] = "ask.dummy_file_name";
3937
4177
  ievent->mask = IN_CLOSE_WRITE;
3939
4179
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
3940
4180
  const size_t ievent_size = (sizeof(struct inotify_event)
3941
4181
                              + sizeof(dummy_file_name));
3942
 
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
 
4182
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
3943
4183
                  ==, ievent_size);
3944
4184
  g_assert_cmpint(close(pipefds[1]), ==, 0);
3945
4185
 
4022
4262
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4023
4263
                                  + NAME_MAX + 1);
4024
4264
  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);
 
4265
  struct {
 
4266
    struct inotify_event event;
 
4267
    char name_buffer[NAME_MAX + 1];
 
4268
  } ievent_buffer;
 
4269
  struct inotify_event *const ievent = &ievent_buffer.event;
4028
4270
 
4029
4271
  const char dummy_file_name[] = "ask.dummy_file_name";
4030
4272
  ievent->mask = IN_MOVED_TO;
4032
4274
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4033
4275
  const size_t ievent_size = (sizeof(struct inotify_event)
4034
4276
                              + sizeof(dummy_file_name));
4035
 
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
 
4277
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4036
4278
                  ==, ievent_size);
4037
4279
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4038
4280
 
4098
4340
      }));
4099
4341
}
4100
4342
 
 
4343
static
 
4344
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
 
4345
                                           test_fixture *fixture,
 
4346
                                           __attribute__((unused))
 
4347
                                           gconstpointer user_data){
 
4348
  __attribute__((cleanup(cleanup_close)))
 
4349
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4350
  g_assert_cmpint(epoll_fd, >=, 0);
 
4351
  __attribute__((cleanup(string_set_clear)))
 
4352
    string_set cancelled_filenames = {};
 
4353
  const mono_microsecs current_time = 0;
 
4354
 
 
4355
  int pipefds[2];
 
4356
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4357
 
 
4358
  /* "sufficient to read at least one event." - inotify(7) */
 
4359
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4360
                                  + NAME_MAX + 1);
 
4361
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4362
  struct {
 
4363
    struct inotify_event event;
 
4364
    char name_buffer[NAME_MAX + 1];
 
4365
  } ievent_buffer;
 
4366
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4367
 
 
4368
  const char dummy_file_name[] = "ask.dummy_file_name";
 
4369
  ievent->mask = IN_MOVED_FROM;
 
4370
  ievent->len = sizeof(dummy_file_name);
 
4371
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4372
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4373
                              + sizeof(dummy_file_name));
 
4374
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4375
                  ==, ievent_size);
 
4376
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4377
 
 
4378
  bool quit_now = false;
 
4379
  buffer password = {};
 
4380
  bool mandos_client_exited = false;
 
4381
  bool password_is_read = false;
 
4382
  __attribute__((cleanup(cleanup_queue)))
 
4383
    task_queue *queue = create_queue();
 
4384
  g_assert_nonnull(queue);
 
4385
 
 
4386
  task_context task = {
 
4387
    .func=read_inotify_event,
 
4388
    .epoll_fd=epoll_fd,
 
4389
    .fd=pipefds[0],
 
4390
    .quit_now=&quit_now,
 
4391
    .password=&password,
 
4392
    .filename=strdup("/nonexistent"),
 
4393
    .cancelled_filenames=&cancelled_filenames,
 
4394
    .current_time=&current_time,
 
4395
    .mandos_client_exited=&mandos_client_exited,
 
4396
    .password_is_read=&password_is_read,
 
4397
  };
 
4398
  task.func(task, queue);
 
4399
  g_assert_false(quit_now);
 
4400
  g_assert_true(queue->next_run == 0);
 
4401
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4402
 
 
4403
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4404
        .func=read_inotify_event,
 
4405
        .epoll_fd=epoll_fd,
 
4406
        .fd=pipefds[0],
 
4407
        .quit_now=&quit_now,
 
4408
        .password=&password,
 
4409
        .filename=task.filename,
 
4410
        .cancelled_filenames=&cancelled_filenames,
 
4411
        .current_time=&current_time,
 
4412
        .mandos_client_exited=&mandos_client_exited,
 
4413
        .password_is_read=&password_is_read,
 
4414
      }));
 
4415
 
 
4416
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4417
                                   EPOLLIN | EPOLLRDHUP));
 
4418
 
 
4419
  __attribute__((cleanup(cleanup_string)))
 
4420
    char *filename = NULL;
 
4421
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
4422
                           dummy_file_name), >, 0);
 
4423
  g_assert_nonnull(filename);
 
4424
  g_assert_true(string_set_contains(*task.cancelled_filenames,
 
4425
                                    filename));
 
4426
}
 
4427
 
4101
4428
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4102
4429
                                              test_fixture *fixture,
4103
4430
                                              __attribute__((unused))
4117
4444
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4118
4445
                                  + NAME_MAX + 1);
4119
4446
  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);
 
4447
  struct {
 
4448
    struct inotify_event event;
 
4449
    char name_buffer[NAME_MAX + 1];
 
4450
  } ievent_buffer;
 
4451
  struct inotify_event *const ievent = &ievent_buffer.event;
4123
4452
 
4124
4453
  const char dummy_file_name[] = "ask.dummy_file_name";
4125
4454
  ievent->mask = IN_DELETE;
4127
4456
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4128
4457
  const size_t ievent_size = (sizeof(struct inotify_event)
4129
4458
                              + sizeof(dummy_file_name));
4130
 
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
 
4459
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4131
4460
                  ==, ievent_size);
4132
4461
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4133
4462
 
4199
4528
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4200
4529
                                  + NAME_MAX + 1);
4201
4530
  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);
 
4531
  struct {
 
4532
    struct inotify_event event;
 
4533
    char name_buffer[NAME_MAX + 1];
 
4534
  } ievent_buffer;
 
4535
  struct inotify_event *const ievent = &ievent_buffer.event;
4205
4536
 
4206
4537
  const char dummy_file_name[] = "ignored.dummy_file_name";
4207
4538
  ievent->mask = IN_CLOSE_WRITE;
4209
4540
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4210
4541
  const size_t ievent_size = (sizeof(struct inotify_event)
4211
4542
                              + sizeof(dummy_file_name));
4212
 
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
 
4543
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4213
4544
                  ==, ievent_size);
4214
4545
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4215
4546
 
4273
4604
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4274
4605
                                  + NAME_MAX + 1);
4275
4606
  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);
 
4607
  struct {
 
4608
    struct inotify_event event;
 
4609
    char name_buffer[NAME_MAX + 1];
 
4610
  } ievent_buffer;
 
4611
  struct inotify_event *const ievent = &ievent_buffer.event;
4279
4612
 
4280
4613
  const char dummy_file_name[] = "ignored.dummy_file_name";
4281
4614
  ievent->mask = IN_MOVED_TO;
4283
4616
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4284
4617
  const size_t ievent_size = (sizeof(struct inotify_event)
4285
4618
                              + sizeof(dummy_file_name));
4286
 
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
 
4619
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4287
4620
                  ==, ievent_size);
4288
4621
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4289
4622
 
4330
4663
                                   EPOLLIN | EPOLLRDHUP));
4331
4664
}
4332
4665
 
 
4666
static void
 
4667
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
 
4668
                                              test_fixture *fixture,
 
4669
                                              __attribute__((unused))
 
4670
                                              gconstpointer
 
4671
                                              user_data){
 
4672
  __attribute__((cleanup(cleanup_close)))
 
4673
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 
4674
  g_assert_cmpint(epoll_fd, >=, 0);
 
4675
  __attribute__((cleanup(string_set_clear)))
 
4676
    string_set cancelled_filenames = {};
 
4677
  const mono_microsecs current_time = 0;
 
4678
 
 
4679
  int pipefds[2];
 
4680
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
 
4681
 
 
4682
  /* "sufficient to read at least one event." - inotify(7) */
 
4683
  const size_t ievent_max_size = (sizeof(struct inotify_event)
 
4684
                                  + NAME_MAX + 1);
 
4685
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
 
4686
  struct {
 
4687
    struct inotify_event event;
 
4688
    char name_buffer[NAME_MAX + 1];
 
4689
  } ievent_buffer;
 
4690
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4691
 
 
4692
  const char dummy_file_name[] = "ignored.dummy_file_name";
 
4693
  ievent->mask = IN_MOVED_FROM;
 
4694
  ievent->len = sizeof(dummy_file_name);
 
4695
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
 
4696
  const size_t ievent_size = (sizeof(struct inotify_event)
 
4697
                              + sizeof(dummy_file_name));
 
4698
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4699
                  ==, ievent_size);
 
4700
  g_assert_cmpint(close(pipefds[1]), ==, 0);
 
4701
 
 
4702
  bool quit_now = false;
 
4703
  buffer password = {};
 
4704
  bool mandos_client_exited = false;
 
4705
  bool password_is_read = false;
 
4706
  __attribute__((cleanup(cleanup_queue)))
 
4707
    task_queue *queue = create_queue();
 
4708
  g_assert_nonnull(queue);
 
4709
 
 
4710
  task_context task = {
 
4711
    .func=read_inotify_event,
 
4712
    .epoll_fd=epoll_fd,
 
4713
    .fd=pipefds[0],
 
4714
    .quit_now=&quit_now,
 
4715
    .password=&password,
 
4716
    .filename=strdup("/nonexistent"),
 
4717
    .cancelled_filenames=&cancelled_filenames,
 
4718
    .current_time=&current_time,
 
4719
    .mandos_client_exited=&mandos_client_exited,
 
4720
    .password_is_read=&password_is_read,
 
4721
  };
 
4722
  task.func(task, queue);
 
4723
  g_assert_false(quit_now);
 
4724
  g_assert_true(queue->next_run == 0);
 
4725
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
 
4726
 
 
4727
  g_assert_nonnull(find_matching_task(queue, (task_context){
 
4728
        .func=read_inotify_event,
 
4729
        .epoll_fd=epoll_fd,
 
4730
        .fd=pipefds[0],
 
4731
        .quit_now=&quit_now,
 
4732
        .password=&password,
 
4733
        .filename=task.filename,
 
4734
        .cancelled_filenames=&cancelled_filenames,
 
4735
        .current_time=&current_time,
 
4736
        .mandos_client_exited=&mandos_client_exited,
 
4737
        .password_is_read=&password_is_read,
 
4738
      }));
 
4739
 
 
4740
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
 
4741
                                   EPOLLIN | EPOLLRDHUP));
 
4742
 
 
4743
  __attribute__((cleanup(cleanup_string)))
 
4744
    char *filename = NULL;
 
4745
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
 
4746
                           dummy_file_name), >, 0);
 
4747
  g_assert_nonnull(filename);
 
4748
  g_assert_false(string_set_contains(cancelled_filenames, filename));
 
4749
}
 
4750
 
4333
4751
static
4334
4752
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4335
4753
                                               test_fixture *fixture,
4350
4768
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4351
4769
                                  + NAME_MAX + 1);
4352
4770
  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);
 
4771
  struct {
 
4772
    struct inotify_event event;
 
4773
    char name_buffer[NAME_MAX + 1];
 
4774
  } ievent_buffer;
 
4775
  struct inotify_event *const ievent = &ievent_buffer.event;
4356
4776
 
4357
4777
  const char dummy_file_name[] = "ignored.dummy_file_name";
4358
4778
  ievent->mask = IN_DELETE;
4360
4780
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4361
4781
  const size_t ievent_size = (sizeof(struct inotify_event)
4362
4782
                              + sizeof(dummy_file_name));
4363
 
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
 
4783
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4364
4784
                  ==, ievent_size);
4365
4785
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4366
4786
 
5253
5673
                                            __attribute__((unused))
5254
5674
                                            gconstpointer user_data){
5255
5675
  __attribute__((cleanup(cleanup_close)))
5256
 
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
 
5676
    const int epoll_fd = open("/dev/null",
 
5677
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
5257
5678
  __attribute__((cleanup(cleanup_string)))
5258
5679
    char *const question_filename = strdup("/nonexistent/question");
5259
5680
  g_assert_nonnull(question_filename);
5388
5809
  char write_data[PIPE_BUF];
5389
5810
  {
5390
5811
    /* Construct test password buffer */
5391
 
    /* Start with + since that is what the real procotol uses */
 
5812
    /* Start with + since that is what the real protocol uses */
5392
5813
    write_data[0] = '+';
5393
5814
    /* Set a special character at string end just to mark the end */
5394
5815
    write_data[sizeof(write_data)-2] = 'y';
5546
5967
  char *const filename = strdup("/nonexistent/socket");
5547
5968
  __attribute__((cleanup(string_set_clear)))
5548
5969
    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,
 
5970
  int socketfds[2];
 
5971
 
 
5972
  /* Find a message size which triggers EMSGSIZE */
 
5973
  __attribute__((cleanup(cleanup_string)))
 
5974
    char *message_buffer = NULL;
 
5975
  size_t message_size = PIPE_BUF + 1;
 
5976
  for(ssize_t ssret = 0; ssret >= 0; message_size += 1024){
 
5977
    if(message_size >= 1024*1024*1024){ /* 1 GiB */
 
5978
      g_test_skip("Skipping EMSGSIZE test: Will not try 1GiB");
 
5979
      return;
 
5980
    }
 
5981
    free(message_buffer);
 
5982
    message_buffer = malloc(message_size);
 
5983
    if(message_buffer == NULL){
 
5984
      g_test_skip("Skipping EMSGSIZE test");
 
5985
      g_test_message("Failed to malloc() %" PRIuMAX " bytes",
 
5986
                     (uintmax_t)message_size);
 
5987
      return;
 
5988
    }
 
5989
    /* Fill buffer with 'x' */
 
5990
    memset(message_buffer, 'x', message_size);
 
5991
    /* Create a new socketpair for each message size to avoid having
 
5992
       to empty the pipe by reading the message to a separate buffer
 
5993
    */
 
5994
    g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
5995
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
5996
                               socketfds), ==, 0);
 
5997
    ssret = send(socketfds[1], message_buffer, message_size,
 
5998
                 MSG_NOSIGNAL);
 
5999
    error_t saved_errno = errno;
 
6000
    g_assert_cmpint(close(socketfds[0]), ==, 0);
 
6001
    g_assert_cmpint(close(socketfds[1]), ==, 0);
 
6002
 
 
6003
    if(ssret < 0){
 
6004
      if(saved_errno != EMSGSIZE) {
 
6005
        g_test_skip("Skipping EMSGSIZE test");
 
6006
        g_test_message("Error on send(): %s", strerror(saved_errno));
 
6007
        return;
 
6008
      }
 
6009
      break;
 
6010
    } else if(ssret != (ssize_t)message_size){
 
6011
      g_test_skip("Skipping EMSGSIZE test");
 
6012
      g_test_message("Partial send(): %" PRIuMAX " of %" PRIdMAX
 
6013
                     " bytes", (uintmax_t)ssret,
 
6014
                     (intmax_t)message_size);
 
6015
      return;
 
6016
    }
 
6017
  }
 
6018
  g_test_message("EMSGSIZE triggered by %" PRIdMAX " bytes",
 
6019
                 (intmax_t)message_size);
 
6020
 
 
6021
  buffer password = {
 
6022
    .data=message_buffer,
 
6023
    .length=message_size - 2,   /* Compensate for added '+' and NUL */
 
6024
    .allocated=message_size,
5555
6025
  };
5556
 
  g_assert_nonnull(password.data);
5557
6026
  if(mlock(password.data, password.allocated) != 0){
5558
6027
    g_assert_true(errno == EPERM or errno == ENOMEM);
5559
6028
  }
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
6029
 
5570
6030
  __attribute__((cleanup(cleanup_queue)))
5571
6031
    task_queue *queue = create_queue();
5572
6032
  g_assert_nonnull(queue);
5573
 
  int socketfds[2];
5574
6033
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5575
6034
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5576
6035
                             socketfds), ==, 0);
5663
6122
                                            __attribute__((unused))
5664
6123
                                            gconstpointer user_data){
5665
6124
  __attribute__((cleanup(cleanup_close)))
5666
 
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
 
6125
    const int epoll_fd = open("/dev/null",
 
6126
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
5667
6127
  __attribute__((cleanup(cleanup_string)))
5668
6128
    char *const question_filename = strdup("/nonexistent/question");
5669
6129
  g_assert_nonnull(question_filename);
5932
6392
                                              const char *const
5933
6393
                                              dirname){
5934
6394
  __attribute__((cleanup(cleanup_close)))
5935
 
    const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
 
6395
    const int devnull_fd = open("/dev/null",
 
6396
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
5936
6397
  g_assert_cmpint(devnull_fd, >=, 0);
5937
6398
  __attribute__((cleanup(cleanup_close)))
5938
6399
    const int real_stderr_fd = dup(STDERR_FILENO);
7481
7942
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7482
7943
  test_add("/queue/create", test_create_queue);
7483
7944
  test_add("/queue/add", test_add_to_queue);
 
7945
  test_add("/queue/add/overflow", test_add_to_queue_overflow);
7484
7946
  test_add("/queue/has_question/empty",
7485
7947
           test_queue_has_question_empty);
7486
7948
  test_add("/queue/has_question/false",
7573
8035
              test_add_inotify_dir_watch);
7574
8036
  test_add_st("/task-creators/add_inotify_dir_watch/fail",
7575
8037
              test_add_inotify_dir_watch_fail);
 
8038
  test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory",
 
8039
              test_add_inotify_dir_watch_nondir);
7576
8040
  test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
7577
8041
              test_add_inotify_dir_watch_EAGAIN);
7578
8042
  test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
7579
8043
              test_add_inotify_dir_watch_IN_CLOSE_WRITE);
7580
8044
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
7581
8045
              test_add_inotify_dir_watch_IN_MOVED_TO);
 
8046
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
 
8047
              test_add_inotify_dir_watch_IN_MOVED_FROM);
 
8048
  test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
 
8049
              test_add_inotify_dir_watch_IN_EXCL_UNLINK);
7582
8050
  test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
7583
8051
              test_add_inotify_dir_watch_IN_DELETE);
7584
8052
  test_add_st("/task/read_inotify_event/readerror",
7593
8061
              test_read_inotify_event_IN_CLOSE_WRITE);
7594
8062
  test_add_st("/task/read_inotify_event/IN_MOVED_TO",
7595
8063
              test_read_inotify_event_IN_MOVED_TO);
 
8064
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
 
8065
              test_read_inotify_event_IN_MOVED_FROM);
7596
8066
  test_add_st("/task/read_inotify_event/IN_DELETE",
7597
8067
              test_read_inotify_event_IN_DELETE);
7598
8068
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
7599
8069
              test_read_inotify_event_IN_CLOSE_WRITE_badname);
7600
8070
  test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
7601
8071
              test_read_inotify_event_IN_MOVED_TO_badname);
 
8072
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
 
8073
              test_read_inotify_event_IN_MOVED_FROM_badname);
7602
8074
  test_add_st("/task/read_inotify_event/IN_DELETE/badname",
7603
8075
              test_read_inotify_event_IN_DELETE_badname);
7604
8076
  test_add_st("/task/open_and_parse_question/ENOENT",
7701
8173
  g_option_context_set_help_enabled(context, FALSE);
7702
8174
  g_option_context_set_ignore_unknown_options(context, TRUE);
7703
8175
 
7704
 
  gboolean run_tests = FALSE;
 
8176
  gboolean should_run_tests = FALSE;
7705
8177
  GOptionEntry entries[] = {
7706
8178
    { "test", 0, 0, G_OPTION_ARG_NONE,
7707
 
      &run_tests, "Run tests", NULL },
 
8179
      &should_run_tests, "Run tests", NULL },
7708
8180
    { NULL }
7709
8181
  };
7710
8182
  g_option_context_add_main_entries(context, entries, NULL);
7717
8189
  }
7718
8190
 
7719
8191
  g_option_context_free(context);
7720
 
  return run_tests != FALSE;
 
8192
  return should_run_tests != FALSE;
7721
8193
}