/mandos/release

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

« back to all changes in this revision

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

  • Committer: Teddy Hogeborn
  • Date: 2019-08-02 22:16:53 UTC
  • mto: This revision was merged to the branch mainline in revision 386.
  • Revision ID: teddy@recompile.se-20190802221653-ic1iko9hbefzwsk7
Fix bug in server Debian package: Fails to start on first install

There has been a very long-standing bug where installation of the
server (the "mandos" Debian package) would fail to start the server
properly right after installation.  It would work on manual (re)start
after installation, or after reboot, and even after package purge and
reinstall, it would then work the first time.  The problem, it turns
out, is when the new "_mandos" user (and corresponding group) is
created, the D-Bus server is not reloaded, and is therefore not aware
of that user, and does not recognize the user and group name in the
/etc/dbus-1/system.d/mandos.conf file.  The Mandos server, when it
tries to start and access the D-Bus, is then not permitted to connect
to its D-Bus bus name, and disables D-Bus use as a fallback measure;
i.e. the server works, but it is not controllable via D-Bus commands
(via mandos-ctl or mandos-monitor).  The next time the D-Bus daemon is
reloaded for any reason, the new user & group would become visible to
the D-Bus daemon and after that, any restart of the Mandos server
would succeed and it would bind to its D-Bus name properly, and
thereby be visible and controllable by mandos-ctl & mandos-monitor.
This was mostly invisible when using sysvinit, but systemd makes the
problem visible since the systemd service file for the Mandos server
is configured to not consider the Mandos server "started" until the
D-Bus name has been bound; this makes the starting of the service wait
for 90 seconds and then fail with a timeout error.

Fixing this should also make the Debian CI autopkgtest tests work.

* debian/mandos.postinst (configure): After creating (or renaming)
                                      user & group, reload D-Bus
                                      daemon (if present).

Show diffs side-by-side

added added

removed removed

Lines of Context:
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, ENOTDIR,
52
 
                                   EEXIST, ECHILD, EPERM, ENOMEM,
53
 
                                   EAGAIN, EINTR, ENOBUFS, EADDRINUSE,
 
51
                                   ENAMETOOLONG, ENOENT, EEXIST,
 
52
                                   ECHILD, EPERM, ENOMEM, EAGAIN,
 
53
                                   EINTR, ENOBUFS, EADDRINUSE,
54
54
                                   ECONNREFUSED, ECONNRESET,
55
55
                                   ETOOMANYREFS, EMSGSIZE, EBADF,
56
56
                                   EINVAL */
83
83
#include <sys/mman.h>           /* munlock(), mlock() */
84
84
#include <fcntl.h>              /* O_CLOEXEC, O_NONBLOCK, fcntl(),
85
85
                                   F_GETFD, F_GETFL, FD_CLOEXEC,
86
 
                                   open(), O_WRONLY, O_NOCTTY,
87
 
                                   O_RDONLY, O_NOFOLLOW */
 
86
                                   open(), O_WRONLY, O_RDONLY */
88
87
#include <sys/wait.h>           /* waitpid(), WNOHANG, WIFEXITED(),
89
88
                                   WEXITSTATUS() */
90
89
#include <limits.h>             /* PIPE_BUF, NAME_MAX, INT_MAX */
91
90
#include <sys/inotify.h>        /* inotify_init1(), IN_NONBLOCK,
92
91
                                   IN_CLOEXEC, inotify_add_watch(),
93
92
                                   IN_CLOSE_WRITE, IN_MOVED_TO,
94
 
                                   IN_MOVED_FROM, IN_DELETE,
95
 
                                   IN_EXCL_UNLINK, IN_ONLYDIR,
96
 
                                   struct inotify_event */
 
93
                                   IN_DELETE, struct inotify_event */
97
94
#include <fnmatch.h>            /* fnmatch(), FNM_FILE_NAME */
98
95
#include <stdio.h>              /* asprintf(), FILE, fopen(),
99
96
                                   getline(), sscanf(), feof(),
148
145
  mono_microsecs next_run;
149
146
} __attribute__((designated_init)) task_queue;
150
147
 
151
 
/* "task_func" - A function type for task functions
 
148
/* "func_type" - A function type for task functions
152
149
 
153
150
   I.e. functions for the code which runs when a task is run, all have
154
151
   this type */
434
431
    case EACCES:
435
432
    case ENAMETOOLONG:
436
433
    case ENOENT:
437
 
    case ENOTDIR:
438
434
      return EX_OSFILE;
439
435
    default:
