/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: 2021-02-03 23:10:42 UTC
  • Revision ID: teddy@recompile.se-20210203231042-2z3egrvpo1zt7nej
mandos-ctl: Fix bad test for command.Remove and related minor issues

The test for command.Remove removes all clients from the spy server,
and then loops over all clients, looking for the corresponding Remove
command as recorded by the spy server.  But since since there aren't
any clients left after they were removed, no assertions are made, and
the test therefore does nothing.  Fix this.

In tests for command.Approve and command.Deny, add checks that clients
were not somehow removed by the command (in which case, likewise, no
assertions are made).

Add related checks to TestPropertySetterCmd.runTest; i.e. test that a
sequence is not empty before looping over it and making assertions.

* mandos-ctl (TestBaseCommands.test_Remove): Save a copy of the
  original "clients" dict, and loop over those instead.  Add assertion
  that all clients were indeed removed.  Also fix the code which looks
  for the Remove command, which now needs to actually work.
  (TestBaseCommands.test_Approve, TestBaseCommands.test_Deny): Add
  assertion that there are still clients before looping over them.
  (TestPropertySetterCmd.runTest): Add assertion that the list of
  values to get is not empty before looping over them.  Also add check
  that there are still clients before looping over clients.

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 Teddy Hogeborn
6
 
 * Copyright © 2019 Björn Påhlsson
 
5
 * Copyright © 2019-2021 Teddy Hogeborn
 
6
 * Copyright © 2019-2021 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
27
 
#include <inttypes.h>           /* uintmax_t, PRIuMAX, PRIdMAX,
28
 
                                   intmax_t, uint32_t, SCNx32,
29
 
                                   SCNuMAX, SCNxMAX */
30
 
#include <stddef.h>             /* size_t */
 
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 */
31
33
#include <sys/types.h>          /* pid_t, uid_t, gid_t, getuid(),
32
34
                                   getpid() */
33
35
#include <stdbool.h>            /* bool, true, false */
40
42
                                   NSIG, sigismember(), SA_ONSTACK,
41
43
                                   SIG_DFL, SIG_IGN, SIGINT, SIGQUIT,
42
44
                                   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() */
43
52
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE,
44
 
                                   malloc(), free(), strtoumax(),
45
 
                                   realloc(), setenv(), calloc(),
46
 
                                   mkdtemp(), mkostemp() */
 
53
                                   malloc(), free(), realloc(),
 
54
                                   setenv(), calloc(), mkdtemp(),
 
55
                                   mkostemp() */
47
56
#include <iso646.h>             /* not, or, and, xor */
48
57
#include <error.h>              /* error() */
49
58
#include <sysexits.h>           /* EX_USAGE, EX_OSERR, EX_OSFILE */
50
59
#include <errno.h>              /* errno, error_t, EACCES,
51
 
                                   ENAMETOOLONG, ENOENT, EEXIST,
52
 
                                   ECHILD, EPERM, ENOMEM, EAGAIN,
53
 
                                   EINTR, ENOBUFS, EADDRINUSE,
 
60
                                   ENAMETOOLONG, ENOENT, ENOTDIR,
 
61
                                   ENOMEM, EEXIST, ECHILD, EPERM,
 
62
                                   EAGAIN, EINTR, ENOBUFS, EADDRINUSE,
54
63
                                   ECONNREFUSED, ECONNRESET,
55
64
                                   ETOOMANYREFS, EMSGSIZE, EBADF,
56
65
                                   EINVAL */
57
66
#include <string.h>             /* strdup(), memcpy(),
58
67
                                   explicit_bzero(), memset(),
59
68
                                   strcmp(), strlen(), strncpy(),
60
 
                                   memcmp(), basename() */
 
69
                                   memcmp(), basename(), strerror() */
61
70
#include <argz.h>               /* argz_create(), argz_count(),
62
71
                                   argz_extract(), argz_next(),
63
72
                                   argz_add() */
73
82
                                   ARGP_ERR_UNKNOWN, ARGP_KEY_ARGS,
74
83
                                   struct argp, argp_parse(),
75
84
                                   ARGP_NO_EXIT */
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() */
 
