/mandos/trunk

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

« back to all changes in this revision

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

  • Committer: Teddy Hogeborn
  • Date: 2019-08-02 22:16:53 UTC
  • 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
 
                                   ENOMEM, EEXIST, ECHILD, EPERM,
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 */
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 */
77
76
#include <unistd.h>             /* uid_t, gid_t, close(), pipe2(),
78
77
                                   fork(), _exit(), dup2(),
79
78
                                   STDOUT_FILENO, setresgid(),
84
83
#include <sys/mman.h>           /* munlock(), mlock() */
85
84
#include <fcntl.h>              /* O_CLOEXEC, O_NONBLOCK, fcntl(),
86
85
                                   F_GETFD, F_GETFL, FD_CLOEXEC,
87
 
                                   open(), O_WRONLY, O_NOCTTY,
88
 
                                   O_RDONLY, O_NOFOLLOW */
 
86
                                   open(), O_WRONLY, O_RDONLY */
89
87
#include <sys/wait.h>           /* waitpid(), WNOHANG, WIFEXITED(),
90
88
                                   WEXITSTATUS() */
91
89
#include <limits.h>             /* PIPE_BUF, NAME_MAX, INT_MAX */
92
90
#include <sys/inotify.h>        /* inotify_init1(), IN_NONBLOCK,
93
91
                                   IN_CLOEXEC, inotify_add_watch(),
94
92
                                   IN_CLOSE_WRITE, IN_MOVED_TO,
95
 
                                   IN_MOVED_FROM, IN_DELETE,
96
 
                                   IN_EXCL_UNLINK, IN_ONLYDIR,
97
 
                                   struct inotify_event */
 
93
                                   IN_DELETE, struct inotify_event */
98
94
#include <fnmatch.h>            /* fnmatch(), FNM_FILE_NAME */
99
 
#include <stdio.h>              /* asprintf(), FILE, stderr, fopen(),
100
 
                                   fclose(), getline(), sscanf(),
101
 
                                   feof(), ferror(), rename(),
102
 
                                   fdopen(), fprintf(), fscanf() */
 
95
#include <stdio.h>              /* asprintf(), FILE, fopen(),
 
96
                                   getline(), sscanf(), feof(),
 
97
                                   ferror(), fclose(), stderr,
 
98
                                   rename(), fdopen(), fprintf(),
 
99
                                   fscanf() */
103
100
#include <glib.h>    /* GKeyFile, g_key_file_free(), g_key_file_new(),
104
101
                        GError, g_key_file_load_from_file(),
105
102
                        G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
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;
651
647
 
652
648
__attribute__((nonnull, warn_unused_result))
653
649
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
 
  }
661
650
  const size_t needed_size = sizeof(task_context)*(queue->length + 1);
662
651
  if(needed_size > (queue->allocated)){
663
652
    task_context *const new_tasks = realloc(queue->tasks,
1028
1017
    return false;
1029
1018
  }
1030
1019
 
1031
 
  if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO
1032
 
                       | IN_MOVED_FROM| IN_DELETE | IN_EXCL_UNLINK
1033
 
                       | IN_ONLYDIR)
 
1020
  if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE
 
1021
                       | IN_MOVED_TO | IN_DELETE)
1034
1022
     == -1){
1035
1023
    error(0, errno, "Failed to create inotify watch on %s", dir);
1036
1024
    return false;
1131
1119
             immediately */
1132
1120
          queue->next_run = 1;
1133
1121
        }
1134
 
      } else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
 
