/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:
2
2
/*
3
3
 * Mandos password agent - Simple password agent to run Mandos client
4
4
 *
5
 
 * Copyright © 2019-2020 Teddy Hogeborn
6
 
 * Copyright © 2019-2020 Björn Påhlsson
 
5
 * Copyright © 2019 Teddy Hogeborn
 
6
 * Copyright © 2019 Björn Påhlsson
7
7
 * 
8
8
 * This file is part of Mandos.
9
9
 * 
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,
868
857
  }
869
858
  close(pipefds[1]);
870
859
 
871
 
  if(pid == -1){
872
 
    error(0, errno, "Failed to fork()");
873
 
    close(pipefds[0]);
874
 
    return false;
875
 
  }
876
 
 
877
860
  if(not add_to_queue(queue, (task_context){
878
861
        .func=wait_for_mandos_client_exit,
879
862
        .pid=pid,
1034
1017
    return false;
1035
1018
  }
1036
1019
 
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)
 
1020
  if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE
 
1021
                       | IN_MOVED_TO | IN_DELETE)
1040
1022
     == -1){
1041
1023
    error(0, errno, "Failed to create inotify watch on %s", dir);
1042
1024
    return false;
1137
1119
             immediately */
1138
1120
          queue->next_run = 1;
1139
1121
        }
1140
 
      } else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
 
1122
      } else if(ievent->mask & IN_DELETE){
1141
1123
        if(not string_set_add(cancelled_filenames,
1142
1124
                              question_filename)){
1143
1125
          error(0, errno, "Could not add question %s to"
1897
1879
  g_assert_true(queue->tasks[0].func == dummy_func);
1898
1880
}
1899
1881
 
1900
 
static void test_add_to_queue_overflow(__attribute__((unused))
1901
 
                                       test_fixture *fixture,
1902
 
                                       __attribute__((unused))
1903
 
                                       gconstpointer user_data){
1904
 
  __attribute__((cleanup(cleanup_queue)))
1905
 
    task_queue *queue = create_queue();
1906
 
  g_assert_nonnull(queue);
1907
 
  g_assert_true(queue->length == 0);
1908
 
  queue->length = SIZE_MAX / sizeof(task_context); /* fake max size */
1909
 
 
1910
 
  FILE *real_stderr = stderr;
1911
 
  FILE *devnull = fopen("/dev/null", "we");
1912
 
  g_assert_nonnull(devnull);
1913
 
  stderr = devnull;
1914
 
  const bool ret = add_to_queue(queue,
1915
 
                                (task_context){ .func=dummy_func });
1916
 
  g_assert_true(errno == ENOMEM);
1917
 
  g_assert_false(ret);
1918
 
  stderr = real_stderr;
1919
 
  g_assert_cmpint(fclose(devnull), ==, 0);
1920
 
  queue->length = 0;            /* Restore real size */
1921
 
}
1922
 
 
1923
1882
static void dummy_func(__attribute__((unused))
1924
1883
                       const task_context task,
1925
1884
                       __attribute__((unused))
2196
2155
    }
2197
2156
    exit(EXIT_SUCCESS);
2198
2157
  }
2199
 
  if(pid == -1){
2200
 
    error(EXIT_FAILURE, errno, "Failed to fork()");
2201
 
  }
2202
 
 
2203
2158
  int status;
2204
2159
  waitpid(pid, &status, 0);
2205
2160
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2267
2222
 
2268
2223
  {
2269
2224
    __attribute__((cleanup(cleanup_close)))
2270
 
      const int devnull_fd = open("/dev/null",
2271
 
                                  O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2225
      const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
2272
2226
    g_assert_cmpint(devnull_fd, >=, 0);
2273
2227
    __attribute__((cleanup(cleanup_close)))
2274
2228
      const int real_stderr_fd = dup(STDERR_FILENO);
2298
2252
    {
2299
2253
      __attribute__((cleanup(cleanup_close)))
2300
2254
        const int devnull_fd = open("/dev/null",
2301
 
                                    O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2255
                                    O_WRONLY | O_CLOEXEC);
2302
2256
      g_assert_cmpint(devnull_fd, >=, 0);
2303
2257
      __attribute__((cleanup(cleanup_close)))
2304
2258
        const int real_stderr_fd = dup(STDERR_FILENO);
2949
2903
 
2950
2904
  __attribute__((cleanup(cleanup_close)))
2951
2905
    const int devnull_fd = open("/dev/null",
2952
 
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2906
                                O_WRONLY | O_CLOEXEC);
2953
2907
  g_assert_cmpint(devnull_fd, >=, 0);
2954
2908
  __attribute__((cleanup(cleanup_close)))
2955
2909
    const int real_stderr_fd = dup(STDERR_FILENO);
3020
2974
 
3021
2975
  __attribute__((cleanup(cleanup_close)))
3022
2976
    const int devnull_fd = open("/dev/null",
3023
 
                                O_WRONLY | O_CLOEXEC, O_NOCTTY);
 
2977
                                O_WRONLY | O_CLOEXEC);
3024
2978
  g_assert_cmpint(devnull_fd, >=, 0);
3025
2979
  __attribute__((cleanup(cleanup_close)))
3026
2980
    const int real_stderr_fd = dup(STDERR_FILENO);
3064
3018
    buffer password = {};
3065
3019
 
3066
3020
  /* Reading /proc/self/mem from offset 0 will always give EIO */
3067
 
  const int fd = open("/proc/self/mem",
3068
 
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
 
3021
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
3069
3022
 
3070
3023
  bool password_is_read = false;
3071
3024
  bool quit_now = false;
3499
3452
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3500
3453
}
3501
3454
 
3502
 
static void test_add_inotify_dir_watch_nondir(__attribute__((unused))
3503
 
                                              test_fixture *fixture,
3504
 
                                            __attribute__((unused))
3505
 
                                              gconstpointer
3506
 
                                              user_data){
3507
 
  __attribute__((cleanup(cleanup_close)))
3508
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3509
 
  g_assert_cmpint(epoll_fd, >=, 0);
3510
 
  __attribute__((cleanup(cleanup_queue)))
3511
 
    task_queue *queue = create_queue();
3512
 
  g_assert_nonnull(queue);
3513
 
  __attribute__((cleanup(string_set_clear)))
3514
 
    string_set cancelled_filenames = {};
3515
 
  const mono_microsecs current_time = 0;
3516
 
 
3517
 
  bool quit_now = false;
3518
 
  buffer password = {};
3519
 
  bool mandos_client_exited = false;
3520
 
  bool password_is_read = false;
3521
 
 
3522
 
  const char not_a_directory[] = "/dev/tty";
3523
 
 
3524
 
  FILE *real_stderr = stderr;
3525
 
  FILE *devnull = fopen("/dev/null", "we");
3526
 
  g_assert_nonnull(devnull);
3527
 
  stderr = devnull;
3528
 
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3529
 
                                       &password, not_a_directory,
3530
 
                                       &cancelled_filenames,
3531
 
                                       &current_time,
3532
 
                                       &mandos_client_exited,
3533
 
                                       &password_is_read));
3534
 
  stderr = real_stderr;
3535
 
  g_assert_cmpint(fclose(devnull), ==, 0);
3536
 
 
3537
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3538
 
}
3539
 
 
3540
3455
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3541
3456
                                              test_fixture *fixture,
3542
3457
                                              __attribute__((unused))
3757
3672
}
3758
3673
 