85
#include <stdint.h>             /* SIZE_MAX, uint32_t */
83
86
#include <sys/mman.h>           /* munlock(), mlock() */
84
87
#include <fcntl.h>              /* O_CLOEXEC, O_NONBLOCK, fcntl(),
85
88
                                   F_GETFD, F_GETFL, FD_CLOEXEC,
86
 
                                   open(), O_WRONLY, O_RDONLY */
 
89
                                   open(), O_WRONLY, O_NOCTTY,
 
90
                                   O_RDONLY, O_NOFOLLOW */
87
91
#include <sys/wait.h>           /* waitpid(), WNOHANG, WIFEXITED(),
88
92
                                   WEXITSTATUS() */
89
93
#include <limits.h>             /* PIPE_BUF, NAME_MAX, INT_MAX */
90
94
#include <sys/inotify.h>        /* inotify_init1(), IN_NONBLOCK,
91
95
                                   IN_CLOEXEC, inotify_add_watch(),
92
96
                                   IN_CLOSE_WRITE, IN_MOVED_TO,
93
 
                                   IN_DELETE, struct inotify_event */
 
97
                                   IN_MOVED_FROM, IN_DELETE,
 
98
                                   IN_EXCL_UNLINK, IN_ONLYDIR,
 
99
                                   struct inotify_event */
94
100
#include <fnmatch.h>            /* fnmatch(), FNM_FILE_NAME */
95
 
#include <stdio.h>              /* asprintf(), FILE, fopen(),
96
 
                                   getline(), sscanf(), feof(),
97
 
                                   ferror(), fclose(), stderr,
98
 
                                   rename(), fdopen(), fprintf(),
99
 
                                   fscanf() */
 
101
#include <stdio.h>              /* asprintf(), FILE, stderr, fopen(),
 
102
                                   fclose(), getline(), sscanf(),
 
103
                                   feof(), ferror(), rename(),
 
104
                                   fdopen(), fprintf(), fscanf() */
100
105
#include <glib.h>    /* GKeyFile, g_key_file_free(), g_key_file_new(),
101
106
                        GError, g_key_file_load_from_file(),
102
107
                        G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
107
112
                        g_assert_null(), g_assert_false(),
108
113
                        g_assert_cmpint(), g_assert_cmpuint(),
109
114
                        g_test_skip(), g_assert_cmpstr(),
110
 
                        g_test_init(), g_test_add(), g_test_run(),
111
 
                        GOptionContext, g_option_context_new(),
 
115
                        g_test_message(), g_test_init(), g_test_add(),
 
116
                        g_test_run(), GOptionContext,
 
117
                        g_option_context_new(),
112
118
                        g_option_context_set_help_enabled(), FALSE,
113
119
                        g_option_context_set_ignore_unknown_options(),
114
120
                        gboolean, GOptionEntry, G_OPTION_ARG_NONE,
145
151
  mono_microsecs next_run;
146
152
} __attribute__((designated_init)) task_queue;
147
153
 
148
 
/* "func_type" - A function type for task functions
 
154
/* "task_func" - A function type for task functions
149
155
 
150
156
   I.e. functions for the code which runs when a task is run, all have
151
157
   this type */
431
437
    case EACCES:
432
438
    case ENAMETOOLONG:
433
439
    case ENOENT:
 
440
    case ENOTDIR:
434
441
      return EX_OSFILE;
435
442
    default:
436
443
      return EX_OSERR;
647
654
 
648
655
__attribute__((nonnull, warn_unused_result))
649
656
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
  }
650
664
  const size_t needed_size = sizeof(task_context)*(queue->length + 1);
