/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-2021 Teddy Hogeborn
6
 
 * Copyright © 2019-2021 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
 * 
23
23
 * Contact the authors at <mandos@recompile.se>.
24
24
 */
25
25
 
26
 
#define _GNU_SOURCE             /* pipe2(), O_CLOEXEC, setresgid(),
27
 
                                   setresuid(), asprintf(), getline(),
28
 
                                   basename() */
29
 
#include <inttypes.h>           /* uintmax_t, strtoumax(), PRIuMAX,
30
 
                                   PRIdMAX, intmax_t, uint32_t,
31
 
                                   SCNx32, SCNuMAX, SCNxMAX */
32
 
#include <stddef.h>             /* size_t, NULL */
 
26
#define _GNU_SOURCE
 
27
#include <inttypes.h>           /* uintmax_t, PRIuMAX, PRIdMAX,
 
28
                                   intmax_t, uint32_t, SCNx32,
 
29
                                   SCNuMAX, SCNxMAX */
 
30
#include <stddef.h>             /* size_t */
33
31
#include <sys/types.h>          /* pid_t, uid_t, gid_t, getuid(),
34
32
                                   getpid() */
35
33
#include <stdbool.h>            /* bool, true, false */
42
40
                                   NSIG, sigismember(), SA_ONSTACK,
43
41
                                   SIG_DFL, SIG_IGN, SIGINT, SIGQUIT,
44
42
                                   SIGHUP, SIGSTOP, SIG_UNBLOCK */
45
 
#include <unistd.h>             /* uid_t, gid_t, close(), pipe2(),
46
 
                                   fork(), _exit(), dup2(),
47
 
                                   STDOUT_FILENO, setresgid(),
48
 
                                   setresuid(), execv(), ssize_t,
49
 
                                   read(), dup3(), getuid(), dup(),
50
 
                                   STDERR_FILENO, pause(), write(),
51
 
                                   rmdir(), unlink(), getpid() */
52
43
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE,
53
 
                                   malloc(), free(), realloc(),
54
 
                                   setenv(), calloc(), mkdtemp(),
55
 
                                   mkostemp() */
 
44
                                   malloc(), free(), strtoumax(),
 
45
                                   realloc(), setenv(), calloc(),
 
46
                                   mkdtemp(), mkostemp() */
56
47
#include <iso646.h>             /* not, or, and, xor */
57
48
#include <error.h>              /* error() */
58
49
#include <sysexits.h>           /* EX_USAGE, EX_OSERR, EX_OSFILE */
59
50
#include <errno.h>              /* errno, error_t, EACCES,
60
 
                                   ENAMETOOLONG, ENOENT, ENOTDIR,
61
 
                                   ENOMEM, EEXIST, ECHILD, EPERM,
62
 
                                   EAGAIN, EINTR, ENOBUFS, EADDRINUSE,
 
51
                                   ENAMETOOLONG, ENOENT, EEXIST,
 
52
                                   ECHILD, EPERM, ENOMEM, EAGAIN,
 
53
                                   EINTR, ENOBUFS, EADDRINUSE,
63
54
                                   ECONNREFUSED, ECONNRESET,
64
55
                                   ETOOMANYREFS, EMSGSIZE, EBADF,
65
56
                                   EINVAL */
66
57
#include <string.h>             /* strdup(), memcpy(),
67
58
                                   explicit_bzero(), memset(),
68
59
                                   strcmp(), strlen(), strncpy(),
69
 
                                   memcmp(), basename(), strerror() */
 
60
                                   memcmp(), basename() */
70
61
#include <argz.h>               /* argz_create(), argz_count(),
71
62
                                   argz_extract(), argz_next(),
72
63
                                   argz_add() */
82
73
                                   ARGP_ERR_UNKNOWN, ARGP_KEY_ARGS,
83
74
                                   struct argp, argp_parse(),
84
75
                                   ARGP_NO_EXIT */
85
 
#include <stdint.h>             /* SIZE_MAX, uint32_t */
 
76
#include <unistd.h>             /* uid_t, gid_t, close(), pipe2(),
 
77
                                   fork(), _exit(), dup2(),
 
78
                                   STDOUT_FILENO, setresgid(),
 
79
                                   setresuid(), execv(), ssize_t,
 
80
                                   read(), dup3(), getuid(), dup(),
 
81
                                   STDERR_FILENO, pause(), write(),
 
82
                                   rmdir(), unlink(), getpid() */
86
83
#include <sys/mman.h>           /* munlock(), mlock() */
87
84
#include <fcntl.h>              /* O_CLOEXEC, O_NONBLOCK, fcntl(),
88
85
                                   F_GETFD, F_GETFL, FD_CLOEXEC,
89
 
                                   open(), O_WRONLY, O_NOCTTY,
90
 
                                   O_RDONLY, O_NOFOLLOW */
 
86
                                   open(), O_WRONLY, O_RDONLY */
91
87
#include <sys/wait.h>           /* waitpid(), WNOHANG, WIFEXITED(),
92
88
                                   WEXITSTATUS() */
93
89
#include <limits.h>             /* PIPE_BUF, NAME_MAX, INT_MAX */
94
90
#include <sys/inotify.h>        /* inotify_init1(), IN_NONBLOCK,
95
91
                                   IN_CLOEXEC, inotify_add_watch(),