3759
3674
static
3760
 
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
3761
 
                                              test_fixture *fixture,
3762
 
                                              __attribute__((unused))
3763
 
                                              gconstpointer
3764
 
                                              user_data){
3765
 
  __attribute__((cleanup(cleanup_close)))
3766
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3767
 
  g_assert_cmpint(epoll_fd, >=, 0);
3768
 
  __attribute__((cleanup(cleanup_queue)))
3769
 
    task_queue *queue = create_queue();
3770
 
  g_assert_nonnull(queue);
3771
 
  __attribute__((cleanup(string_set_clear)))
3772
 
    string_set cancelled_filenames = {};
3773
 
  const mono_microsecs current_time = 0;
3774
 
 
3775
 
  bool quit_now = false;
3776
 
  buffer password = {};
3777
 
  bool mandos_client_exited = false;
3778
 
  bool password_is_read = false;
3779
 
 
3780
 
  __attribute__((cleanup(cleanup_string)))
3781
 
    char *tempdir = make_temporary_directory();
3782
 
  g_assert_nonnull(tempdir);
3783
 
 
3784
 
  __attribute__((cleanup(cleanup_string)))
3785
 
    char *tempfilename = make_temporary_file_in_directory(tempdir);
3786
 
  g_assert_nonnull(tempfilename);
3787
 
 
3788
 
  __attribute__((cleanup(cleanup_string)))
3789
 
    char *targetdir = make_temporary_directory();
3790
 
  g_assert_nonnull(targetdir);
3791
 
 
3792
 
  __attribute__((cleanup(cleanup_string)))
3793
 
    char *targetfilename = NULL;
3794
 
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
3795
 
                           basename(tempfilename)), >, 0);