651
665
  if(needed_size > (queue->allocated)){
652
666
    task_context *const new_tasks = realloc(queue->tasks,
857
871
  }
858
872
  close(pipefds[1]);
859
873
 
 
874
  if(pid == -1){
 
875
    error(0, errno, "Failed to fork()");
 
876
    close(pipefds[0]);
 
877
    return false;
 
878
  }
 
879
 
860
880
  if(not add_to_queue(queue, (task_context){
861
881
        .func=wait_for_mandos_client_exit,
862
882
        .pid=pid,
1017
1037
    return false;
1018
1038
  }
1019
1039
 
1020
 
  if(inotify_add_watch(fd, dir, IN_CLOSE_WRITE
1021
 
                       | IN_MOVED_TO | IN_DELETE)
 
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)
1022
1043
     == -1){
1023
1044
    error(0, errno, "Failed to create inotify watch on %s", dir);
1024
1045
    return false;
1119
1140
             immediately */
1120
1141
          queue->next_run = 1;
1121
1142
        }
1122
 
      } else if(ievent->mask & IN_DELETE){
 
1143
      } else if(ievent->mask & (IN_MOVED_FROM | IN_DELETE)){
1123
1144
        if(not string_set_add(cancelled_filenames,
1124
1145
                              question_filename)){
1125
1146
          error(0, errno, "Could not add question %s to"
1175
1196
  bool *const password_is_read = task.password_is_read;
1176
1197
 
1177
1198
  /* We use the GLib "Key-value file parser" functions to parse the
1178
 
     question file.  See <https://www.freedesktop.org/wiki/Software
1179
 
     /systemd/PasswordAgents/> for specification of contents */
 
1199
     question file.  See <https://systemd.io/PASSWORD_AGENTS/> for
 
1200
     specification of contents */
1180
1201
  __attribute__((nonnull))
1181
1202
    void cleanup_g_key_file(GKeyFile **key_file){
1182
1203
    if(*key_file != NULL){
1472
1493
         not. You may but don't have to include a final NUL byte in
1473
1494
         your message.
1474
1495
 
1475
 
         — <https://www.freedesktop.org/wiki/Software/systemd/
1476
 
         PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
 
1496
         — <https://systemd.io/PASSWORD_AGENTS/> (Tue, 15 Sep 2020
 
1497
         14:24:20 GMT)
1477
1498
      */
1478
1499
      send_buffer[0] = '+';     /* Prefix with "+" */
1479
1500
      /* Always add an extra NUL */
1484
1505
      errno = 0;
1485
1506
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1486
1507
                           MSG_NOSIGNAL);
1487
 
      const error_t saved_errno = errno;
 
1508
      const error_t saved_errno = (ssret < 0) ? errno : 0;
1488
1509
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1489
1510
      explicit_bzero(send_buffer, send_buffer_length);
1490
1511
#else
1508
1529
          /* Retry, below */
1509
1530
          break;
1510
1531
        case EMSGSIZE:
1511
 
          error(0, 0, "Password of size %" PRIuMAX " is too big",
1512
 
                (uintmax_t)password->length);
 
1532
          error(0, saved_errno, "Password of size %" PRIuMAX
 
1533
                " is too big", (uintmax_t)password->length);
1513
1534
#if __GNUC__ < 7
1514
1535
          /* FALLTHROUGH */
1515
1536
#else
1517
1538
#endif
1518
1539
        case 0:
1519
1540
          if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1520
 
            error(0, 0, "Password only partially sent to socket");
 
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);
1521
1544
          }
1522
1545
#if __GNUC__ < 7
1523
1546
          /* FALLTHROUGH */
1879
1902
  g_assert_true(queue->tasks[0].func == dummy_func);
1880
1903
}
1881
1904
 
 
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
 
1882
1928
static void dummy_func(__attribute__((unused))
1883
1929
                       const task_context task,
1884
1930
                       __attribute__((unused))
2155
2201
    }
2156
2202
    exit(EXIT_SUCCESS);
2157
2203
  }
 
2204
  if(pid == -1){
 
2205
    error(EXIT_FAILURE, errno, "Failed to fork()");
 
2206
  }
 
2207
 
2158
2208
  int status;
2159
2209
  waitpid(pid, &status, 0);
2160
2210
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2222
2272
 