440
436
      return EX_OSERR;
1021
1017
    return false;
1022
1018
  }
1023
1019
 
1024
 
  if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO
1025
 
                       | IN_MOVED_FROM| IN_DELETE | IN_EXCL_UNLINK
1026
 
                       | IN_ONLYDIR)
 
1020
  if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE
 
1021
                       | IN_MOVED_TO | IN_DELETE)
1027
1022
     == -1){
1028
1023
    error(0, errno, "Failed to create inotify watch on %s", dir);
1029
1024
    return false;
1124
1119
             immediately */
1125
1120
          queue->next_run = 1;
1126
1121
        }
1127
 
      } else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
 
1122
      } else if(ievent->mask & IN_DELETE){
1128
1123
        if(not string_set_add(cancelled_filenames,
1129
1124
                              question_filename)){
1130
1125
          error(0, errno, "Could not add question %s to"
2227
2222
 
2228
2223
  {
2229
2224
    __attribute__((cleanup(cleanup_close)))
2230
 
      const int devnull_fd = open("/dev/null",
2231
 
                                  O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2225
      const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
2232
2226
    g_assert_cmpint(devnull_fd, >=, 0);
2233
2227
    __attribute__((cleanup(cleanup_close)))
2234
2228
      const int real_stderr_fd = dup(STDERR_FILENO);
2258
2252
    {
2259
2253
      __attribute__((cleanup(cleanup_close)))
2260
2254
        const int devnull_fd = open("/dev/null",
2261
 
                                    O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2255
                                    O_WRONLY | O_CLOEXEC);
2262
2256
      g_assert_cmpint(devnull_fd, >=, 0);
2263
2257
      __attribute__((cleanup(cleanup_close)))
2264
2258
        const int real_stderr_fd = dup(STDERR_FILENO);
2909
2903
 
2910
2904
  __attribute__((cleanup(cleanup_close)))
2911
2905
    const int devnull_fd = open("/dev/null",
2912
 
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2906
                                O_WRONLY | O_CLOEXEC);
2913
2907
  g_assert_cmpint(devnull_fd, >=, 0);
2914
2908
  __attribute__((cleanup(cleanup_close)))
2915
2909
    const int real_stderr_fd = dup(STDERR_FILENO);
2980
2974
 
2981
2975
  __attribute__((cleanup(cleanup_close)))
2982
2976
    const int devnull_fd = open("/dev/null",
2983
 
                                O_WRONLY | O_CLOEXEC, O_NOCTTY);
 
2977
                                O_WRONLY | O_CLOEXEC);
2984
2978
  g_assert_cmpint(devnull_fd, >=, 0);
2985
2979
  __attribute__((cleanup(cleanup_close)))
2986
2980
    const int real_stderr_fd = dup(STDERR_FILENO);
3024
3018
    buffer password = {};
3025
3019
 
3026
3020
  /* Reading /proc/self/mem from offset 0 will always give EIO */
3027
 
  const int fd = open("/proc/self/mem",
3028
 
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
 
3021
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
3029
3022
 
3030
3023
  bool password_is_read = false;
3031
3024
  bool quit_now = false;
3459
3452
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3460
3453
}
3461
3454
 
3462
 
static void test_add_inotify_dir_watch_nondir(__attribute__((unused))
3463
 
                                              test_fixture *fixture,
3464
 
                                            __attribute__((unused))
3465
 
                                              gconstpointer
3466
 
                                              user_data){
3467
 
  __attribute__((cleanup(cleanup_close)))
3468
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3469
 
  g_assert_cmpint(epoll_fd, >=, 0);
3470
 
  __attribute__((cleanup(cleanup_queue)))
3471
 
    task_queue *queue = create_queue();
3472
 
  g_assert_nonnull(queue);
3473
 
  __attribute__((cleanup(string_set_clear)))
3474
 
    string_set cancelled_filenames = {};
3475
 
  const mono_microsecs current_time = 0;
3476
 
 
3477
 
  bool quit_now = false;
3478
 
  buffer password = {};
3479
 
  bool mandos_client_exited = false;
3480
 
  bool password_is_read = false;
3481
 
 
3482
 
  const char not_a_directory[] = "/dev/tty";
3483
 
 
3484
 
  FILE *real_stderr = stderr;
3485
 
  FILE *devnull = fopen("/dev/null", "we");
3486
 
  g_assert_nonnull(devnull);
3487
 
  stderr = devnull;
3488
 
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3489
 
                                       &password, not_a_directory,
3490
 
                                       &cancelled_filenames,
3491
 
                                       &current_time,
3492
 
                                       &mandos_client_exited,
3493
 
                                       &password_is_read));
3494
 
  stderr = real_stderr;
3495
 
  g_assert_cmpint(fclose(devnull), ==, 0);
3496
 
 
3497
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3498
 
}
3499
 
 
3500
3455
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3501
3456
                                              test_fixture *fixture,
3502
3457
                                              __attribute__((unused))
3717
3672
}
3718
3673
 
3719
3674
static
3720
 
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
3721
 
                                              test_fixture *fixture,
3722
 
                                              __attribute__((unused))
3723
 
                                              gconstpointer
3724
 
                                              user_data){
3725
 
  __attribute__((cleanup(cleanup_close)))
3726
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3727
 
  g_assert_cmpint(epoll_fd, >=, 0);
3728
 
  __attribute__((cleanup(cleanup_queue)))
3729
 
    task_queue *queue = create_queue();
3730
 
  g_assert_nonnull(queue);
3731
 
  __attribute__((cleanup(string_set_clear)))
3732
 
    string_set cancelled_filenames = {};
3733
 
  const mono_microsecs current_time = 0;
3734
 
 
3735
 
  bool quit_now = false;
3736
 
  buffer password = {};
3737
 
  bool mandos_client_exited = false;
3738
 
  bool password_is_read = false;
3739
 
 
3740
 
  __attribute__((cleanup(cleanup_string)))
3741
 
    char *tempdir = make_temporary_directory();
3742
 
  g_assert_nonnull(tempdir);
3743
 
 
3744
 
  __attribute__((cleanup(cleanup_string)))
3745
 
    char *tempfilename = make_temporary_file_in_directory(tempdir);
3746
 
  g_assert_nonnull(tempfilename);
3747
 
 
3748
 
  __attribute__((cleanup(cleanup_string)))
3749
 
    char *targetdir = make_temporary_directory();
3750
 
  g_assert_nonnull(targetdir);
3751
 
 
3752
 
  __attribute__((cleanup(cleanup_string)))
3753
 
    char *targetfilename = NULL;
3754
 
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
3755
 
                           basename(tempfilename)), >, 0);