3796
 
  g_assert_nonnull(targetfilename);
3797
 
 
3798
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3799
 
                                      &password, tempdir,
3800
 
                                      &cancelled_filenames,
3801
 
                                      &current_time,
3802
 
                                      &mandos_client_exited,
3803
 
                                      &password_is_read));
3804
 
 
3805
 
  g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
3806
 
 
3807
 
  const task_context *const added_read_task
3808
 
    = find_matching_task(queue,
3809
 
                         (task_context){ .func=read_inotify_event });
3810
 
  g_assert_nonnull(added_read_task);
3811
 
 
3812
 
  /* "sufficient to read at least one event." - inotify(7) */
3813
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3814
 
                              + NAME_MAX + 1);
3815
 
  struct inotify_event *ievent = malloc(ievent_size);
3816
 
  g_assert_nonnull(ievent);
3817
 
 
3818
 
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3819
 
 
3820
 
  g_assert_cmpint((int)read_size, >, 0);
3821
 
  g_assert_true(ievent->mask & IN_MOVED_FROM);
3822
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
3823
 
 
3824
 
  free(ievent);
3825
 
 
3826
 
  g_assert_cmpint(unlink(targetfilename), ==, 0);
3827
 
  g_assert_cmpint(rmdir(targetdir), ==, 0);
3828
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3829
 
}
3830
 
 
3831
 
static
3832
3675
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3833
3676
                                          test_fixture *fixture,
3834
3677
                                          __attribute__((unused))
3892
3735
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3893
3736
}
3894
3737
 
3895
 
static
3896
 
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
3897
 
                                               test_fixture *fixture,
3898
 
                                               __attribute__((unused))
3899
 
                                               gconstpointer
3900
 
                                               user_data){
3901
 
  __attribute__((cleanup(cleanup_close)))
3902
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3903
 
  g_assert_cmpint(epoll_fd, >=, 0);
3904
 
  __attribute__((cleanup(cleanup_queue)))
3905
 
    task_queue *queue = create_queue();
3906
 
  g_assert_nonnull(queue);
3907
 
  __attribute__((cleanup(string_set_clear)))
3908
 
    string_set cancelled_filenames = {};
3909
 
  const mono_microsecs current_time = 0;
3910
 
 
3911
 
  bool quit_now = false;
3912
 
  buffer password = {};
3913
 
  bool mandos_client_exited = false;
3914
 
  bool password_is_read = false;
3915
 
 
3916
 
  __attribute__((cleanup(cleanup_string)))
3917
 
    char *tempdir = make_temporary_directory();
3918
 
  g_assert_nonnull(tempdir);
3919
 
 
3920
 
  __attribute__((cleanup(cleanup_string)))
3921
 
    char *tempfile = make_temporary_file_in_directory(tempdir);
3922
 
  g_assert_nonnull(tempfile);
3923
 
  int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
3924
 
                         | O_NOFOLLOW);
3925
 
  g_assert_cmpint(tempfile_fd, >, 2);
3926
 
 
3927
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3928
 
                                      &password, tempdir,
3929
 
                                      &cancelled_filenames,
3930
 
                                      &current_time,
3931
 
                                      &mandos_client_exited,
3932
 
                                      &password_is_read));
3933
 
  g_assert_cmpint(unlink(tempfile), ==, 0);
3934
 
 
3935
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
3936
 
 
3937
 
  const task_context *const added_read_task
3938
 
    = find_matching_task(queue,
3939
 
                         (task_context){ .func=read_inotify_event });
3940
 
  g_assert_nonnull(added_read_task);
3941
 
 
3942
 
  g_assert_cmpint(added_read_task->fd, >, 2);
3943
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3944
 
 
3945
 
  /* "sufficient to read at least one event." - inotify(7) */
3946
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3947
 
                              + NAME_MAX + 1);
3948
 
  struct inotify_event *ievent = malloc(ievent_size);
3949
 
  g_assert_nonnull(ievent);
3950
 
 
3951
 
  ssize_t read_size = 0;
3952
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
3953
 
 
3954
 
  g_assert_cmpint((int)read_size, >, 0);
3955
 
  g_assert_true(ievent->mask & IN_DELETE);
3956
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3957
 
 
3958
 
  g_assert_cmpint(close(tempfile_fd), ==, 0);