1122
      } else if(ievent->mask & IN_DELETE){
1135
1123
        if(not string_set_add(cancelled_filenames,
1136
1124
                              question_filename)){
1137
1125
          error(0, errno, "Could not add question %s to"
1891
1879
  g_assert_true(queue->tasks[0].func == dummy_func);
1892
1880
}
1893
1881
 
1894
 
static void test_add_to_queue_overflow(__attribute__((unused))
1895
 
                                       test_fixture *fixture,
1896
 
                                       __attribute__((unused))
1897
 
                                       gconstpointer user_data){
1898
 
  __attribute__((cleanup(cleanup_queue)))
1899
 
    task_queue *queue = create_queue();
1900
 
  g_assert_nonnull(queue);
1901
 
  g_assert_true(queue->length == 0);
1902
 
  queue->length = SIZE_MAX / sizeof(task_context); /* fake max size */
1903
 
 
1904
 
  FILE *real_stderr = stderr;
1905
 
  FILE *devnull = fopen("/dev/null", "we");
1906
 
  g_assert_nonnull(devnull);
1907
 
  stderr = devnull;
1908
 
  const bool ret = add_to_queue(queue,
1909
 
                                (task_context){ .func=dummy_func });
1910
 
  g_assert_true(errno == ENOMEM);
1911
 
  g_assert_false(ret);
1912
 
  stderr = real_stderr;
1913
 
  g_assert_cmpint(fclose(devnull), ==, 0);
1914
 
  queue->length = 0;            /* Restore real size */
1915
 
}
1916
 
 
1917
1882
static void dummy_func(__attribute__((unused))
1918
1883
                       const task_context task,
1919
1884
                       __attribute__((unused))
2257
2222
 
2258
2223
  {
2259
2224
    __attribute__((cleanup(cleanup_close)))
2260
 
      const int devnull_fd = open("/dev/null",
2261
 
                                  O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2225
      const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
2262
2226
    g_assert_cmpint(devnull_fd, >=, 0);
2263
2227
    __attribute__((cleanup(cleanup_close)))
2264
2228
      const int real_stderr_fd = dup(STDERR_FILENO);
2288
2252
    {
2289
2253
      __attribute__((cleanup(cleanup_close)))
2290
2254
        const int devnull_fd = open("/dev/null",
2291
 
                                    O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2255
                                    O_WRONLY | O_CLOEXEC);
2292
2256
      g_assert_cmpint(devnull_fd, >=, 0);
2293
2257
      __attribute__((cleanup(cleanup_close)))
2294
2258
        const int real_stderr_fd = dup(STDERR_FILENO);
2939
2903
 
2940
2904
  __attribute__((cleanup(cleanup_close)))
2941
2905
    const int devnull_fd = open("/dev/null",
2942
 
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2906
                                O_WRONLY | O_CLOEXEC);
2943
2907
  g_assert_cmpint(devnull_fd, >=, 0);
2944
2908
  __attribute__((cleanup(cleanup_close)))
2945
2909
    const int real_stderr_fd = dup(STDERR_FILENO);
3010
2974
 
3011
2975
  __attribute__((cleanup(cleanup_close)))
3012
2976
    const int devnull_fd = open("/dev/null",
3013
 
                                O_WRONLY | O_CLOEXEC, O_NOCTTY);
 
2977
                                O_WRONLY | O_CLOEXEC);
3014
2978
  g_assert_cmpint(devnull_fd, >=, 0);
3015
2979
  __attribute__((cleanup(cleanup_close)))
3016
2980
    const int real_stderr_fd = dup(STDERR_FILENO);
3054
3018
    buffer password = {};
3055
3019
 
3056
3020
  /* Reading /proc/self/mem from offset 0 will always give EIO */
3057
 
  const int fd = open("/proc/self/mem",
3058
 
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
 
3021
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
3059
3022
 
3060
3023
  bool password_is_read = false;
3061
3024
  bool quit_now = false;
3489
3452
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3490
3453
}
3491
3454
 
3492
 
static void test_add_inotify_dir_watch_nondir(__attribute__((unused))
3493
 
                                              test_fixture *fixture,
3494
 
                                            __attribute__((unused))
3495
 
                                              gconstpointer
3496
 
                                              user_data){
3497
 
  __attribute__((cleanup(cleanup_close)))
3498
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3499
 
  g_assert_cmpint(epoll_fd, >=, 0);
3500
 
  __attribute__((cleanup(cleanup_queue)))
3501
 
    task_queue *queue = create_queue();
3502
 
  g_assert_nonnull(queue);
3503
 
  __attribute__((cleanup(string_set_clear)))
3504
 
    string_set cancelled_filenames = {};
3505
 
  const mono_microsecs current_time = 0;
3506
 
 
3507
 
  bool quit_now = false;
3508
 
  buffer password = {};
3509
 
  bool mandos_client_exited = false;
3510
 
  bool password_is_read = false;
3511
 
 
3512
 
  const char not_a_directory[] = "/dev/tty";
3513
 
 
3514
 
  FILE *real_stderr = stderr;
3515
 
  FILE *devnull = fopen("/dev/null", "we");
3516
 
  g_assert_nonnull(devnull);
3517
 
  stderr = devnull;
3518
 
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3519
 
                                       &password, not_a_directory,
3520
 
                                       &cancelled_filenames,
3521
 
                                       &current_time,
3522
 
                                       &mandos_client_exited,
3523
 
                                       &password_is_read));
3524
 
  stderr = real_stderr;
3525
 
  g_assert_cmpint(fclose(devnull), ==, 0);
3526
 
 
3527
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3528
 
}
3529
 
 
3530
3455
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3531
3456
                                              test_fixture *fixture,
3532
3457
                                              __attribute__((unused))
3747
3672
}
3748
3673
 
3749
3674
static
3750
 
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
3751
 
                                              test_fixture *fixture,
3752
 
                                              __attribute__((unused))
3753
 
                                              gconstpointer
3754
 
                                              user_data){
3755
 
  __attribute__((cleanup(cleanup_close)))
3756
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3757
 
  g_assert_cmpint(epoll_fd, >=, 0);
3758
 
  __attribute__((cleanup(cleanup_queue)))
3759
 
    task_queue *queue = create_queue();
3760
 
  g_assert_nonnull(queue);
3761
 
  __attribute__((cleanup(string_set_clear)))
3762
 
    string_set cancelled_filenames = {};
3763
 
  const mono_microsecs current_time = 0;
3764
 
 
3765
 
  bool quit_now = false;
3766
 
  buffer password = {};
3767
 
  bool mandos_client_exited = false;
3768
 
  bool password_is_read = false;
3769
 
 
3770
 
  __attribute__((cleanup(cleanup_string)))
3771
 
    char *tempdir = make_temporary_directory();
3772
 
  g_assert_nonnull(tempdir);
3773
 
 
3774
 
  __attribute__((cleanup(cleanup_string)))
3775
 
    char *tempfilename = make_temporary_file_in_directory(tempdir);
3776
 
  g_assert_nonnull(tempfilename);
3777
 
 
3778
 
  __attribute__((cleanup(cleanup_string)))
3779
 
    char *targetdir = make_temporary_directory();
3780
 
  g_assert_nonnull(targetdir);
3781
 
 
3782
 
  __attribute__((cleanup(cleanup_string)))
3783
 
    char *targetfilename = NULL;
3784
 
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
3785
 
                           basename(tempfilename)), >, 0);