2223
2273
  {
2224
2274
    __attribute__((cleanup(cleanup_close)))
2225
 
      const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
 
2275
      const int devnull_fd = open("/dev/null",
 
2276
                                  O_WRONLY | O_CLOEXEC | O_NOCTTY);
2226
2277
    g_assert_cmpint(devnull_fd, >=, 0);
2227
2278
    __attribute__((cleanup(cleanup_close)))
2228
2279
      const int real_stderr_fd = dup(STDERR_FILENO);
2252
2303
    {
2253
2304
      __attribute__((cleanup(cleanup_close)))
2254
2305
        const int devnull_fd = open("/dev/null",
2255
 
                                    O_WRONLY | O_CLOEXEC);
 
2306
                                    O_WRONLY | O_CLOEXEC | O_NOCTTY);
2256
2307
      g_assert_cmpint(devnull_fd, >=, 0);
2257
2308
      __attribute__((cleanup(cleanup_close)))
2258
2309
        const int real_stderr_fd = dup(STDERR_FILENO);
2903
2954
 
2904
2955
  __attribute__((cleanup(cleanup_close)))
2905
2956
    const int devnull_fd = open("/dev/null",
2906
 
                                O_WRONLY | O_CLOEXEC);
 
2957
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
2907
2958
  g_assert_cmpint(devnull_fd, >=, 0);
2908
2959
  __attribute__((cleanup(cleanup_close)))
2909
2960
    const int real_stderr_fd = dup(STDERR_FILENO);
2974
3025
 
2975
3026
  __attribute__((cleanup(cleanup_close)))
2976
3027
    const int devnull_fd = open("/dev/null",
2977
 
                                O_WRONLY | O_CLOEXEC);
 
3028
                                O_WRONLY | O_CLOEXEC, O_NOCTTY);
2978
3029
  g_assert_cmpint(devnull_fd, >=, 0);
2979
3030
  __attribute__((cleanup(cleanup_close)))
2980
3031
    const int real_stderr_fd = dup(STDERR_FILENO);
3018
3069
    buffer password = {};
3019
3070
 
3020
3071
  /* Reading /proc/self/mem from offset 0 will always give EIO */
3021
 
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
 
3072
  const int fd = open("/proc/self/mem",
 
3073
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
3022
3074
 
3023
3075
  bool password_is_read = false;
3024
3076
  bool quit_now = false;
3452
3504
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3453
3505
}
3454
3506
 
 
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
 
3455
3545
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3456
3546
                                              test_fixture *fixture,
3457
3547
                                              __attribute__((unused))
3672
3762
}
3673
3763
 
3674
3764
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
3675
3837
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3676
3838
                                          test_fixture *fixture,
3677
3839
                                          __attribute__((unused))
3735
3897
  g_assert_cmpint(rmdir(tempdir), ==, 0);
3736
3898
}
3737
3899
 
 
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
 
3738
3976
static void test_read_inotify_event_readerror(__attribute__((unused))
3739
3977
                                              test_fixture *fixture,
3740
3978
                                              __attribute__((unused))
3746
3984
  const mono_microsecs current_time = 0;
3747
3985
 
3748
3986
  /* Reading /proc/self/mem from offset 0 will always result in EIO */
3749
 
  const int fd = open("/proc/self/mem", O_RDONLY | O_CLOEXEC);
 
3987
  const int fd = open("/proc/self/mem",
 
3988
                      O_RDONLY | O_CLOEXEC | O_NOCTTY);
3750
3989
 
3751
3990
  bool quit_now = false;
3752
3991
  __attribute__((cleanup(cleanup_queue)))
4104
4343
      }));
4105
4344
}
4106
4345
 
 
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
 
4107
4431
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4108
4432
                                              test_fixture *fixture,
4109
4433
                                              __attribute__((unused))
4342
4666
                                   EPOLLIN | EPOLLRDHUP));
4343
4667
}
4344
4668
 
 
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
 
4345
4754
static
4346
4755
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4347
4756
                                               test_fixture *fixture,
5267
5676
                                            __attribute__((unused))
5268
5677
                                            gconstpointer user_data){
5269
5678
  __attribute__((cleanup(cleanup_close)))
5270
 
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
 
5679
    const int epoll_fd = open("/dev/null",
 
5680
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
5271
5681
  __attribute__((cleanup(cleanup_string)))
5272
5682
    char *const question_filename = strdup("/nonexistent/question");
5273
5683
  g_assert_nonnull(question_filename);
5402
5812
  char write_data[PIPE_BUF];
5403
5813
  {
5404
5814
    /* Construct test password buffer */
5405
 
    /* Start with + since that is what the real procotol uses */
 
5815
    /* Start with + since that is what the real protocol uses */
5406
5816
    write_data[0] = '+';
5407
5817
    /* Set a special character at string end just to mark the end */
5408
5818
    write_data[sizeof(write_data)-2] = 'y';
5560
5970
  char *const filename = strdup("/nonexistent/socket");
5561
5971
  __attribute__((cleanup(string_set_clear)))
5562
5972
    string_set cancelled_filenames = {};
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,
 
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,
5569
6027
  };
5570
 
  g_assert_nonnull(password.data);
5571
6028
  if(mlock(password.data, password.allocated) != 0){
5572
6029
    g_assert_true(errno == EPERM or errno == ENOMEM);
5573
6030
  }
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);
5583
6031
 
5584
6032
  __attribute__((cleanup(cleanup_queue)))
5585
6033
    task_queue *queue = create_queue();
5586
6034
  g_assert_nonnull(queue);
5587
 
  int socketfds[2];
5588
6035
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5589
6036
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5590
6037
                             socketfds), ==, 0);