3959
 
 
3960
 
  /* IN_EXCL_UNLINK should make the closing of the previously unlinked
3961
 
     file not appear as an ievent, so we should not see it now. */
3962
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
3963
 
  g_assert_cmpint((int)read_size, ==, -1);
3964
 
  g_assert_true(errno == EAGAIN);
3965
 
 
3966
 
  free(ievent);
3967
 
 
3968
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3969
 
}
3970
 
 
3971
3738
static void test_read_inotify_event_readerror(__attribute__((unused))
3972
3739
                                              test_fixture *fixture,
3973
3740
                                              __attribute__((unused))
3979
3746
  const mono_microsecs current_time = 0;
3980
3747
 
3981
3748
  /* Reading /proc/self/mem from offset 0 will always result in EIO */
3982
 
  const int fd = open("/proc/self/mem",
3983
 
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
 
3749
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
3984
3750
 
3985
3751
  bool quit_now = false;
3986
3752
  __attribute__((cleanup(cleanup_queue)))
4338
4104
      }));
4339
4105
}
4340
4106
 
4341
 
static
4342
 
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
4343
 
                                           test_fixture *fixture,
4344
 
                                           __attribute__((unused))
4345
 
                                           gconstpointer user_data){
4346
 
  __attribute__((cleanup(cleanup_close)))
4347
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4348
 
  g_assert_cmpint(epoll_fd, >=, 0);
4349
 
  __attribute__((cleanup(string_set_clear)))
4350
 
    string_set cancelled_filenames = {};
4351
 
  const mono_microsecs current_time = 0;
4352
 
 
4353
 
  int pipefds[2];
4354
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4355
 
 
4356
 
  /* "sufficient to read at least one event." - inotify(7) */
4357
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4358
 
                                  + NAME_MAX + 1);
4359
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4360
 
  struct {
4361
 
    struct inotify_event event;
4362
 
    char name_buffer[NAME_MAX + 1];
4363
 
  } ievent_buffer;
4364
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4365
 
 
4366
 
  const char dummy_file_name[] = "ask.dummy_file_name";
4367
 
  ievent->mask = IN_MOVED_FROM;
4368
 
  ievent->len = sizeof(dummy_file_name);
4369
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4370
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4371
 
                              + sizeof(dummy_file_name));
4372
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4373
 
                  ==, ievent_size);
4374
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4375
 
 
4376
 
  bool quit_now = false;
4377
 
  buffer password = {};
4378
 
  bool mandos_client_exited = false;
4379
 
  bool password_is_read = false;
4380
 
  __attribute__((cleanup(cleanup_queue)))
4381
 
    task_queue *queue = create_queue();
4382
 
  g_assert_nonnull(queue);
4383
 
 
4384
 
  task_context task = {
4385
 
    .func=read_inotify_event,
4386
 
    .epoll_fd=epoll_fd,
4387
 
    .fd=pipefds[0],
4388
 
    .quit_now=&quit_now,
4389
 
    .password=&password,
4390
 
    .filename=strdup("/nonexistent"),
4391
 
    .cancelled_filenames=&cancelled_filenames,
4392
 
    .current_time=&current_time,
4393
 
    .mandos_client_exited=&mandos_client_exited,
4394
 
    .password_is_read=&password_is_read,
4395
 
  };
4396
 
  task.func(task, queue);
4397
 
  g_assert_false(quit_now);
4398
 
  g_assert_true(queue->next_run == 0);
4399
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4400
 
 
4401
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4402
 
        .func=read_inotify_event,
4403
 
        .epoll_fd=epoll_fd,
4404
 
        .fd=pipefds[0],
4405
 
        .quit_now=&quit_now,
4406
 
        .password=&password,
4407
 
        .filename=task.filename,
4408
 
        .cancelled_filenames=&cancelled_filenames,
4409
 
        .current_time=&current_time,
4410
 
        .mandos_client_exited=&mandos_client_exited,
4411
 
        .password_is_read=&password_is_read,
4412
 
      }));
4413
 
 
4414
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4415
 
                                   EPOLLIN | EPOLLRDHUP));
4416
 
 
4417
 
  __attribute__((cleanup(cleanup_string)))
4418
 
    char *filename = NULL;
4419
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4420
 
                           dummy_file_name), >, 0);
4421
 
  g_assert_nonnull(filename);
4422
 
  g_assert_true(string_set_contains(*task.cancelled_filenames,
4423
 
                                    filename));
4424
 
}
4425
 
 
4426
4107
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4427
4108
                                              test_fixture *fixture,