3786
 
  g_assert_nonnull(targetfilename);
3787
 
 
3788
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3789
 
                                      &password, tempdir,
3790
 
                                      &cancelled_filenames,
3791
 
                                      &current_time,
3792
 
                                      &mandos_client_exited,
3793
 
                                      &password_is_read));
3794
 
 
3795
 
  g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
3796
 
 
3797
 
  const task_context *const added_read_task
3798
 
    = find_matching_task(queue,
3799
 
                         (task_context){ .func=read_inotify_event });
3800
 
  g_assert_nonnull(added_read_task);
3801
 
 
3802
 
  /* "sufficient to read at least one event." - inotify(7) */
3803
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3804
 
                              + NAME_MAX + 1);
3805
 
  struct inotify_event *ievent = malloc(ievent_size);
3806
 
  g_assert_nonnull(ievent);
3807
 
 
3808
 
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3809
 
 
3810
 
  g_assert_cmpint((int)read_size, >, 0);
3811
 
  g_assert_true(ievent->mask & IN_MOVED_FROM);
3812
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
3813
 
 
3814
 
  free(ievent);
3815
 
 
3816
 
  g_assert_cmpint(unlink(targetfilename), ==, 0);
3817
 
  g_assert_cmpint(rmdir(targetdir), ==, 0);