5677
6124
                                            __attribute__((unused))
5678
6125
                                            gconstpointer user_data){
5679
6126
  __attribute__((cleanup(cleanup_close)))
5680
 
    const int epoll_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
 
6127
    const int epoll_fd = open("/dev/null",
 
6128
                              O_WRONLY | O_CLOEXEC | O_NOCTTY);
5681
6129
  __attribute__((cleanup(cleanup_string)))
5682
6130
    char *const question_filename = strdup("/nonexistent/question");
5683
6131
  g_assert_nonnull(question_filename);
5946
6394
                                              const char *const
5947
6395
                                              dirname){
5948
6396
  __attribute__((cleanup(cleanup_close)))
5949
 
    const int devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
 
6397
    const int devnull_fd = open("/dev/null",
 
6398
                                O_WRONLY | O_CLOEXEC | O_NOCTTY);
5950
6399
  g_assert_cmpint(devnull_fd, >=, 0);
5951
6400
  __attribute__((cleanup(cleanup_close)))
5952
6401
    const int real_stderr_fd = dup(STDERR_FILENO);
7495
7944
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7496
7945
  test_add("/queue/create", test_create_queue);
7497
7946
  test_add("/queue/add", test_add_to_queue);
 
7947
  test_add("/queue/add/overflow", test_add_to_queue_overflow);
7498
7948
  test_add("/queue/has_question/empty",
7499
7949
           test_queue_has_question_empty);
7500
7950
  test_add("/queue/has_question/false",
7587
8037
              test_add_inotify_dir_watch);
7588
8038
  test_add_st("/task-creators/add_inotify_dir_watch/fail",
7589
8039
              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);
7590
8042
  test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
7591
8043
              test_add_inotify_dir_watch_EAGAIN);
7592
8044
  test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
7593
8045
              test_add_inotify_dir_watch_IN_CLOSE_WRITE);
7594
8046
  test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
7595
8047
              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);
7596
8052
  test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
7597
8053
              test_add_inotify_dir_watch_IN_DELETE);
7598
8054
  test_add_st("/task/read_inotify_event/readerror",
7607
8063
              test_read_inotify_event_IN_CLOSE_WRITE);
7608
8064
  test_add_st("/task/read_inotify_event/IN_MOVED_TO",
7609
8065
              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);
7610
8068
  test_add_st("/task/read_inotify_event/IN_DELETE",
7611
8069
              test_read_inotify_event_IN_DELETE);
7612
8070
  test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
7613
8071
              test_read_inotify_event_IN_CLOSE_WRITE_badname);
7614
8072
  test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
7615
8073
              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);
7616
8076
  test_add_st("/task/read_inotify_event/IN_DELETE/badname",
7617
8077
              test_read_inotify_event_IN_DELETE_badname);
7618
8078
  test_add_st("/task/open_and_parse_question/ENOENT",
7715
8175
  g_option_context_set_help_enabled(context, FALSE);
7716
8176
  g_option_context_set_ignore_unknown_options(context, TRUE);
7717
8177
 
7718
 
  gboolean run_tests = FALSE;
 
8178
  gboolean should_run_tests = FALSE;
7719
8179
  GOptionEntry entries[] = {
7720
8180
    { "test", 0, 0, G_OPTION_ARG_NONE,
7721
 
      &run_tests, "Run tests", NULL },
 
8181
      &should_run_tests, "Run tests", NULL },
7722
8182
    { NULL }
7723
8183
  };
7724
8184
  g_option_context_add_main_entries(context, entries, NULL);
7731
8191
  }
7732
8192
 
7733
8193
  g_option_context_free(context);
7734
 
  return run_tests != FALSE;
 
8194
  return should_run_tests != FALSE;
7735
8195
}