4428
4109
                                              __attribute__((unused))
4661
4342
                                   EPOLLIN | EPOLLRDHUP));
4662
4343
}
4663
4344
 
4664
 
static void
4665
 
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
4666
 
                                              test_fixture *fixture,
4667
 
                                              __attribute__((unused))
4668
 
                                              gconstpointer
4669
 
                                              user_data){
4670
 
  __attribute__((cleanup(cleanup_close)))
4671
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4672
 
  g_assert_cmpint(epoll_fd, >=, 0);
4673
 
  __attribute__((cleanup(string_set_clear)))
4674
 
    string_set cancelled_filenames = {};
4675
 
  const mono_microsecs current_time = 0;
4676
 
 
4677
 
  int pipefds[2];
4678
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4679
 
 
4680
 
  /* "sufficient to read at least one event." - inotify(7) */
4681
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4682
 
                                  + NAME_MAX + 1);
4683
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4684
 
  struct {
4685
 
    struct inotify_event event;
4686
 
    char name_buffer[NAME_MAX + 1];
4687
 
  } ievent_buffer;
4688
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4689
 
 
4690
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
4691
 
  ievent->mask = IN_MOVED_FROM;
4692
 
  ievent->len = sizeof(dummy_file_name);
4693
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4694
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4695
 
                              + sizeof(dummy_file_name));
4696
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4697
 
                  ==, ievent_size);
4698
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4699
 
 
4700
 
  bool quit_now = false;
4701
 
  buffer password = {};
4702
 
  bool mandos_client_exited = false;
4703
 
  bool password_is_read = false;
4704
 
  __attribute__((cleanup(cleanup_queue)))
4705
 
    task_queue *queue = create_queue();
4706
 
  g_assert_nonnull(queue);
4707
 
 
4708
 
  task_context task = {
4709
 
    .func=read_inotify_event,
4710
 
    .epoll_fd=epoll_fd,
4711
 
    .fd=pipefds[0],
4712
 
    .quit_now=&quit_now,
4713
 
    .password=&password,
4714
 
    .filename=strdup("/nonexistent"),
4715
 
    .cancelled_filenames=&cancelled_filenames,
4716
 
    .current_time=&current_time,
4717
 
    .mandos_client_exited=&mandos_client_exited,
4718
 
    .password_is_read=&password_is_read,
4719
 
  };
4720
 
  task.func(task, queue);
4721
 
  g_assert_false(quit_now);
4722
 
  g_assert_true(queue->next_run == 0);
4723
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4724
 
 
4725
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4726
 
        .func=read_inotify_event,
4727
 
        .epoll_fd=epoll_fd,
4728
 
        .fd=pipefds[0],
4729
 
        .quit_now=&quit_now,
4730
 
        .password=&password,
4731
 
        .filename=task.filename,
4732
 
        .cancelled_filenames=&cancelled_filenames,
4733
 
        .current_time=&current_time,
4734
 
        .mandos_client_exited=&mandos_client_exited,
4735
 
        .password_is_read=&password_is_read,
4736
 
      }));
4737
 
 
4738
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4739
 
                                   EPOLLIN | EPOLLRDHUP));
4740
 
 
4741
 
  __attribute__((cleanup(cleanup_string)))
4742
 
    char *filename = NULL;
4743
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4744
 
                           dummy_file_name), >, 0);
4745
 
  g_assert_nonnull(filename);
4746
 
  g_assert_false(string_set_contains(cancelled_filenames, filename));
4747
 
}
4748
 
 
4749
4345
static
4750
4346
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4751
4347
                                               test_fixture *fixture,
5671
5267
                                            __attribute__((unused))