3818
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3819
 
}
3820
 
 
3821
 
static
3822
3675
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3823
3676
                                          test_fixture *fixture,
3824
3677
                                          __attribute__((unused))
3882
3735
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3883
3736
}
3884
3737
 
3885
 
static
3886
 
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
3887
 
                                               test_fixture *fixture,
3888
 
                                               __attribute__((unused))
3889
 
                                               gconstpointer
3890
 
                                               user_data){
3891
 
  __attribute__((cleanup(cleanup_close)))
3892
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3893
 
  g_assert_cmpint(epoll_fd, >=, 0);
3894
 
  __attribute__((cleanup(cleanup_queue)))
3895
 
    task_queue *queue = create_queue();
3896
 
  g_assert_nonnull(queue);
3897
 
  __attribute__((cleanup(string_set_clear)))
3898
 
    string_set cancelled_filenames = {};
3899
 
  const mono_microsecs current_time = 0;
3900
 
 
3901
 
  bool quit_now = false;
3902
 
  buffer password = {};
3903
 
  bool mandos_client_exited = false;
3904
 
  bool password_is_read = false;
3905
 
 
3906
 
  __attribute__((cleanup(cleanup_string)))
3907
 
    char *tempdir = make_temporary_directory();
3908
 
  g_assert_nonnull(tempdir);
3909
 
 
3910
 
  __attribute__((cleanup(cleanup_string)))
3911
 
    char *tempfile = make_temporary_file_in_directory(tempdir);
3912
 
  g_assert_nonnull(tempfile);
3913
 
  int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
3914
 
                         | O_NOFOLLOW);
3915
 
  g_assert_cmpint(tempfile_fd, >, 2);
3916
 
 
3917
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3918
 
                                      &password, tempdir,
3919
 
                                      &cancelled_filenames,
3920
 
                                      &current_time,
3921
 
                                      &mandos_client_exited,
3922
 
                                      &password_is_read));
3923
 
  g_assert_cmpint(unlink(tempfile), ==, 0);
3924
 
 
3925
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
3926
 
 
3927
 
  const task_context *const added_read_task
3928
 
    = find_matching_task(queue,
3929
 
                         (task_context){ .func=read_inotify_event });
3930
 
  g_assert_nonnull(added_read_task);
3931
 
 
3932
 
  g_assert_cmpint(added_read_task->fd, >, 2);
3933
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3934
 
 
3935
 
  /* "sufficient to read at least one event." - inotify(7) */
3936
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3937
 
                              + NAME_MAX + 1);
3938
 
  struct inotify_event *ievent = malloc(ievent_size);
3939
 
  g_assert_nonnull(ievent);
3940
 
 
3941
 
  ssize_t read_size = 0;
3942
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
3943
 
 
3944
 
  g_assert_cmpint((int)read_size, >, 0);
3945
 
  g_assert_true(ievent->mask & IN_DELETE);
3946
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3947
 
 
3948
 
  g_assert_cmpint(close(tempfile_fd), ==, 0);
3949
 
 
3950
 
  /* IN_EXCL_UNLINK should make the closing of the previously unlinked
3951
 
     file not appear as an ievent, so we should not see it now. */
3952
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
3953
 
  g_assert_cmpint((int)read_size, ==, -1);
3954
 
  g_assert_true(errno == EAGAIN);
3955
 
 
3956
 
  free(ievent);
3957
 
 
3958
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3959
 
}
3960
 
 
3961
3738
static void test_read_inotify_event_readerror(__attribute__((unused))
3962
3739
                                              test_fixture *fixture,
3963
3740
                                              __attribute__((unused))
3969
3746
  const mono_microsecs current_time = 0;
3970
3747
 
3971
3748
  /* Reading /proc/self/mem from offset 0 will always result in EIO */
3972
 
  const int fd = open("/proc/self/mem",
3973
 
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
 
3749
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
3974
3750
 
3975
3751
  bool quit_now = false;
3976
3752
  __attribute__((cleanup(cleanup_queue)))
4328
4104
      }));