96
92
                                   IN_CLOSE_WRITE, IN_MOVED_TO,
97
 
                                   IN_MOVED_FROM, IN_DELETE,
98
 
                                   IN_EXCL_UNLINK, IN_ONLYDIR,
99
 
                                   struct inotify_event */
 
93
                                   IN_DELETE, struct inotify_event */
100
94
#include <fnmatch.h>            /* fnmatch(), FNM_FILE_NAME */
101
 
#include <stdio.h>              /* asprintf(), FILE, stderr, fopen(),
102
 
                                   fclose(), getline(), sscanf(),
103
 
                                   feof(), ferror(), rename(),
104
 
                                   fdopen(), fprintf(), fscanf() */
 
95
#include <stdio.h>              /* asprintf(), FILE, fopen(),
 
96
                                   getline(), sscanf(), feof(),
 
97
                                   ferror(), fclose(), stderr,
 
98
                                   rename(), fdopen(), fprintf(),
 
99
                                   fscanf() */
105
100
#include <glib.h>    /* GKeyFile, g_key_file_free(), g_key_file_new(),
106
101
                        GError, g_key_file_load_from_file(),
107
102
                        G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
112
107
                        g_assert_null(), g_assert_false(),
113
108
                        g_assert_cmpint(), g_assert_cmpuint(),
114
109
                        g_test_skip(), g_assert_cmpstr(),
115
 
                        g_test_message(), g_test_init(), g_test_add(),
116
 
                        g_test_run(), GOptionContext,
117
 
                        g_option_context_new(),
 
110
                        g_test_init(), g_test_add(), g_test_run(),
 
111
                        GOptionContext, g_option_context_new(),
118
112
                        g_option_context_set_help_enabled(), FALSE,
119
113
                        g_option_context_set_ignore_unknown_options(),
120
114
                        gboolean, GOptionEntry, G_OPTION_ARG_NONE,
151
145
  mono_microsecs next_run;
152
146
} __attribute__((designated_init)) task_queue;
153
147
 
154
 
/* "task_func" - A function type for task functions
 
148
/* "func_type" - A function type for task functions
155
149
 
156
150
   I.e. functions for the code which runs when a task is run, all have
157
151
   this type */
437
431
    case EACCES:
438
432
    case ENAMETOOLONG:
439
433
    case ENOENT:
440
 
    case ENOTDIR:
441
434
      return EX_OSFILE;
442
435
    default:
443
436
      return EX_OSERR;
654
647
 
655
648
__attribute__((nonnull, warn_unused_result))
656
649
bool add_to_queue(task_queue *const queue, const task_context task){
657
 
  if((queue->length + 1) > (SIZE_MAX / sizeof(task_context))){
658
 
    /* overflow */
659
 
    error(0, ENOMEM, "Failed to allocate %" PRIuMAX
660
 
          " tasks for queue->tasks", (uintmax_t)(queue->length + 1));
661
 
    errno = ENOMEM;
662
 
    return false;
663
 
  }
664
650
  const size_t needed_size = sizeof(task_context)*(queue->length + 1);
665
651
  if(needed_size > (queue->allocated)){
666
652
    task_context *const new_tasks = realloc(queue->tasks,
871
857
  }
872
858
  close(pipefds[1]);
873
859
 
874
 
  if(pid == -1){
875
 
    error(0, errno, "Failed to fork()");
876
 
    close(pipefds[0]);
877
 
    return false;
878
 
  }
879
 
 
880
860
  if(not add_to_queue(queue, (task_context){
881
861
        .func=wait_for_mandos_client_exit,
882
862
        .pid=pid,
1037
1017
    return false;
1038
1018
  }
1039
1019
 
1040
 
  if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE | IN_MOVED_TO
1041
 
                       | IN_MOVED_FROM| IN_DELETE | IN_EXCL_UNLINK
1042
 
                       | IN_ONLYDIR)
 
1020
  if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE
 
1021
                       | IN_MOVED_TO | IN_DELETE)
1043
1022
     == -1){
1044
1023
    error(0, errno, "Failed to create inotify watch on %s", dir);
1045
1024
    return false;
1140
1119
             immediately */
1141
1120
          queue->next_run = 1;
1142
1121
        }
1143
 
      } else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
 
