/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-07-29 16:35:53 UTC
  • Revision ID: teddy@recompile.se-20190729163553-1i442i2cbx64c537
Make tests and man page examples match

Make the tests test_manual_page_example[1-5] match exactly what is
written in the manual page, and add comments to manual page as
reminders to keep tests and manual page examples in sync.

* mandos-ctl (Test_commands_from_options.test_manual_page_example_1):
  Remove "--verbose" option, since the manual does not have it as the
  first example, and change assertion to match.
* mandos-ctl.xml (EXAMPLE): Add comments to all examples documenting
  which test function they correspond to.  Also remove unnecessary
  quotes from option arguments in fourth example, and clarify language
  slightly in fifth example.

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;
1089
1071
  /* "sufficient to read at least one event." - inotify(7) */
1090
1072
  const size_t ievent_size = (sizeof(struct inotify_event)
1091
1073
                              + NAME_MAX + 1);
1092
 
  struct {
1093
 
    struct inotify_event event;
1094
 
    char name_buffer[NAME_MAX + 1];
1095
 
  } ievent_buffer;
1096
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
1074
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
1075
  struct inotify_event *ievent = ((struct inotify_event *)
 
1076
                                  ievent_buffer);
1097
1077
 
1098
1078
  const ssize_t read_length = read(fd, ievent, ievent_size);
1099
1079
  if(read_length == 0){ /* EOF */
1137
1117
             immediately */
1138
1118
          queue->next_run = 1;
1139
1119
        }
1140
 
      } else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
 