4329
4105
}
4330
4106
 
4331
 
static
4332
 
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
4333
 
                                           test_fixture *fixture,
4334
 
                                           __attribute__((unused))
4335
 
                                           gconstpointer user_data){
4336
 
  __attribute__((cleanup(cleanup_close)))
4337
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4338
 
  g_assert_cmpint(epoll_fd, >=, 0);
4339
 
  __attribute__((cleanup(string_set_clear)))
4340
 
    string_set cancelled_filenames = {};
4341
 
  const mono_microsecs current_time = 0;
4342
 
 
4343
 
  int pipefds[2];
4344
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4345
 
 
4346
 
  /* "sufficient to read at least one event." - inotify(7) */
4347
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4348
 
                                  + NAME_MAX + 1);
4349
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4350
 
  struct {
4351
 
    struct inotify_event event;
4352
 
    char name_buffer[NAME_MAX + 1];
4353
 
  } ievent_buffer;
4354
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4355
 
 
4356
 
  const char dummy_file_name[] = "ask.dummy_file_name";
4357
 
  ievent->mask = IN_MOVED_FROM;
4358
 
  ievent->len = sizeof(dummy_file_name);
4359
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4360
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4361
 
                              + sizeof(dummy_file_name));
4362
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4363
 
                  ==, ievent_size);
4364
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4365
 
 
4366
 
  bool quit_now = false;
4367
 
  buffer password = {};
4368
 
  bool mandos_client_exited = false;
4369
 
  bool password_is_read = false;
4370
 
  __attribute__((cleanup(cleanup_queue)))
4371
 
    task_queue *queue = create_queue();
4372
 
  g_assert_nonnull(queue);
4373
 
 
4374
 
  task_context task = {
4375
 
    .func=read_inotify_event,
4376
 
    .epoll_fd=epoll_fd,
4377
 
    .fd=pipefds[0],
4378
 
    .quit_now=&quit_now,
4379
 
    .password=&password,
4380
 
    .filename=strdup("/nonexistent"),
4381
 
    .cancelled_filenames=&cancelled_filenames,
4382
 
    .current_time=&current_time,
4383
 
    .mandos_client_exited=&mandos_client_exited,
4384
 
    .password_is_read=&password_is_read,
4385
 
  };
4386
 
  task.func(task, queue);
4387
 
  g_assert_false(quit_now);
4388
 
  g_assert_true(queue->next_run == 0);
4389
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4390
 
 
4391
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4392
 
        .func=read_inotify_event,
4393
 
        .epoll_fd=epoll_fd,
4394
 
        .fd=pipefds[0],
4395
 
        .quit_now=&quit_now,
4396
 
        .password=&password,
4397
 
        .filename=task.filename,
4398
 
        .cancelled_filenames=&cancelled_filenames,
4399
 
        .current_time=&current_time,
4400
 
        .mandos_client_exited=&mandos_client_exited,
4401
 
        .password_is_read=&password_is_read,
4402
 
      }));
4403
 
 
4404
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4405
 
                                   EPOLLIN | EPOLLRDHUP));
4406
 
 
4407
 
  __attribute__((cleanup(cleanup_string)))
4408
 
    char *filename = NULL;
4409
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4410
 
                           dummy_file_name), >, 0);
4411
 
  g_assert_nonnull(filename);
4412
 
  g_assert_true(string_set_contains(*task.cancelled_filenames,
4413
 
                                    filename));
4414
 
}
4415
 
 
4416
4107
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4417
4108
                                              test_fixture *fixture,
4418
4109
                                              __attribute__((unused))
4651
4342
                                   EPOLLIN | EPOLLRDHUP));
4652
4343
}
4653
4344
 
4654
 
static void
4655
 
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
4656
 
                                              test_fixture *fixture,