3756
 
  g_assert_nonnull(targetfilename);
3757
 
 
3758
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3759
 
                                      &password, tempdir,
3760
 
                                      &cancelled_filenames,
3761
 
                                      &current_time,
3762
 
                                      &mandos_client_exited,
3763
 
                                      &password_is_read));
3764
 
 
3765
 
  g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
3766
 
 
3767
 
  const task_context *const added_read_task
3768
 
    = find_matching_task(queue,
3769
 
                         (task_context){ .func=read_inotify_event });
3770
 
  g_assert_nonnull(added_read_task);
3771
 
 
3772
 
  /* "sufficient to read at least one event." - inotify(7) */
3773
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3774
 
                              + NAME_MAX + 1);
3775
 
  struct inotify_event *ievent = malloc(ievent_size);
3776
 
  g_assert_nonnull(ievent);
3777
 
 
3778
 
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3779
 
 
3780
 
  g_assert_cmpint((int)read_size, >, 0);
3781
 
  g_assert_true(ievent->mask & IN_MOVED_FROM);
3782
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
3783
 
 
3784
 
  free(ievent);
3785
 
 
3786
 
  g_assert_cmpint(unlink(targetfilename), ==, 0);
3787
 
  g_assert_cmpint(rmdir(targetdir), ==, 0);
3788
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3789
 
}
3790
 
 
3791
 
static
3792
3675
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3793
3676
                                          test_fixture *fixture,
3794
3677
                                          __attribute__((unused))
3852
3735
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3853
3736
}
3854
3737
 
3855
 
static
3856
 
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
3857
 
                                               test_fixture *fixture,
3858
 
                                               __attribute__((unused))
3859
 
                                               gconstpointer
3860
 
                                               user_data){
3861
 
  __attribute__((cleanup(cleanup_close)))
3862
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3863
 
  g_assert_cmpint(epoll_fd, >=, 0);
3864
 
  __attribute__((cleanup(cleanup_queue)))
3865
 
    task_queue *queue = create_queue();
3866
 
  g_assert_nonnull(queue);
3867
 
  __attribute__((cleanup(string_set_clear)))
3868
 
    string_set cancelled_filenames = {};
3869
 
  const mono_microsecs current_time = 0;
3870
 
 
3871
 
  bool quit_now = false;
3872
 
  buffer password = {};
3873
 
  bool mandos_client_exited = false;
3874
 
  bool password_is_read = false;
3875
 
 
3876
 
  __attribute__((cleanup(cleanup_string)))
3877
 
    char *tempdir = make_temporary_directory();
3878
 
  g_assert_nonnull(tempdir);
3879
 
 
3880
 
  __attribute__((cleanup(cleanup_string)))
3881
 
    char *tempfile = make_temporary_file_in_directory(tempdir);
3882
 
  g_assert_nonnull(tempfile);
3883
 
  int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
3884
 
                         | O_NOFOLLOW);