1120
      } else if(ievent->mask & IN_DELETE){
1141
1121
        if(not string_set_add(cancelled_filenames,
1142
1122
                              question_filename)){
1143
1123
          error(0, errno, "Could not add question %s to"
1897
1877
  g_assert_true(queue->tasks[0].func == dummy_func);
1898
1878
}
1899
1879
 
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
1880
static void dummy_func(__attribute__((unused))
1924
1881
                       const task_context task,
1925
1882
                       __attribute__((unused))
2196
2153
    }
2197
2154
    exit(EXIT_SUCCESS);
2198
2155
  }
2199
 
  if(pid == -1){
2200
 
    error(EXIT_FAILURE, errno, "Failed to fork()");
2201
 
  }
2202
 
 
2203
2156
  int status;
2204
2157
  waitpid(pid, &status, 0);
2205
2158
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2267
2220
 
2268
2221
  {
2269
2222
    __attribute__((cleanup(cleanup_close)))
2270
 
      const int devnull_fd = open("/dev/null",
2271
 
                                  O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2223
      const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
2272
2224
    g_assert_cmpint(devnull_fd, >=, 0);
2273
2225
    __attribute__((cleanup(cleanup_close)))
2274
2226
      const int real_stderr_fd = dup(STDERR_FILENO);
2298
2250
    {
2299
2251
      __attribute__((cleanup(cleanup_close)))
2300
2252
        const int devnull_fd = open("/dev/null",
2301
 
                                    O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2253
                                    O_WRONLY | O_CLOEXEC);
2302
2254
      g_assert_cmpint(devnull_fd, >=, 0);
2303
2255
      __attribute__((cleanup(cleanup_close)))
2304
2256
        const int real_stderr_fd = dup(STDERR_FILENO);
2949
2901
 
2950
2902
  __attribute__((cleanup(cleanup_close)))
2951
2903
    const int devnull_fd = open("/dev/null",
2952
 
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2904
                                O_WRONLY | O_CLOEXEC);
2953
2905
  g_assert_cmpint(devnull_fd, >=, 0);
2954
2906
  __attribute__((cleanup(cleanup_close)))
2955
2907
    const int real_stderr_fd = dup(STDERR_FILENO);
3020
2972
 
3021
2973
  __attribute__((cleanup(cleanup_close)))
3022
2974
    const int devnull_fd = open("/dev/null",
3023
 
                                O_WRONLY | O_CLOEXEC, O_NOCTTY);
 
2975
                                O_WRONLY | O_CLOEXEC);
3024
2976
  g_assert_cmpint(devnull_fd, >=, 0);
3025
2977
  __attribute__((cleanup(cleanup_close)))
3026
2978
    const int real_stderr_fd = dup(STDERR_FILENO);
3064
3016
    buffer password = {};
3065
3017
 
3066
3018
  /* 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);
 
3019
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
3069
3020
 
3070
3021
  bool password_is_read = false;
3071
3022
  bool quit_now = false;
3499
3450
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3500
3451
}
3501
3452
 
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
3453
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3541
3454
                                              test_fixture *fixture,
3542
3455
                                              __attribute__((unused))
3757
3670
}
3758
3671
 
3759
3672
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
3673
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3833
3674
                                          test_fixture *fixture,
3834
3675
                                          __attribute__((unused))
3892
3733
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3893
3734
}
3894
3735
 
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
3736
static void test_read_inotify_event_readerror(__attribute__((unused))
3972
3737
                                              test_fixture *fixture,
3973
3738
                                              __attribute__((unused))
3979
3744
  const mono_microsecs current_time = 0;
3980
3745
 
3981
3746
  /* 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);
 
3747
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
3984
3748
 
3985
3749
  bool quit_now = false;
3986
3750
  __attribute__((cleanup(cleanup_queue)))
4165
3929
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4166
3930
                                  + NAME_MAX + 1);
4167
3931
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4168
 
  struct {
4169
 
    struct inotify_event event;
4170
 
    char name_buffer[NAME_MAX + 1];
4171
 
  } ievent_buffer;
4172
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
3932
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
3933
  struct inotify_event *ievent = ((struct inotify_event *)
 
3934
                                  ievent_buffer);
4173
3935
 
4174
3936
  const char dummy_file_name[] = "ask.dummy_file_name";
4175
3937
  ievent->mask = IN_CLOSE_WRITE;
4177
3939
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4178
3940
  const size_t ievent_size = (sizeof(struct inotify_event)
4179
3941
                              + sizeof(dummy_file_name));
4180
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
3942
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
4181
3943
                  ==, ievent_size);
4182
3944
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4183
3945
 
4260
4022
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4261
4023
                                  + NAME_MAX + 1);
4262
4024
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4263
 
  struct {
4264
 
    struct inotify_event event;
4265
 
    char name_buffer[NAME_MAX + 1];
4266
 
  } ievent_buffer;
4267
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4025
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
4026
  struct inotify_event *ievent = ((struct inotify_event *)
 
4027
                                  ievent_buffer);
4268
4028
 
4269
4029
  const char dummy_file_name[] = "ask.dummy_file_name";
4270
4030
  ievent->mask = IN_MOVED_TO;
4272
4032
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4273
4033
  const size_t ievent_size = (sizeof(struct inotify_event)
4274
4034
                              + sizeof(dummy_file_name));
4275
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4035
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
4276
4036
                  ==, ievent_size);
4277
4037
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4278
4038
 
4338
4098
      }));
4339
4099
}
4340
4100
 
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
4101
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4427
4102
                                              test_fixture *fixture,
4428
4103
                                              __attribute__((unused))
4442
4117
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4443
4118
                                  + NAME_MAX + 1);
4444
4119
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4445
 
  struct {
4446
 
    struct inotify_event event;
4447
 
    char name_buffer[NAME_MAX + 1];
4448
 
  } ievent_buffer;
4449
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4120
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
4121
  struct inotify_event *ievent = ((struct inotify_event *)
 
4122
                                  ievent_buffer);
4450
4123
 
4451
4124
  const char dummy_file_name[] = "ask.dummy_file_name";
4452
4125
  ievent->mask = IN_DELETE;
4454
4127
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4455
4128
  const size_t ievent_size = (sizeof(struct inotify_event)
4456
4129
                              + sizeof(dummy_file_name));
4457
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4130
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
4458
4131
                  ==, ievent_size);
4459
4132
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4460
4133
 
4526
4199
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4527
4200
                                  + NAME_MAX + 1);
4528
4201
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4529
 
  struct {
4530
 
    struct inotify_event event;
4531
 
    char name_buffer[NAME_MAX + 1];
4532
 
  } ievent_buffer;
4533
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4202
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
4203
  struct inotify_event *ievent = ((struct inotify_event *)
 
4204
                                  ievent_buffer);
4534
4205
 
4535
4206
  const char dummy_file_name[] = "ignored.dummy_file_name";
4536
4207
  ievent->mask = IN_CLOSE_WRITE;
4538
4209
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4539
4210
  const size_t ievent_size = (sizeof(struct inotify_event)
4540
4211
                              + sizeof(dummy_file_name));
4541
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4212
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
4542
4213
                  ==, ievent_size);
4543
4214
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4544
4215
 
4602
4273
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4603
4274
                                  + NAME_MAX + 1);
4604
4275
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4605
 
  struct {
4606
 
    struct inotify_event event;
4607
 
    char name_buffer[NAME_MAX + 1];
4608
 
  } ievent_buffer;
4609
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4276
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
4277
  struct inotify_event *ievent = ((struct inotify_event *)
 
4278
                                  ievent_buffer);
4610
4279
 
4611
4280
  const char dummy_file_name[] = "ignored.dummy_file_name";
4612
4281
  ievent->mask = IN_MOVED_TO;
4614
4283
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4615
4284
  const size_t ievent_size = (sizeof(struct inotify_event)
4616
4285
                              + sizeof(dummy_file_name));
4617
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4286
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
4618
4287
                  ==, ievent_size);
4619
4288
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4620
4289
 
4661
4330
                                   EPOLLIN | EPOLLRDHUP));
4662
4331
}
4663
4332
 
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
4333
static
4750
4334
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4751
4335
                                               test_fixture *fixture,
4766
4350
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4767
4351
                                  + NAME_MAX + 1);
4768
4352
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4769
 
  struct {
4770
 
    struct inotify_event event;
4771
 
    char name_buffer[NAME_MAX + 1];
4772
 
  } ievent_buffer;
4773
 
  struct inotify_event *const ievent = &ievent_buffer.event;
 
4353
  char ievent_buffer[sizeof(struct inotify_event) + NAME_MAX + 1];
 
4354
  struct inotify_event *ievent = ((struct inotify_event *)
 
4355
                                  ievent_buffer);
4774
4356
 
4775
4357
  const char dummy_file_name[] = "ignored.dummy_file_name";
4776
4358
  ievent->mask = IN_DELETE;
4778
4360
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4779
4361
  const size_t ievent_size = (sizeof(struct inotify_event)
4780
4362
                              + sizeof(dummy_file_name));
4781
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
 
4363
  g_assert_cmpint(write(pipefds[1], ievent_buffer, ievent_size),
4782
4364
                  ==, ievent_size);
4783
4365
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4784
4366
 
5671
5253
                                            __attribute__((unused))
5672
5254
                                            gconstpointer user_data){
5673
5255
  __attribute__((cleanup(cleanup_close)))
5674
 
    const int epoll_fd = open("/dev/null",
5675
 
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
5256
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
5676
5257
  __attribute__((cleanup(cleanup_string)))
5677
5258
    char *const question_filename = strdup("/nonexistent/question");
5678
5259
  g_assert_nonnull(question_filename);
5958
5539
                                           test_fixture *fixture,
5959
5540
                                           __attribute__((unused))
5960
5541
                                           gconstpointer user_data){
5961
 
#ifndef __amd64__
5962
 
  g_test_skip("Skipping EMSGSIZE test on non-AMD64 platform");
5963
 
#else
5964
5542
  __attribute__((cleanup(cleanup_close)))
5965
5543
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
5966
5544
  g_assert_cmpint(epoll_fd, >=, 0);
6019
5597
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6020
5598
  g_assert_true(string_set_contains(cancelled_filenames,
6021
5599
                                    question_filename));
6022
 
#endif
6023
5600
}
6024
5601
 
6025
5602
static void test_send_password_to_socket_retry(__attribute__((unused))
6086
5663
                                            __attribute__((unused))
6087
5664
                                            gconstpointer user_data){
6088
5665
  __attribute__((cleanup(cleanup_close)))
6089
 
    const int epoll_fd = open("/dev/null",
6090
 
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
5666
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
6091
5667
  __attribute__((cleanup(cleanup_string)))
6092
5668
    char *const question_filename = strdup("/nonexistent/question");
6093
5669
  g_assert_nonnull(question_filename);
6356
5932
                                              const char *const
6357
5933
                                              dirname){
6358
5934
  __attribute__((cleanup(cleanup_close)))
6359
 
    const int devnull_fd = open("/dev/null",
6360
 
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
5935
    const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
6361
5936
  g_assert_cmpint(devnull_fd, >=, 0);
6362
5937
  __attribute__((cleanup(cleanup_close)))
6363
5938
    const int real_stderr_fd = dup(STDERR_FILENO);
7906
7481
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7907
7482
  test_add("/queue/create", test_create_queue);
7908
7483
  test_add("/queue/add", test_add_to_queue);
7909
 
  test_add("/queue/add/overflow", test_add_to_queue_overflow);
7910
7484
  test_add("/queue/has_question/empty",
7911
7485
           test_queue_has_question_empty);
7912
7486
  test_add("/queue/has_question/false",
7999
7573
              test_add_inotify_dir_watch);
8000
7574
  test_add_st("/task-creators/add_inotify_dir_watch/fail",
8001
7575
              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
7576
  test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
8005
7577
              test_add_inotify_dir_watch_EAGAIN);
8006
7578
  test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
8007
7579
              test_add_inotify_dir_watch_IN_CLOSE_WRITE);
8008
7580
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
8009
7581
              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
7582
  test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
8015
7583
              test_add_inotify_dir_watch_IN_DELETE);
8016
7584
  test_add_st("/task/read_inotify_event/readerror",
8025
7593
              test_read_inotify_event_IN_CLOSE_WRITE);
8026
7594
  test_add_st("/task/read_inotify_event/IN_MOVED_TO",
8027
7595
              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
7596
  test_add_st("/task/read_inotify_event/IN_DELETE",
8031
7597
              test_read_inotify_event_IN_DELETE);
8032
7598
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
8033
7599
              test_read_inotify_event_IN_CLOSE_WRITE_badname);
8034
7600
  test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
8035
7601
              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
7602
  test_add_st("/task/read_inotify_event/IN_DELETE/badname",
8039
7603
              test_read_inotify_event_IN_DELETE_badname);
8040
7604
  test_add_st("/task/open_and_parse_question/ENOENT",
8137
7701
  g_option_context_set_help_enabled(context, FALSE);
8138
7702
  g_option_context_set_ignore_unknown_options(context, TRUE);
8139
7703
 
8140
 
  gboolean should_run_tests = FALSE;
 
7704
  gboolean run_tests = FALSE;
8141
7705
  GOptionEntry entries[] = {
8142
7706
    { "test", 0, 0, G_OPTION_ARG_NONE,
8143
 
      &should_run_tests, "Run tests", NULL },
 
7707
      &run_tests, "Run tests", NULL },
8144
7708
    { NULL }
8145
7709
  };
8146
7710
  g_option_context_add_main_entries(context, entries, NULL);
8153
7717
  }
8154
7718
 
8155
7719
  g_option_context_free(context);
8156
 
  return should_run_tests != FALSE;
 
7720
  return run_tests != FALSE;
8157
7721
}