4657
 
                                              __attribute__((unused))
4658
 
                                              gconstpointer
4659
 
                                              user_data){
4660
 
  __attribute__((cleanup(cleanup_close)))
4661
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4662
 
  g_assert_cmpint(epoll_fd, >=, 0);
4663
 
  __attribute__((cleanup(string_set_clear)))
4664
 
    string_set cancelled_filenames = {};
4665
 
  const mono_microsecs current_time = 0;
4666
 
 
4667
 
  int pipefds[2];
4668
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4669
 
 
4670
 
  /* "sufficient to read at least one event." - inotify(7) */
4671
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4672
 
                                  + NAME_MAX + 1);
4673
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4674
 
  struct {
4675
 
    struct inotify_event event;
4676
 
    char name_buffer[NAME_MAX + 1];
4677
 
  } ievent_buffer;
4678
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4679
 
 
4680
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
4681
 
  ievent->mask = IN_MOVED_FROM;
4682
 
  ievent->len = sizeof(dummy_file_name);
4683
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4684
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4685
 
                              + sizeof(dummy_file_name));
4686
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4687
 
                  ==, ievent_size);
4688
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4689
 
 
4690
 
  bool quit_now = false;
4691
 
  buffer password = {};
4692
 
  bool mandos_client_exited = false;
4693
 
  bool password_is_read = false;
4694
 
  __attribute__((cleanup(cleanup_queue)))
4695
 
    task_queue *queue = create_queue();
4696
 
  g_assert_nonnull(queue);
4697
 
 
4698
 
  task_context task = {
4699
 
    .func=read_inotify_event,
4700
 
    .epoll_fd=epoll_fd,
4701
 
    .fd=pipefds[0],
4702
 
    .quit_now=&quit_now,
4703
 
    .password=&password,
4704
 
    .filename=strdup("/nonexistent"),
4705
 
    .cancelled_filenames=&cancelled_filenames,
4706
 
    .current_time=&current_time,
4707
 
    .mandos_client_exited=&mandos_client_exited,
4708
 
    .password_is_read=&password_is_read,
4709
 
  };
4710
 
  task.func(task, queue);
4711
 
  g_assert_false(quit_now);
4712
 
  g_assert_true(queue->next_run == 0);
4713
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4714
 
 
4715
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4716
 
        .func=read_inotify_event,
4717
 
        .epoll_fd=epoll_fd,
4718
 
        .fd=pipefds[0],
4719
 
        .quit_now=&quit_now,
4720
 
        .password=&password,
4721
 
        .filename=task.filename,
4722
 
        .cancelled_filenames=&cancelled_filenames,
4723
 
        .current_time=&current_time,
4724
 
        .mandos_client_exited=&mandos_client_exited,
4725
 
        .password_is_read=&password_is_read,
4726
 
      }));
4727
 
 
4728
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4729
 
                                   EPOLLIN | EPOLLRDHUP));
4730
 
 
4731
 
  __attribute__((cleanup(cleanup_string)))
4732
 
    char *filename = NULL;
4733
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4734
 
                           dummy_file_name), >, 0);
4735
 
  g_assert_nonnull(filename);
4736
 
  g_assert_false(string_set_contains(cancelled_filenames, filename));
4737
 
}
4738
 
 
4739
4345
static
4740
4346
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4741
4347
                                               test_fixture *fixture,
5661
5267
                                            __attribute__((unused))