3885
 
  g_assert_cmpint(tempfile_fd, >, 2);
3886
 
 
3887
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3888
 
                                      &password, tempdir,
3889
 
                                      &cancelled_filenames,
3890
 
                                      &current_time,
3891
 
                                      &mandos_client_exited,
3892
 
                                      &password_is_read));
3893
 
  g_assert_cmpint(unlink(tempfile), ==, 0);
3894
 
 
3895
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
3896
 
 
3897
 
  const task_context *const added_read_task
3898
 
    = find_matching_task(queue,
3899
 
                         (task_context){ .func=read_inotify_event });
3900
 
  g_assert_nonnull(added_read_task);
3901
 
 
3902
 
  g_assert_cmpint(added_read_task->fd, >, 2);
3903
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3904
 
 
3905
 
  /* "sufficient to read at least one event." - inotify(7) */
3906
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3907
 
                              + NAME_MAX + 1);
3908
 
  struct inotify_event *ievent = malloc(ievent_size);
3909
 
  g_assert_nonnull(ievent);
3910
 
 
3911
 
  ssize_t read_size = 0;
3912
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
3913
 
 
3914
 
  g_assert_cmpint((int)read_size, >, 0);
3915
 
  g_assert_true(ievent->mask & IN_DELETE);
3916
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3917
 
 
3918
 
  g_assert_cmpint(close(tempfile_fd), ==, 0);
3919
 
 
3920
 
  /* IN_EXCL_UNLINK should make the closing of the previously unlinked
3921
 
     file not appear as an ievent, so we should not see it now. */
3922
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
3923
 
  g_assert_cmpint((int)read_size, ==, -1);
3924
 
  g_assert_true(errno == EAGAIN);
3925
 
 
3926
 
  free(ievent);
3927
 
 
3928
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3929
 
}
3930
 
 
3931
3738
static void test_read_inotify_event_readerror(__attribute__((unused))
3932
3739
                                              test_fixture *fixture,
3933
3740
                                              __attribute__((unused))
3939
3746
  const mono_microsecs current_time = 0;
3940
3747
 
3941
3748
  /* Reading /proc/self/mem from offset 0 will always result in EIO */
3942
 
  const int fd = open("/proc/self/mem",
3943
 
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
 
3749
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
3944
3750
 
3945
3751
  bool quit_now = false;
3946
3752
  __attribute__((cleanup(cleanup_queue)))
4298
4104
      }));
4299
4105
}
4300
4106
 
4301
 
static
4302
 
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
4303
 
                                           test_fixture *fixture,
4304
 
                                           __attribute__((unused))
4305
 
                                           gconstpointer user_data){
4306
 
  __attribute__((cleanup(cleanup_close)))
4307
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4308
 
  g_assert_cmpint(epoll_fd, >=, 0);
4309
 
  __attribute__((cleanup(string_set_clear)))
4310
 
    string_set cancelled_filenames = {};
4311
 
  const mono_microsecs current_time = 0;
4312
 
 
4313
 
  int pipefds[2];
4314
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4315
 
 
4316
 
  /* "sufficient to read at least one event." - inotify(7) */
4317
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4318
 
                                  + NAME_MAX + 1);
4319
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4320
 
  struct {
4321
 
    struct inotify_event event;
4322
 
    char name_buffer[NAME_MAX + 1];
4323
 
  } ievent_buffer;
4324
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4325
 
 
4326
 
  const char dummy_file_name[] = "ask.dummy_file_name";
4327
 
  ievent->mask = IN_MOVED_FROM;
4328
 
  ievent->len = sizeof(dummy_file_name);
4329
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4330
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4331
 
                              + sizeof(dummy_file_name));
4332
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4333
 
                  ==, ievent_size);
4334
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4335
 
 
4336
 
  bool quit_now = false;
4337
 
  buffer password = {};
4338
 
  bool mandos_client_exited = false;
4339
 
  bool password_is_read = false;