1122
      } else if(ievent->mask & IN_DELETE){
1144
1123
        if(not string_set_add(cancelled_filenames,
1145
1124
                              question_filename)){
1146
1125
          error(0, errno, "Could not add question %s to"
1196
1175
  bool *const password_is_read = task.password_is_read;
1197
1176
 
1198
1177
  /* We use the GLib "Key-value file parser" functions to parse the
1199
 
     question file.  See <https://systemd.io/PASSWORD_AGENTS/> for
1200
 
     specification of contents */
 
1178
     question file.  See <https://www.freedesktop.org/wiki/Software
 
1179
     /systemd/PasswordAgents/> for specification of contents */
1201
1180
  __attribute__((nonnull))
1202
1181
    void cleanup_g_key_file(GKeyFile **key_file){
1203
1182
    if(*key_file != NULL){
1493
1472
         not. You may but don't have to include a final NUL byte in
1494
1473
         your message.
1495
1474
 
1496
 
         — <https://systemd.io/PASSWORD_AGENTS/> (Tue, 15 Sep 2020
1497
 
         14:24:20 GMT)
 
1475
         — <https://www.freedesktop.org/wiki/Software/systemd/
 
1476
         PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
1498
1477
      */
1499
1478
      send_buffer[0] = '+';     /* Prefix with "+" */
1500
1479
      /* Always add an extra NUL */
1505
1484
      errno = 0;
1506
1485
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1507
1486
                           MSG_NOSIGNAL);
1508
 
      const error_t saved_errno = (ssret < 0) ? errno : 0;
 
1487
      const error_t saved_errno = errno;
1509
1488
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1510
1489
      explicit_bzero(send_buffer, send_buffer_length);
1511
1490
#else
1529
1508
          /* Retry, below */
1530
1509
          break;
1531
1510
        case EMSGSIZE:
1532
 
          error(0, saved_errno, "Password of size %" PRIuMAX
1533
 
                " is too big", (uintmax_t)password->length);
 
1511
          error(0, 0, "Password of size %" PRIuMAX " is too big",
 
1512
                (uintmax_t)password->length);
1534
1513
#if __GNUC__ < 7
1535
1514
          /* FALLTHROUGH */
1536
1515
#else
1538
1517
#endif
1539
1518
        case 0:
1540
1519
          if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1541
 
            error(0, 0, "Password only partially sent to socket %s: %"
1542
 
                  PRIuMAX " out of %" PRIuMAX " bytes sent", filename,
1543
 
                  (uintmax_t)ssret, (uintmax_t)send_buffer_length);
 
1520
            error(0, 0, "Password only partially sent to socket");
1544
1521
          }
1545
1522
#if __GNUC__ < 7
1546
1523
          /* FALLTHROUGH */
1902
1879
  g_assert_true(queue->tasks[0].func == dummy_func);
1903
1880
}
1904
1881
 
1905
 
static void test_add_to_queue_overflow(__attribute__((unused))
1906
 
                                       test_fixture *fixture,
1907
 
                                       __attribute__((unused))
1908
 
                                       gconstpointer user_data){
1909
 
  __attribute__((cleanup(cleanup_queue)))
1910
 
    task_queue *queue = create_queue();
1911
 
  g_assert_nonnull(queue);
1912
 
  g_assert_true(queue->length == 0);
1913
 
  queue->length = SIZE_MAX / sizeof(task_context); /* fake max size */
1914
 
 
1915
 
  FILE *real_stderr = stderr;
1916
 
  FILE *devnull = fopen("/dev/null", "we");
1917
 
  g_assert_nonnull(devnull);
1918
 
  stderr = devnull;
1919
 
  const bool ret = add_to_queue(queue,
1920
 
                                (task_context){ .func=dummy_func });
1921
 
  g_assert_true(errno == ENOMEM);
1922
 
  g_assert_false(ret);
1923
 
  stderr = real_stderr;
1924
 
  g_assert_cmpint(fclose(devnull), ==, 0);
1925
 
  queue->length = 0;            /* Restore real size */
1926
 
}
1927
 
 
1928
1882
static void dummy_func(__attribute__((unused))
1929
1883
                       const task_context task,
1930
1884
                       __attribute__((unused))
2201
2155
    }
2202
2156
    exit(EXIT_SUCCESS);
2203
2157
  }
2204
 
  if(pid == -1){
2205
 
    error(EXIT_FAILURE, errno, "Failed to fork()");
2206
 
  }
2207
 
 
2208
2158
  int status;
2209
2159
  waitpid(pid, &status, 0);
2210
2160
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2272
2222
 