5662
5268
                                            gconstpointer user_data){
5663
5269
  __attribute__((cleanup(cleanup_close)))
5664
 
    const int epoll_fd = open("/dev/null",
5665
 
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
5270
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
5666
5271
  __attribute__((cleanup(cleanup_string)))
5667
5272
    char *const question_filename = strdup("/nonexistent/question");
5668
5273
  g_assert_nonnull(question_filename);
6072
5677
                                            __attribute__((unused))
6073
5678
                                            gconstpointer user_data){
6074
5679
  __attribute__((cleanup(cleanup_close)))
6075
 
    const int epoll_fd = open("/dev/null",
6076
 
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
5680
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
6077
5681
  __attribute__((cleanup(cleanup_string)))
6078
5682
    char *const question_filename = strdup("/nonexistent/question");
6079
5683
  g_assert_nonnull(question_filename);
6342
5946
                                              const char *const
6343
5947
                                              dirname){
6344
5948
  __attribute__((cleanup(cleanup_close)))
6345
 
    const int devnull_fd = open("/dev/null",
6346
 
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
5949
    const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
6347
5950
  g_assert_cmpint(devnull_fd, >=, 0);
6348
5951
  __attribute__((cleanup(cleanup_close)))
6349
5952
    const int real_stderr_fd = dup(STDERR_FILENO);
7892
7495
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7893
7496
  test_add("/queue/create", test_create_queue);
7894
7497
  test_add("/queue/add", test_add_to_queue);
7895
 
  test_add("/queue/add/overflow", test_add_to_queue_overflow);
7896
7498
  test_add("/queue/has_question/empty",
7897
7499
           test_queue_has_question_empty);
7898
7500
  test_add("/queue/has_question/false",
7985
7587
              test_add_inotify_dir_watch);
7986
7588
  test_add_st("/task-creators/add_inotify_dir_watch/fail",
7987
7589
              test_add_inotify_dir_watch_fail);
7988
 
  test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory",
7989
 
              test_add_inotify_dir_watch_nondir);
7990
7590
  test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
7991
7591
              test_add_inotify_dir_watch_EAGAIN);
7992
7592
  test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
7993
7593
              test_add_inotify_dir_watch_IN_CLOSE_WRITE);
7994
7594
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
7995
7595
              test_add_inotify_dir_watch_IN_MOVED_TO);
7996
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
7997
 
              test_add_inotify_dir_watch_IN_MOVED_FROM);
7998
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
7999
 
              test_add_inotify_dir_watch_IN_EXCL_UNLINK);
8000
7596
  test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
8001
7597
              test_add_inotify_dir_watch_IN_DELETE);
8002
7598
  test_add_st("/task/read_inotify_event/readerror",
8011
7607
              test_read_inotify_event_IN_CLOSE_WRITE);
8012
7608
  test_add_st("/task/read_inotify_event/IN_MOVED_TO",
8013
7609
              test_read_inotify_event_IN_MOVED_TO);
8014
 
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
8015
 
              test_read_inotify_event_IN_MOVED_FROM);
8016
7610
  test_add_st("/task/read_inotify_event/IN_DELETE",
8017
7611
              test_read_inotify_event_IN_DELETE);
8018
7612
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
8019
7613
              test_read_inotify_event_IN_CLOSE_WRITE_badname);
8020
7614
  test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
8021
7615
              test_read_inotify_event_IN_MOVED_TO_badname);
8022
 
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
8023
 
              test_read_inotify_event_IN_MOVED_FROM_badname);
8024
7616
  test_add_st("/task/read_inotify_event/IN_DELETE/badname",
8025
7617
              test_read_inotify_event_IN_DELETE_badname);
8026
7618
  test_add_st("/task/open_and_parse_question/ENOENT",
8123
7715
  g_option_context_set_help_enabled(context, FALSE);
8124
7716
  g_option_context_set_ignore_unknown_options(context, TRUE);
8125
7717
 
8126
 
  gboolean should_run_tests = FALSE;
 
7718
  gboolean run_tests = FALSE;
8127
7719
  GOptionEntry entries[] = {
8128
7720
    { "test", 0, 0, G_OPTION_ARG_NONE,
8129
 
      &should_run_tests, "Run tests", NULL },
 
7721
      &run_tests, "Run tests", NULL },
8130
7722
    { NULL }
8131
7723
  };
8132
7724
  g_option_context_add_main_entries(context, entries, NULL);
8139
7731
  }
8140
7732
 
8141
7733
  g_option_context_free(context);
8142
 
  return should_run_tests != FALSE;
 
7734
  return run_tests != FALSE;
8143
7735
}