4340
 
  __attribute__((cleanup(cleanup_queue)))
4341
 
    task_queue *queue = create_queue();
4342
 
  g_assert_nonnull(queue);
4343
 
 
4344
 
  task_context task = {
4345
 
    .func=read_inotify_event,
4346
 
    .epoll_fd=epoll_fd,
4347
 
    .fd=pipefds[0],
4348
 
    .quit_now=&quit_now,
4349
 
    .password=&password,
4350
 
    .filename=strdup("/nonexistent"),
4351
 
    .cancelled_filenames=&cancelled_filenames,
4352
 
    .current_time=&current_time,
4353
 
    .mandos_client_exited=&mandos_client_exited,
4354
 
    .password_is_read=&password_is_read,
4355
 
  };
4356
 
  task.func(task, queue);
4357
 
  g_assert_false(quit_now);
4358
 
  g_assert_true(queue->next_run == 0);
4359
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4360
 
 
4361
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4362
 
        .func=read_inotify_event,
4363
 
        .epoll_fd=epoll_fd,
4364
 
        .fd=pipefds[0],
4365
 
        .quit_now=&quit_now,
4366
 
        .password=&password,
4367
 
        .filename=task.filename,
4368
 
        .cancelled_filenames=&cancelled_filenames,
4369
 
        .current_time=&current_time,
4370
 
        .mandos_client_exited=&mandos_client_exited,
4371
 
        .password_is_read=&password_is_read,
4372
 
      }));
4373
 
 
4374
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4375
 
                                   EPOLLIN | EPOLLRDHUP));
4376
 
 
4377
 
  __attribute__((cleanup(cleanup_string)))
4378
 
    char *filename = NULL;
4379
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4380
 
                           dummy_file_name), >, 0);
4381
 
  g_assert_nonnull(filename);
4382
 
  g_assert_true(string_set_contains(*task.cancelled_filenames,
4383
 
                                    filename));
4384
 
}
4385
 
 
4386
4107
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4387
4108
                                              test_fixture *fixture,
4388
4109
                                              __attribute__((unused))
4621
4342
                                   EPOLLIN | EPOLLRDHUP));
4622
4343
}
4623
4344
 
4624
 
static void
4625
 
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
4626
 
                                              test_fixture *fixture,
4627
 
                                              __attribute__((unused))
4628
 
                                              gconstpointer
4629
 
                                              user_data){
4630
 
  __attribute__((cleanup(cleanup_close)))
4631
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4632
 
  g_assert_cmpint(epoll_fd, >=, 0);
4633
 
  __attribute__((cleanup(string_set_clear)))
4634
 
    string_set cancelled_filenames = {};
4635
 
  const mono_microsecs current_time = 0;
4636
 
 
4637
 
  int pipefds[2];
4638
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4639
 
 
4640
 
  /* "sufficient to read at least one event." - inotify(7) */
4641
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4642
 
                                  + NAME_MAX + 1);
4643
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4644
 
  struct {
4645
 
    struct inotify_event event;
4646
 
    char name_buffer[NAME_MAX + 1];
4647
 
  } ievent_buffer;
4648
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4649
 
 
4650
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
4651
 
  ievent->mask = IN_MOVED_FROM;
4652
 
  ievent->len = sizeof(dummy_file_name);
4653
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4654
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4655
 
                              + sizeof(dummy_file_name));
4656
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4657
 
                  ==, ievent_size);
4658
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4659
 
 
4660
 
  bool quit_now = false;
4661
 
  buffer password = {};
4662
 
  bool mandos_client_exited = false;
4663
 
  bool password_is_read = false;
4664
 
  __attribute__((cleanup(cleanup_queue)))
4665
 
    task_queue *queue = create_queue();
4666
 
  g_assert_nonnull(queue);
4667
 
 
4668
 
  task_context task = {
4669
 
    .func=read_inotify_event,
4670
 
    .epoll_fd=epoll_fd,
4671
 
    .fd=pipefds[0],
4672
 
    .quit_now=&quit_now,
4673
 
    .password=&password,
4674
 
    .filename=strdup("/nonexistent"),
4675
 
    .cancelled_filenames=&cancelled_filenames,
4676
 
    .current_time=&current_time,
4677
 
    .mandos_client_exited=&mandos_client_exited,
4678
 
    .password_is_read=&password_is_read,
4679
 
  };
4680
 
  task.func(task, queue);
4681
 
  g_assert_false(quit_now);
4682
 
  g_assert_true(queue->next_run == 0);