2273
2223
  {
2274
2224
    __attribute__((cleanup(cleanup_close)))
2275
 
      const int devnull_fd = open("/dev/null",
2276
 
                                  O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2225
      const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
2277
2226
    g_assert_cmpint(devnull_fd, >=, 0);
2278
2227
    __attribute__((cleanup(cleanup_close)))
2279
2228
      const int real_stderr_fd = dup(STDERR_FILENO);
2303
2252
    {
2304
2253
      __attribute__((cleanup(cleanup_close)))
2305
2254
        const int devnull_fd = open("/dev/null",
2306
 
                                    O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2255
                                    O_WRONLY | O_CLOEXEC);
2307
2256
      g_assert_cmpint(devnull_fd, >=, 0);
2308
2257
      __attribute__((cleanup(cleanup_close)))
2309
2258
        const int real_stderr_fd = dup(STDERR_FILENO);
2954
2903
 
2955
2904
  __attribute__((cleanup(cleanup_close)))
2956
2905
    const int devnull_fd = open("/dev/null",
2957
 
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
2906
                                O_WRONLY | O_CLOEXEC);
2958
2907
  g_assert_cmpint(devnull_fd, >=, 0);
2959
2908
  __attribute__((cleanup(cleanup_close)))
2960
2909
    const int real_stderr_fd = dup(STDERR_FILENO);
3025
2974
 
3026
2975
  __attribute__((cleanup(cleanup_close)))
3027
2976
    const int devnull_fd = open("/dev/null",
3028
 
                                O_WRONLY | O_CLOEXEC, O_NOCTTY);
 
2977
                                O_WRONLY | O_CLOEXEC);
3029
2978
  g_assert_cmpint(devnull_fd, >=, 0);
3030
2979
  __attribute__((cleanup(cleanup_close)))
3031
2980
    const int real_stderr_fd = dup(STDERR_FILENO);
3069
3018
    buffer password = {};
3070
3019
 
3071
3020
  /* Reading /proc/self/mem from offset 0 will always give EIO */
3072
 
  const int fd = open("/proc/self/mem",
3073
 
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
 
3021
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
3074
3022
 
3075
3023
  bool password_is_read = false;
3076
3024
  bool quit_now = false;
3504
3452
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3505
3453
}
3506
3454
 
3507
 
static void test_add_inotify_dir_watch_nondir(__attribute__((unused))
3508
 
                                              test_fixture *fixture,
3509
 
                                            __attribute__((unused))
3510
 
                                              gconstpointer
3511
 
                                              user_data){
3512
 
  __attribute__((cleanup(cleanup_close)))
3513
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3514
 
  g_assert_cmpint(epoll_fd, >=, 0);
3515
 
  __attribute__((cleanup(cleanup_queue)))
3516
 
    task_queue *queue = create_queue();
3517
 
  g_assert_nonnull(queue);
3518
 
  __attribute__((cleanup(string_set_clear)))
3519
 
    string_set cancelled_filenames = {};
3520
 
  const mono_microsecs current_time = 0;
3521
 
 
3522
 
  bool quit_now = false;
3523
 
  buffer password = {};
3524
 
  bool mandos_client_exited = false;
3525
 
  bool password_is_read = false;
3526
 
 
3527
 
  const char not_a_directory[] = "/dev/tty";
3528
 
 
3529
 
  FILE *real_stderr = stderr;
3530
 
  FILE *devnull = fopen("/dev/null", "we");
3531
 
  g_assert_nonnull(devnull);
3532
 
  stderr = devnull;
3533
 
  g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3534
 
                                       &password, not_a_directory,
3535
 
                                       &cancelled_filenames,
3536
 
                                       &current_time,
3537
 
                                       &mandos_client_exited,
3538
 
                                       &password_is_read));
3539
 
  stderr = real_stderr;
3540
 
  g_assert_cmpint(fclose(devnull), ==, 0);
3541
 
 
3542
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3543
 
}
3544
 
 
3545
3455
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3546
3456
                                              test_fixture *fixture,
3547
3457
                                              __attribute__((unused))
3762
3672
}
3763
3673
 
3764
3674
static
3765
 
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
3766
 
                                              test_fixture *fixture,
3767
 
                                              __attribute__((unused))
3768
 
                                              gconstpointer
3769
 
                                              user_data){
3770
 
  __attribute__((cleanup(cleanup_close)))
3771
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3772
 
  g_assert_cmpint(epoll_fd, >=, 0);
3773
 
  __attribute__((cleanup(cleanup_queue)))
3774
 
    task_queue *queue = create_queue();
3775
 
  g_assert_nonnull(queue);
3776
 
  __attribute__((cleanup(string_set_clear)))
3777
 
    string_set cancelled_filenames = {};
3778
 
  const mono_microsecs current_time = 0;
3779
 
 
3780
 
  bool quit_now = false;
3781
 
  buffer password = {};
3782
 
  bool mandos_client_exited = false;
3783
 
  bool password_is_read = false;
3784
 
 
3785
 
  __attribute__((cleanup(cleanup_string)))
3786
 
    char *tempdir = make_temporary_directory();
3787
 
  g_assert_nonnull(tempdir);
3788
 
 
3789
 
  __attribute__((cleanup(cleanup_string)))
3790
 
    char *tempfilename = make_temporary_file_in_directory(tempdir);
3791
 
  g_assert_nonnull(tempfilename);
3792
 
 
3793
 
  __attribute__((cleanup(cleanup_string)))
3794
 
    char *targetdir = make_temporary_directory();
3795
 
  g_assert_nonnull(targetdir);
3796
 
 
3797
 
  __attribute__((cleanup(cleanup_string)))
3798
 
    char *targetfilename = NULL;
3799
 
  g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
3800
 
                           basename(tempfilename)), >, 0);
3801
 
  g_assert_nonnull(targetfilename);
3802
 
 
3803
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3804
 
                                      &password, tempdir,
3805
 
                                      &cancelled_filenames,
3806
 
                                      &current_time,
3807
 
                                      &mandos_client_exited,
3808
 
                                      &password_is_read));
3809
 
 
3810
 
  g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
3811
 
 
3812
 
  const task_context *const added_read_task
3813
 
    = find_matching_task(queue,
3814
 
                         (task_context){ .func=read_inotify_event });
3815
 
  g_assert_nonnull(added_read_task);
3816
 
 
3817
 
  /* "sufficient to read at least one event." - inotify(7) */
3818
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3819
 
                              + NAME_MAX + 1);
3820
 
  struct inotify_event *ievent = malloc(ievent_size);
3821
 
  g_assert_nonnull(ievent);
3822
 
 
3823
 
  ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3824
 
 
3825
 
  g_assert_cmpint((int)read_size, >, 0);
3826
 
  g_assert_true(ievent->mask & IN_MOVED_FROM);
3827
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
3828
 
 
3829
 
  free(ievent);
3830
 
 
3831
 
  g_assert_cmpint(unlink(targetfilename), ==, 0);
3832
 
  g_assert_cmpint(rmdir(targetdir), ==, 0);