5672
5268
                                            gconstpointer user_data){
5673
5269
  __attribute__((cleanup(cleanup_close)))
5674
 
    const int epoll_fd = open("/dev/null",
5675
 
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
5270
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
5676
5271
  __attribute__((cleanup(cleanup_string)))
5677
5272
    char *const question_filename = strdup("/nonexistent/question");
5678
5273
  g_assert_nonnull(question_filename);
5958
5553
                                           test_fixture *fixture,
5959
5554
                                           __attribute__((unused))
5960
5555
                                           gconstpointer user_data){
5961
 
#ifndef __amd64__
5962
 
  g_test_skip("Skipping EMSGSIZE test on non-AMD64 platform");
5963
 
#else
5964
5556
  __attribute__((cleanup(cleanup_close)))
5965
5557
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5966
5558
  g_assert_cmpint(epoll_fd, >=, 0);
6019
5611
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6020
5612
  g_assert_true(string_set_contains(cancelled_filenames,
6021
5613
                                    question_filename));
6022
 
#endif
6023
5614
}
6024
5615
 
6025
5616
static void test_send_password_to_socket_retry(__attribute__((unused))
6086
5677
                                            __attribute__((unused))
6087
5678
                                            gconstpointer user_data){
6088
5679
  __attribute__((cleanup(cleanup_close)))
6089
 
    const int epoll_fd = open("/dev/null",
6090
 
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
5680
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
6091
5681
  __attribute__((cleanup(cleanup_string)))
6092
5682
    char *const question_filename = strdup("/nonexistent/question");
6093
5683
  g_assert_nonnull(question_filename);
6356
5946
                                              const char *const
6357
5947
                                              dirname){
6358
5948
  __attribute__((cleanup(cleanup_close)))
6359
 
    const int devnull_fd = open("/dev/null",
6360
 
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
5949
    const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
6361
5950
  g_assert_cmpint(devnull_fd, >=, 0);
6362
5951
  __attribute__((cleanup(cleanup_close)))
6363
5952
    const int real_stderr_fd = dup(STDERR_FILENO);
7906
7495
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7907
7496
  test_add("/queue/create", test_create_queue);
7908
7497
  test_add("/queue/add", test_add_to_queue);
7909
 
  test_add("/queue/add/overflow", test_add_to_queue_overflow);
7910
7498
  test_add("/queue/has_question/empty",
7911
7499
           test_queue_has_question_empty);
7912
7500
  test_add("/queue/has_question/false",
7999
7587
              test_add_inotify_dir_watch);
8000
7588
  test_add_st("/task-creators/add_inotify_dir_watch/fail",
8001
7589
              test_add_inotify_dir_watch_fail);
8002
 
  test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory",
8003
 
              test_add_inotify_dir_watch_nondir);
8004
7590
  test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
8005
7591
              test_add_inotify_dir_watch_EAGAIN);
8006
7592
  test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
8007
7593
              test_add_inotify_dir_watch_IN_CLOSE_WRITE);
8008
7594
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
8009
7595
              test_add_inotify_dir_watch_IN_MOVED_TO);
8010
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
8011
 
              test_add_inotify_dir_watch_IN_MOVED_FROM);
8012
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
8013
 
              test_add_inotify_dir_watch_IN_EXCL_UNLINK);
8014
7596
  test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
8015
7597
              test_add_inotify_dir_watch_IN_DELETE);
8016
7598
  test_add_st("/task/read_inotify_event/readerror",
8025
7607
              test_read_inotify_event_IN_CLOSE_WRITE);
8026
7608
  test_add_st("/task/read_inotify_event/IN_MOVED_TO",
8027
7609
              test_read_inotify_event_IN_MOVED_TO);
8028
 
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
8029
 
              test_read_inotify_event_IN_MOVED_FROM);
8030
7610
  test_add_st("/task/read_inotify_event/IN_DELETE",
8031
7611
              test_read_inotify_event_IN_DELETE);
8032
7612
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
8033
7613
              test_read_inotify_event_IN_CLOSE_WRITE_badname);
8034
7614
  test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
8035
7615
              test_read_inotify_event_IN_MOVED_TO_badname);
8036
 
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
8037
 
              test_read_inotify_event_IN_MOVED_FROM_badname);
8038
7616
  test_add_st("/task/read_inotify_event/IN_DELETE/badname",
8039
7617
              test_read_inotify_event_IN_DELETE_badname);
8040
7618
  test_add_st("/task/open_and_parse_question/ENOENT",
8137
7715
  g_option_context_set_help_enabled(context, FALSE);
8138
7716
  g_option_context_set_ignore_unknown_options(context, TRUE);
8139
7717
 
8140
 
  gboolean should_run_tests = FALSE;
 
7718
  gboolean run_tests = FALSE;
8141
7719
  GOptionEntry entries[] = {
8142
7720
    { "test", 0, 0, G_OPTION_ARG_NONE,
8143
 
      &should_run_tests, "Run tests", NULL },
 
7721
      &run_tests, "Run tests", NULL },
8144
7722
    { NULL }
8145
7723
  };
8146
7724
  g_option_context_add_main_entries(context, entries, NULL);
8153
7731
  }
8154
7732
 
8155
7733
  g_option_context_free(context);
8156
 
  return should_run_tests != FALSE;
 
7734
  return run_tests != FALSE;
8157
7735
}