4683
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4684
 
 
4685
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4686
 
        .func=read_inotify_event,
4687
 
        .epoll_fd=epoll_fd,
4688
 
        .fd=pipefds[0],
4689
 
        .quit_now=&quit_now,
4690
 
        .password=&password,
4691
 
        .filename=task.filename,
4692
 
        .cancelled_filenames=&cancelled_filenames,
4693
 
        .current_time=&current_time,
4694
 
        .mandos_client_exited=&mandos_client_exited,
4695
 
        .password_is_read=&password_is_read,
4696
 
      }));
4697
 
 
4698
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4699
 
                                   EPOLLIN | EPOLLRDHUP));
4700
 
 
4701
 
  __attribute__((cleanup(cleanup_string)))
4702
 
    char *filename = NULL;
4703
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4704
 
                           dummy_file_name), >, 0);
4705
 
  g_assert_nonnull(filename);
4706
 
  g_assert_false(string_set_contains(cancelled_filenames, filename));
4707
 
}
4708
 
 
4709
4345
static
4710
4346
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4711
4347
                                               test_fixture *fixture,
5631
5267
                                            __attribute__((unused))
5632
5268
                                            gconstpointer user_data){
5633
5269
  __attribute__((cleanup(cleanup_close)))
5634
 
    const int epoll_fd = open("/dev/null",
5635
 
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
5270
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
5636
5271
  __attribute__((cleanup(cleanup_string)))
5637
5272
    char *const question_filename = strdup("/nonexistent/question");
5638
5273
  g_assert_nonnull(question_filename);
6042
5677
                                            __attribute__((unused))
6043
5678
                                            gconstpointer user_data){
6044
5679
  __attribute__((cleanup(cleanup_close)))
6045
 
    const int epoll_fd = open("/dev/null",
6046
 
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
5680
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
6047
5681
  __attribute__((cleanup(cleanup_string)))
6048
5682
    char *const question_filename = strdup("/nonexistent/question");
6049
5683
  g_assert_nonnull(question_filename);
6312
5946
                                              const char *const
6313
5947
                                              dirname){
6314
5948
  __attribute__((cleanup(cleanup_close)))
6315
 
    const int devnull_fd = open("/dev/null",
6316
 
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
5949
    const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
6317
5950
  g_assert_cmpint(devnull_fd, >=, 0);
6318
5951
  __attribute__((cleanup(cleanup_close)))
6319
5952
    const int real_stderr_fd = dup(STDERR_FILENO);
7954
7587
              test_add_inotify_dir_watch);
7955
7588
  test_add_st("/task-creators/add_inotify_dir_watch/fail",
7956
7589
              test_add_inotify_dir_watch_fail);
7957
 
  test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory",
7958
 
              test_add_inotify_dir_watch_nondir);
7959
7590
  test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
7960
7591
              test_add_inotify_dir_watch_EAGAIN);
7961
7592
  test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
7962
7593
              test_add_inotify_dir_watch_IN_CLOSE_WRITE);
7963
7594
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
7964
7595
              test_add_inotify_dir_watch_IN_MOVED_TO);
7965
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
7966
 
              test_add_inotify_dir_watch_IN_MOVED_FROM);
7967
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
7968
 
              test_add_inotify_dir_watch_IN_EXCL_UNLINK);
7969
7596
  test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
7970
7597
              test_add_inotify_dir_watch_IN_DELETE);
7971
7598
  test_add_st("/task/read_inotify_event/readerror",
7980
7607
              test_read_inotify_event_IN_CLOSE_WRITE);
7981
7608
  test_add_st("/task/read_inotify_event/IN_MOVED_TO",
7982
7609
              test_read_inotify_event_IN_MOVED_TO);
7983
 
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
7984
 
              test_read_inotify_event_IN_MOVED_FROM);
7985
7610
  test_add_st("/task/read_inotify_event/IN_DELETE",
7986
7611
              test_read_inotify_event_IN_DELETE);
7987
7612
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
7988
7613
              test_read_inotify_event_IN_CLOSE_WRITE_badname);
7989
7614
  test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
7990
7615
              test_read_inotify_event_IN_MOVED_TO_badname);
7991
 
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
7992
 
              test_read_inotify_event_IN_MOVED_FROM_badname);
7993
7616
  test_add_st("/task/read_inotify_event/IN_DELETE/badname",
7994
7617
              test_read_inotify_event_IN_DELETE_badname);
7995
7618
  test_add_st("/task/open_and_parse_question/ENOENT",