3833
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3834
 
}
3835
 
 
3836
 
static
3837
3675
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3838
3676
                                          test_fixture *fixture,
3839
3677
                                          __attribute__((unused))
3897
3735
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3898
3736
}
3899
3737
 
3900
 
static
3901
 
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
3902
 
                                               test_fixture *fixture,
3903
 
                                               __attribute__((unused))
3904
 
                                               gconstpointer
3905
 
                                               user_data){
3906
 
  __attribute__((cleanup(cleanup_close)))
3907
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3908
 
  g_assert_cmpint(epoll_fd, >=, 0);
3909
 
  __attribute__((cleanup(cleanup_queue)))
3910
 
    task_queue *queue = create_queue();
3911
 
  g_assert_nonnull(queue);
3912
 
  __attribute__((cleanup(string_set_clear)))
3913
 
    string_set cancelled_filenames = {};
3914
 
  const mono_microsecs current_time = 0;
3915
 
 
3916
 
  bool quit_now = false;
3917
 
  buffer password = {};
3918
 
  bool mandos_client_exited = false;
3919
 
  bool password_is_read = false;
3920
 
 
3921
 
  __attribute__((cleanup(cleanup_string)))
3922
 
    char *tempdir = make_temporary_directory();
3923
 
  g_assert_nonnull(tempdir);
3924
 
 
3925
 
  __attribute__((cleanup(cleanup_string)))
3926
 
    char *tempfile = make_temporary_file_in_directory(tempdir);
3927
 
  g_assert_nonnull(tempfile);
3928
 
  int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
3929
 
                         | O_NOFOLLOW);
3930
 
  g_assert_cmpint(tempfile_fd, >, 2);
3931
 
 
3932
 
  g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3933
 
                                      &password, tempdir,
3934
 
                                      &cancelled_filenames,
3935
 
                                      &current_time,
3936
 
                                      &mandos_client_exited,
3937
 
                                      &password_is_read));
3938
 
  g_assert_cmpint(unlink(tempfile), ==, 0);
3939
 
 
3940
 
  g_assert_cmpuint((unsigned int)queue->length, >, 0);
3941
 
 
3942
 
  const task_context *const added_read_task
3943
 
    = find_matching_task(queue,
3944
 
                         (task_context){ .func=read_inotify_event });
3945
 
  g_assert_nonnull(added_read_task);
3946
 
 
3947
 
  g_assert_cmpint(added_read_task->fd, >, 2);
3948
 
  g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3949
 
 
3950
 
  /* "sufficient to read at least one event." - inotify(7) */
3951
 
  const size_t ievent_size = (sizeof(struct inotify_event)
3952
 
                              + NAME_MAX + 1);
3953
 
  struct inotify_event *ievent = malloc(ievent_size);
3954
 
  g_assert_nonnull(ievent);
3955
 
 
3956
 
  ssize_t read_size = 0;
3957
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
3958
 
 
3959
 
  g_assert_cmpint((int)read_size, >, 0);
3960
 
  g_assert_true(ievent->mask & IN_DELETE);
3961
 
  g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3962
 
 
3963
 
  g_assert_cmpint(close(tempfile_fd), ==, 0);
3964
 
 
3965
 
  /* IN_EXCL_UNLINK should make the closing of the previously unlinked
3966
 
     file not appear as an ievent, so we should not see it now. */
3967
 
  read_size = read(added_read_task->fd, ievent, ievent_size);
3968
 
  g_assert_cmpint((int)read_size, ==, -1);
3969
 
  g_assert_true(errno == EAGAIN);
3970
 
 
3971
 
  free(ievent);
3972
 
 
3973
 
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3974
 
}
3975
 
 
3976
3738
static void test_read_inotify_event_readerror(__attribute__((unused))
3977
3739
                                              test_fixture *fixture,
3978
3740
                                              __attribute__((unused))
3984
3746
  const mono_microsecs current_time = 0;
3985
3747
 
3986
3748
  /* Reading /proc/self/mem from offset 0 will always result in EIO */
3987
 
  const int fd = open("/proc/self/mem",
3988
 
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
 
3749
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
3989
3750
 
3990
3751
  bool quit_now = false;
3991
3752
  __attribute__((cleanup(cleanup_queue)))
4343
4104
      }));
4344
4105
}
4345
4106
 
4346
 
static
4347
 
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
4348
 
                                           test_fixture *fixture,
4349
 
                                           __attribute__((unused))
4350
 
                                           gconstpointer user_data){
4351
 
  __attribute__((cleanup(cleanup_close)))
4352
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4353
 
  g_assert_cmpint(epoll_fd, >=, 0);
4354
 
  __attribute__((cleanup(string_set_clear)))
4355
 
    string_set cancelled_filenames = {};
4356
 
  const mono_microsecs current_time = 0;
4357
 
 
4358
 
  int pipefds[2];
4359
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4360
 
 
4361
 
  /* "sufficient to read at least one event." - inotify(7) */
4362
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4363
 
                                  + NAME_MAX + 1);
4364
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4365
 
  struct {
4366
 
    struct inotify_event event;
4367
 
    char name_buffer[NAME_MAX + 1];
4368
 
  } ievent_buffer;
4369
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4370
 
 
4371
 
  const char dummy_file_name[] = "ask.dummy_file_name";
4372
 
  ievent->mask = IN_MOVED_FROM;
4373
 
  ievent->len = sizeof(dummy_file_name);
4374
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4375
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4376
 
                              + sizeof(dummy_file_name));
4377
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4378
 
                  ==, ievent_size);
4379
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4380
 
 
4381
 
  bool quit_now = false;
4382
 
  buffer password = {};
4383
 
  bool mandos_client_exited = false;
4384
 
  bool password_is_read = false;
4385
 
  __attribute__((cleanup(cleanup_queue)))
4386
 
    task_queue *queue = create_queue();
4387
 
  g_assert_nonnull(queue);
4388
 
 
4389
 
  task_context task = {
4390
 
    .func=read_inotify_event,
4391
 
    .epoll_fd=epoll_fd,
4392
 
    .fd=pipefds[0],
4393
 
    .quit_now=&quit_now,
4394
 
    .password=&password,
4395
 
    .filename=strdup("/nonexistent"),
4396
 
    .cancelled_filenames=&cancelled_filenames,
4397
 
    .current_time=&current_time,
4398
 
    .mandos_client_exited=&mandos_client_exited,
4399
 
    .password_is_read=&password_is_read,
4400
 
  };
4401
 
  task.func(task, queue);
4402
 
  g_assert_false(quit_now);
4403
 
  g_assert_true(queue->next_run == 0);
4404
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4405
 
 
4406
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4407
 
        .func=read_inotify_event,
4408
 
        .epoll_fd=epoll_fd,
4409
 
        .fd=pipefds[0],
4410
 
        .quit_now=&quit_now,
4411
 
        .password=&password,
4412
 
        .filename=task.filename,
4413
 
        .cancelled_filenames=&cancelled_filenames,
4414
 
        .current_time=&current_time,
4415
 
        .mandos_client_exited=&mandos_client_exited,
4416
 
        .password_is_read=&password_is_read,
4417
 
      }));
4418
 
 
4419
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4420
 
                                   EPOLLIN | EPOLLRDHUP));
4421
 
 
4422
 
  __attribute__((cleanup(cleanup_string)))
4423
 
    char *filename = NULL;
4424
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4425
 
                           dummy_file_name), >, 0);
4426
 
  g_assert_nonnull(filename);
4427
 
  g_assert_true(string_set_contains(*task.cancelled_filenames,
4428
 
                                    filename));
4429
 
}
4430
 
 
4431
4107
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4432
4108
                                              test_fixture *fixture,
4433
4109
                                              __attribute__((unused))
4666
4342
                                   EPOLLIN | EPOLLRDHUP));
4667
4343
}
4668
4344
 
4669
 
static void
4670
 
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
4671
 
                                              test_fixture *fixture,
4672
 
                                              __attribute__((unused))
4673
 
                                              gconstpointer
4674
 
                                              user_data){
4675
 
  __attribute__((cleanup(cleanup_close)))
4676
 
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4677
 
  g_assert_cmpint(epoll_fd, >=, 0);
4678
 
  __attribute__((cleanup(string_set_clear)))
4679
 
    string_set cancelled_filenames = {};
4680
 
  const mono_microsecs current_time = 0;
4681
 
 
4682
 
  int pipefds[2];
4683
 
  g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4684
 
 
4685
 
  /* "sufficient to read at least one event." - inotify(7) */
4686
 
  const size_t ievent_max_size = (sizeof(struct inotify_event)
4687
 
                                  + NAME_MAX + 1);
4688
 
  g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4689
 
  struct {
4690
 
    struct inotify_event event;
4691
 
    char name_buffer[NAME_MAX + 1];
4692
 
  } ievent_buffer;
4693
 
  struct inotify_event *const ievent = &ievent_buffer.event;
4694
 
 
4695
 
  const char dummy_file_name[] = "ignored.dummy_file_name";
4696
 
  ievent->mask = IN_MOVED_FROM;
4697
 
  ievent->len = sizeof(dummy_file_name);
4698
 
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4699
 
  const size_t ievent_size = (sizeof(struct inotify_event)
4700
 
                              + sizeof(dummy_file_name));
4701
 
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4702
 
                  ==, ievent_size);
4703
 
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4704
 
 
4705
 
  bool quit_now = false;
4706
 
  buffer password = {};
4707
 
  bool mandos_client_exited = false;
4708
 
  bool password_is_read = false;
4709
 
  __attribute__((cleanup(cleanup_queue)))
4710
 
    task_queue *queue = create_queue();
4711
 
  g_assert_nonnull(queue);
4712
 
 
4713
 
  task_context task = {
4714
 
    .func=read_inotify_event,
4715
 
    .epoll_fd=epoll_fd,
4716
 
    .fd=pipefds[0],
4717
 
    .quit_now=&quit_now,
4718
 
    .password=&password,
4719
 
    .filename=strdup("/nonexistent"),
4720
 
    .cancelled_filenames=&cancelled_filenames,
4721
 
    .current_time=&current_time,
4722
 
    .mandos_client_exited=&mandos_client_exited,
4723
 
    .password_is_read=&password_is_read,
4724
 
  };
4725
 
  task.func(task, queue);
4726
 
  g_assert_false(quit_now);
4727
 
  g_assert_true(queue->next_run == 0);
4728
 
  g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4729
 
 
4730
 
  g_assert_nonnull(find_matching_task(queue, (task_context){
4731
 
        .func=read_inotify_event,
4732
 
        .epoll_fd=epoll_fd,
4733
 
        .fd=pipefds[0],
4734
 
        .quit_now=&quit_now,
4735
 
        .password=&password,
4736
 
        .filename=task.filename,
4737
 
        .cancelled_filenames=&cancelled_filenames,
4738
 
        .current_time=&current_time,
4739
 
        .mandos_client_exited=&mandos_client_exited,
4740
 
        .password_is_read=&password_is_read,
4741
 
      }));
4742
 
 
4743
 
  g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4744
 
                                   EPOLLIN | EPOLLRDHUP));
4745
 
 
4746
 
  __attribute__((cleanup(cleanup_string)))
4747
 
    char *filename = NULL;
4748
 
  g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4749
 
                           dummy_file_name), >, 0);
4750
 
  g_assert_nonnull(filename);
4751
 
  g_assert_false(string_set_contains(cancelled_filenames, filename));
4752
 
}
4753
 
 
4754
4345
static
4755
4346
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4756
4347
                                               test_fixture *fixture,
5676
5267
                                            __attribute__((unused))
5677
5268
                                            gconstpointer user_data){
5678
5269
  __attribute__((cleanup(cleanup_close)))
5679
 
    const int epoll_fd = open("/dev/null",
5680
 
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
5270
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
5681
5271
  __attribute__((cleanup(cleanup_string)))
5682
5272
    char *const question_filename = strdup("/nonexistent/question");
5683
5273
  g_assert_nonnull(question_filename);
5812
5402
  char write_data[PIPE_BUF];
5813
5403
  {
5814
5404
    /* Construct test password buffer */
5815
 
    /* Start with + since that is what the real protocol uses */
 
5405
    /* Start with + since that is what the real procotol uses */
5816
5406
    write_data[0] = '+';
5817
5407
    /* Set a special character at string end just to mark the end */
5818
5408
    write_data[sizeof(write_data)-2] = 'y';
5970
5560
  char *const filename = strdup("/nonexistent/socket");
5971
5561
  __attribute__((cleanup(string_set_clear)))
5972
5562
    string_set cancelled_filenames = {};
5973
 
  int socketfds[2];
5974
 
 
5975
 
  /* Find a message size which triggers EMSGSIZE */
5976
 
  __attribute__((cleanup(cleanup_string)))
5977
 
    char *message_buffer = NULL;
5978
 
  size_t message_size = PIPE_BUF + 1;
5979
 
  for(ssize_t ssret = 0; ssret >= 0; message_size += 1024){
5980
 
    if(message_size >= 1024*1024*1024){ /* 1 GiB */
5981
 
      g_test_skip("Skipping EMSGSIZE test: Will not try 1GiB");
5982
 
      return;
5983
 
    }
5984
 
    message_buffer = realloc(message_buffer, message_size);
5985
 
    if(message_buffer == NULL){
5986
 
      g_test_skip("Skipping EMSGSIZE test");
5987
 
      g_test_message("Failed to malloc() %" PRIuMAX " bytes",
5988
 
                     (uintmax_t)message_size);
5989
 
      return;
5990
 
    }
5991
 
    /* Fill buffer with 'x' */
5992
 
    memset(message_buffer, 'x', message_size);
5993
 
    /* Create a new socketpair for each message size to avoid having
5994
 
       to empty the pipe by reading the message to a separate buffer
5995
 
    */
5996
 
    g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5997
 
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5998
 
                               socketfds), ==, 0);
5999
 
    ssret = send(socketfds[1], message_buffer, message_size,
6000
 
                 MSG_NOSIGNAL);
6001
 
    error_t saved_errno = errno;
6002
 
    g_assert_cmpint(close(socketfds[0]), ==, 0);
6003
 
    g_assert_cmpint(close(socketfds[1]), ==, 0);
6004
 
 
6005
 
    if(ssret < 0){
6006
 
      if(saved_errno != EMSGSIZE) {
6007
 
        g_test_skip("Skipping EMSGSIZE test");
6008
 
        g_test_message("Error on send(): %s", strerror(saved_errno));
6009
 
        return;
6010
 
      }
6011
 
      break;
6012
 
    } else if(ssret != (ssize_t)message_size){
6013
 
      g_test_skip("Skipping EMSGSIZE test");
6014
 
      g_test_message("Partial send(): %" PRIuMAX " of %" PRIdMAX
6015
 
                     " bytes", (uintmax_t)ssret,
6016
 
                     (intmax_t)message_size);
6017
 
      return;
6018
 
    }
6019
 
  }
6020
 
  g_test_message("EMSGSIZE triggered by %" PRIdMAX " bytes",
6021
 
                 (intmax_t)message_size);
6022
 
 
6023
 
  buffer password = {
6024
 
    .data=message_buffer,
6025
 
    .length=message_size - 2,   /* Compensate for added '+' and NUL */
6026
 
    .allocated=message_size,
 
5563
  const size_t oversized = 1024*1024; /* Limit seems to be 212960 */
 
5564
  __attribute__((cleanup(cleanup_buffer)))
 
5565
    buffer password = {
 
5566
    .data=malloc(oversized),
 
5567
    .length=oversized,
 
5568
    .allocated=oversized,
6027
5569
  };
 
5570
  g_assert_nonnull(password.data);
6028
5571
  if(mlock(password.data, password.allocated) != 0){
6029
5572
    g_assert_true(errno == EPERM or errno == ENOMEM);
6030
5573
  }
 
5574
  /* Construct test password buffer */
 
5575
  /* Start with + since that is what the real procotol uses */
 
5576
  password.data[0] = '+';
 
5577
  /* Set a special character at string end just to mark the end */
 
5578
  password.data[oversized-3] = 'y';
 
5579
  /* Set NUL at buffer end, as suggested by the protocol */
 
5580
  password.data[oversized-2] = '\0';
 
5581
  /* Fill rest of password with 'x' */
 
5582
  memset(password.data+1, 'x', oversized-3);
6031
5583
 
6032
5584
  __attribute__((cleanup(cleanup_queue)))
6033
5585
    task_queue *queue = create_queue();
6034
5586
  g_assert_nonnull(queue);
 
5587
  int socketfds[2];
6035
5588
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6036
5589
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6037
5590
                             socketfds), ==, 0);
6124
5677
                                            __attribute__((unused))
6125
5678
                                            gconstpointer user_data){
6126
5679
  __attribute__((cleanup(cleanup_close)))
6127
 
    const int epoll_fd = open("/dev/null",
6128
 
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
5680
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
6129
5681
  __attribute__((cleanup(cleanup_string)))
6130
5682
    char *const question_filename = strdup("/nonexistent/question");
6131
5683
  g_assert_nonnull(question_filename);
6394
5946
                                              const char *const
6395
5947
                                              dirname){
6396
5948
  __attribute__((cleanup(cleanup_close)))
6397
 
    const int devnull_fd = open("/dev/null",
6398
 
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
 
5949
    const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
6399
5950
  g_assert_cmpint(devnull_fd, >=, 0);
6400
5951
  __attribute__((cleanup(cleanup_close)))
6401
5952
    const int real_stderr_fd = dup(STDERR_FILENO);
7944
7495
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7945
7496
  test_add("/queue/create", test_create_queue);
7946
7497
  test_add("/queue/add", test_add_to_queue);
7947
 
  test_add("/queue/add/overflow", test_add_to_queue_overflow);
7948
7498
  test_add("/queue/has_question/empty",
7949
7499
           test_queue_has_question_empty);
7950
7500
  test_add("/queue/has_question/false",
8037
7587
              test_add_inotify_dir_watch);
8038
7588
  test_add_st("/task-creators/add_inotify_dir_watch/fail",
8039
7589
              test_add_inotify_dir_watch_fail);
8040
 
  test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory",
8041
 
              test_add_inotify_dir_watch_nondir);
8042
7590
  test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
8043
7591
              test_add_inotify_dir_watch_EAGAIN);
8044
7592
  test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
8045
7593
              test_add_inotify_dir_watch_IN_CLOSE_WRITE);
8046
7594
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
8047
7595
              test_add_inotify_dir_watch_IN_MOVED_TO);
8048
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
8049
 
              test_add_inotify_dir_watch_IN_MOVED_FROM);
8050
 
  test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
8051
 
              test_add_inotify_dir_watch_IN_EXCL_UNLINK);
8052
7596
  test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
8053
7597
              test_add_inotify_dir_watch_IN_DELETE);
8054
7598
  test_add_st("/task/read_inotify_event/readerror",
8063
7607
              test_read_inotify_event_IN_CLOSE_WRITE);
8064
7608
  test_add_st("/task/read_inotify_event/IN_MOVED_TO",
8065
7609
              test_read_inotify_event_IN_MOVED_TO);
8066
 
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
8067
 
              test_read_inotify_event_IN_MOVED_FROM);
8068
7610
  test_add_st("/task/read_inotify_event/IN_DELETE",
8069
7611
              test_read_inotify_event_IN_DELETE);
8070
7612
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
8071
7613
              test_read_inotify_event_IN_CLOSE_WRITE_badname);
8072
7614
  test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
8073
7615
              test_read_inotify_event_IN_MOVED_TO_badname);
8074
 
  test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
8075
 
              test_read_inotify_event_IN_MOVED_FROM_badname);
8076
7616
  test_add_st("/task/read_inotify_event/IN_DELETE/badname",
8077
7617
              test_read_inotify_event_IN_DELETE_badname);
8078
7618
  test_add_st("/task/open_and_parse_question/ENOENT",
8175
7715
  g_option_context_set_help_enabled(context, FALSE);
8176
7716
  g_option_context_set_ignore_unknown_options(context, TRUE);
8177
7717
 
8178
 
  gboolean should_run_tests = FALSE;
 
7718
  gboolean run_tests = FALSE;
8179
7719
  GOptionEntry entries[] = {
8180
7720
    { "test", 0, 0, G_OPTION_ARG_NONE,
8181
 
      &should_run_tests, "Run tests", NULL },
 
7721
      &run_tests, "Run tests", NULL },
8182
7722
    { NULL }
8183
7723
  };
8184
7724
  g_option_context_add_main_entries(context, entries, NULL);
8191
7731
  }
8192
7732
 
8193
7733
  g_option_context_free(context);
8194
 
  return should_run_tests != FALSE;
 
7734
  return run_tests != FALSE;
8195
7735
}