/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: 2024-09-09 01:36:41 UTC
  • Revision ID: teddy@recompile.se-20240909013641-6zu6kx2f7meu134k
Make all required directories when installing

When installing into a normal system, one can assume that target
directories, such as /usr/bin, already exists.  But when installing
into a subdirectory for the purpose of creating a package, one cannot
assume that all directories already exist.  Therefore, when
installing, we must not check if any directories exist, and must
instead always create any directories we want to install into.

* Makefile (confdir/mandos.conf, confdir/clients.conf, install-html):
  Use the "-D" option to "install" instead of creating the directory
  separately.
  (install-server): Move creation of $(CONFDIR) down to before it is
  needed.  Don't check if the $(TMPFILES) or $(SYSUSERS) directories
  exist; instead create them by using the "-D" option to "install".
  Create the $(PREFIX)/sbin directory.  Always use
  "--target-directory" if possible; i.e. if the file name is the same.
  Create the $(DBUSPOLICYDIR) and $(DESTDIR)/etc/init.d directories by
  using the "-D" option to "install".  Don't check if the $(SYSTEMD)
  directory exists; instead create it by using the "-D" option to
  "install".  Create the $(DESTDIR)/etc/default and $(MANDIR)/man8
  directories by using the "-D" option to "install".  Create the
  $(MANDIR)/man5 directories explicitly.
  (install-client-nokey): Remove unnecessary creation of the
  $(CONFDIR) directory.  Don't check if the $(SYSUSERS) directory
  exists; instead create it by using the "-D" option to "install".
  Move the "--directory" argument to be the first argument, for
  clarity.  Create the $(PREFIX)/sbin directory.  Use the "-D"
  argument to "install" when installing
  $(INITRAMFSTOOLS)/hooks/mandos,
  $(INITRAMFSTOOLS)/conf.d/mandos-conf,
  $(INITRAMFSTOOLS)/conf-hooks.d/zz-mandos,
  $(INITRAMFSTOOLS)/scripts/init-premount/mandos,
  $(INITRAMFSTOOLS)/scripts/local-premount/mandos,
  $(DRACUTMODULE)/ask-password-mandos.path, and
  $(DRACUTMODULE)/dracut-module/ask-password-mandos.service.  Create
  the $(MANDIR)/man8 directory.

Reported-By: Erich Eckner <erich@eckner.net>
Thanks: Erich Eckner <erich@eckner.net> for analysis

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* -*- mode: c; coding: utf-8; after-save-hook: (lambda () (let* ((find-build-directory (lambda (try-directory &optional base-directory) (let ((base-directory (or base-directory try-directory))) (cond ((equal try-directory "/") base-directory) ((file-readable-p (concat (file-name-as-directory try-directory) "Makefile")) try-directory) ((funcall find-build-directory (directory-file-name (file-name-directory try-directory)) base-directory)))))) (build-directory (funcall find-build-directory (buffer-file-name))) (local-build-directory (if (fboundp 'file-local-name) (file-local-name build-directory) (or (file-remote-p build-directory 'localname) build-directory))) (command (file-relative-name (file-name-sans-extension (buffer-file-name)) build-directory))) (pcase (progn (if (get-buffer "*Test*") (kill-buffer "*Test*")) (process-file-shell-command (let ((qbdir (shell-quote-argument local-build-directory)) (qcmd (shell-quote-argument command))) (format "cd %s && CFLAGS=-Werror make --silent %s && %s --test --verbose" qbdir qcmd qcmd)) nil "*Test*")) (0 (let ((w (get-buffer-window "*Test*"))) (if w (delete-window w)))) (_ (with-current-buffer "*Test*" (compilation-mode) (cd-absolute build-directory)) (display-buffer "*Test*" '(display-buffer-in-side-window)))))); -*- */
 
1
/* -*- coding: utf-8; lexical-binding: t -*- */
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-2022 Teddy Hogeborn
 
6
 * Copyright © 2019-2022 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
60
                                   ENAMETOOLONG, ENOENT, ENOTDIR,
52
 
                                   EEXIST, ECHILD, EPERM, ENOMEM,
 
61
                                   ENOMEM, EEXIST, ECHILD, EPERM,
53
62
                                   EAGAIN, EINTR, ENOBUFS, EADDRINUSE,
54
63
                                   ECONNREFUSED, ECONNRESET,
55
64
                                   ETOOMANYREFS, EMSGSIZE, EBADF,
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,
95
98
                                   IN_EXCL_UNLINK, IN_ONLYDIR,
96
99
                                   struct inotify_event */
97
100
#include <fnmatch.h>            /* fnmatch(), FNM_FILE_NAME */
98
 
#include <stdio.h>              /* asprintf(), FILE, fopen(),
99
 
                                   getline(), sscanf(), feof(),
100
 
                                   ferror(), fclose(), stderr,
101
 
                                   rename(), fdopen(), fprintf(),
102
 
                                   fscanf() */
 
101
#include <stdio.h>              /* asprintf(), FILE, stderr, fopen(),
 
102
                                   fclose(), getline(), sscanf(),
 
103
                                   feof(), ferror(), rename(),
 
104
                                   fdopen(), fprintf(), fscanf() */
103
105
#include <glib.h>    /* GKeyFile, g_key_file_free(), g_key_file_new(),
104
106
                        GError, g_key_file_load_from_file(),
105
107
                        G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
110
112
                        g_assert_null(), g_assert_false(),
111
113
                        g_assert_cmpint(), g_assert_cmpuint(),
112
114
                        g_test_skip(), g_assert_cmpstr(),
113
 
                        g_test_init(), g_test_add(), g_test_run(),
114
 
                        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(),
115
118
                        g_option_context_set_help_enabled(), FALSE,
116
119
                        g_option_context_set_ignore_unknown_options(),
117
120
                        gboolean, GOptionEntry, G_OPTION_ARG_NONE,
651
654
 
652
655
__attribute__((nonnull, warn_unused_result))
653
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
  }
654
664
  const size_t needed_size = sizeof(task_context)*(queue->length + 1);
655
665
  if(needed_size > (queue->allocated)){
656
666
    task_context *const new_tasks = realloc(queue->tasks,
861
871
  }
862
872
  close(pipefds[1]);
863
873
 
 
874
  if(pid == -1){
 
875
    error(0, errno, "Failed to fork()");
 
876
    close(pipefds[0]);
 
877
    return false;
 
878
  }
 
879
 
864
880
  if(not add_to_queue(queue, (task_context){
865
881
        .func=wait_for_mandos_client_exit,
866
882
        .pid=pid,
1082
1098
  } ievent_buffer;
1083
1099
  struct inotify_event *const ievent = &ievent_buffer.event;
1084
1100
 
 
1101
#if defined(__GNUC__) and __GNUC__ >= 7
 
1102
#pragma GCC diagnostic push
 
1103
  /* ievent is pointing into a struct which is of sufficient size */
 
1104
#pragma GCC diagnostic ignored "-Wstringop-overflow"
 
1105
#endif
1085
1106
  const ssize_t read_length = read(fd, ievent, ievent_size);
 
1107
#if defined(__GNUC__) and __GNUC__ >= 7
 
1108
#pragma GCC diagnostic pop
 
1109
#endif
1086
1110
  if(read_length == 0){ /* EOF */
1087
1111
    error(0, 0, "Got EOF from inotify fd for directory %s", filename);
1088
1112
    *quit_now = true;
1180
1204
  bool *const password_is_read = task.password_is_read;
1181
1205
 
1182
1206
  /* We use the GLib "Key-value file parser" functions to parse the
1183
 
     question file.  See <https://www.freedesktop.org/wiki/Software
1184
 
     /systemd/PasswordAgents/> for specification of contents */
 
1207
     question file.  See <https://systemd.io/PASSWORD_AGENTS/> for
 
1208
     specification of contents */
1185
1209
  __attribute__((nonnull))
1186
1210
    void cleanup_g_key_file(GKeyFile **key_file){
1187
1211
    if(*key_file != NULL){
1464
1488
    if(send_buffer == NULL){
1465
1489
      error(0, errno, "Failed to allocate send_buffer");
1466
1490
    } else {
 
1491
#if defined(__GNUC__) and __GNUC__ >= 5
 
1492
#pragma GCC diagnostic push
 
1493
  /* mlock() does not access the memory */
 
1494
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
 
1495
#endif
1467
1496
      if(mlock(send_buffer, send_buffer_length) != 0){
 
1497
#if defined(__GNUC__) and __GNUC__ >= 5
 
1498
#pragma GCC diagnostic pop
 
1499
#endif
1468
1500
        /* Warn but do not treat as fatal error */
1469
1501
        if(errno != EPERM and errno != ENOMEM){
1470
1502
          error(0, errno, "Failed to lock memory for password"
1477
1509
         not. You may but don't have to include a final NUL byte in
1478
1510
         your message.
1479
1511
 
1480
 
         — <https://www.freedesktop.org/wiki/Software/systemd/
1481
 
         PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
 
1512
         — <https://systemd.io/PASSWORD_AGENTS/> (Tue, 15 Sep 2020
 
1513
         14:24:20 GMT)
1482
1514
      */
1483
1515
      send_buffer[0] = '+';     /* Prefix with "+" */
1484
1516
      /* Always add an extra NUL */
1489
1521
      errno = 0;
1490
1522
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1491
1523
                           MSG_NOSIGNAL);
1492
 
      const error_t saved_errno = errno;
 
1524
      const error_t saved_errno = (ssret < 0) ? errno : 0;
1493
1525
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1494
1526
      explicit_bzero(send_buffer, send_buffer_length);
1495
1527
#else
1513
1545
          /* Retry, below */
1514
1546
          break;
1515
1547
        case EMSGSIZE:
1516
 
          error(0, 0, "Password of size %" PRIuMAX " is too big",
1517
 
                (uintmax_t)password->length);
 
1548
          error(0, saved_errno, "Password of size %" PRIuMAX
 
1549
                " is too big", (uintmax_t)password->length);
1518
1550
#if __GNUC__ < 7
1519
1551
          /* FALLTHROUGH */
1520
1552
#else
1522
1554
#endif
1523
1555
        case 0:
1524
1556
          if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1525
 
            error(0, 0, "Password only partially sent to socket");
 
1557
            error(0, 0, "Password only partially sent to socket %s: %"
 
1558
                  PRIuMAX " out of %" PRIuMAX " bytes sent", filename,
 
1559
                  (uintmax_t)ssret, (uintmax_t)send_buffer_length);
1526
1560
          }
1527
1561
#if __GNUC__ < 7
1528
1562
          /* FALLTHROUGH */
1884
1918
  g_assert_true(queue->tasks[0].func == dummy_func);
1885
1919
}
1886
1920
 
 
1921
static void test_add_to_queue_overflow(__attribute__((unused))
 
1922
                                       test_fixture *fixture,
 
1923
                                       __attribute__((unused))
 
1924
                                       gconstpointer user_data){
 
1925
  __attribute__((cleanup(cleanup_queue)))
 
1926
    task_queue *queue = create_queue();
 
1927
  g_assert_nonnull(queue);
 
1928
  g_assert_true(queue->length == 0);
 
1929
  queue->length = SIZE_MAX / sizeof(task_context); /* fake max size */
 
1930
 
 
1931
  FILE *real_stderr = stderr;
 
1932
  FILE *devnull = fopen("/dev/null", "we");
 
1933
  g_assert_nonnull(devnull);
 
1934
  stderr = devnull;
 
1935
  const bool ret = add_to_queue(queue,
 
1936
                                (task_context){ .func=dummy_func });
 
1937
  g_assert_true(errno == ENOMEM);
 
1938
  g_assert_false(ret);
 
1939
  stderr = real_stderr;
 
1940
  g_assert_cmpint(fclose(devnull), ==, 0);
 
1941
  queue->length = 0;            /* Restore real size */
 
1942
}
 
1943
 
1887
1944
static void dummy_func(__attribute__((unused))
1888
1945
                       const task_context task,
1889
1946
                       __attribute__((unused))
2160
2217
    }
2161
2218
    exit(EXIT_SUCCESS);
2162
2219
  }
 
2220
  if(pid == -1){
 
2221
    error(EXIT_FAILURE, errno, "Failed to fork()");
 
2222
  }
 
2223
 
2163
2224
  int status;
2164
2225
  waitpid(pid, &status, 0);
2165
2226
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2608
2669
  bool password_is_read = false;
2609
2670
  const char helper_directory[] = "/nonexistent";
2610
2671
  const char *const argv[] = { "/bin/sh", "-c",
2611
 
    "echo -n ${MANDOSPLUGINHELPERDIR}", NULL };
 
2672
    "printf %s \"${MANDOSPLUGINHELPERDIR}\"", NULL };
2612
2673
 
2613
2674
  const bool success = start_mandos_client(queue, epoll_fd,
2614
2675
                                           &mandos_client_exited,
4137
4198
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4138
4199
  const size_t ievent_size = (sizeof(struct inotify_event)
4139
4200
                              + sizeof(dummy_file_name));
 
4201
#if defined(__GNUC__) and __GNUC__ >= 11
 
4202
#pragma GCC diagnostic push
 
4203
  /* ievent is pointing into a struct which is of sufficient size */
 
4204
#pragma GCC diagnostic ignored "-Wstringop-overread"
 
4205
#endif
4140
4206
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4141
4207
                  ==, ievent_size);
 
4208
#if defined(__GNUC__) and __GNUC__ >= 11
 
4209
#pragma GCC diagnostic pop
 
4210
#endif
4142
4211
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4143
4212
 
4144
4213
  bool quit_now = false;
4232
4301
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4233
4302
  const size_t ievent_size = (sizeof(struct inotify_event)
4234
4303
                              + sizeof(dummy_file_name));
 
4304
#if defined(__GNUC__) and __GNUC__ >= 11
 
4305
#pragma GCC diagnostic push
 
4306
  /* ievent is pointing into a struct which is of sufficient size */
 
4307
#pragma GCC diagnostic ignored "-Wstringop-overread"
 
4308
#endif
4235
4309
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4236
4310
                  ==, ievent_size);
 
4311
#if defined(__GNUC__) and __GNUC__ >= 11
 
4312
#pragma GCC diagnostic pop
 
4313
#endif
4237
4314
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4238
4315
 
4239
4316
  bool quit_now = false;
4329
4406
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4330
4407
  const size_t ievent_size = (sizeof(struct inotify_event)
4331
4408
                              + sizeof(dummy_file_name));
 
4409
#if defined(__GNUC__) and __GNUC__ >= 11
 
4410
#pragma GCC diagnostic push
 
4411
  /* ievent is pointing into a struct which is of sufficient size */
 
4412
#pragma GCC diagnostic ignored "-Wstringop-overread"
 
4413
#endif
4332
4414
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4333
4415
                  ==, ievent_size);
 
4416
#if defined(__GNUC__) and __GNUC__ >= 11
 
4417
#pragma GCC diagnostic pop
 
4418
#endif
4334
4419
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4335
4420
 
4336
4421
  bool quit_now = false;
4414
4499
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4415
4500
  const size_t ievent_size = (sizeof(struct inotify_event)
4416
4501
                              + sizeof(dummy_file_name));
 
4502
#if defined(__GNUC__) and __GNUC__ >= 11
 
4503
#pragma GCC diagnostic push
 
4504
  /* ievent is pointing into a struct which is of sufficient size */
 
4505
#pragma GCC diagnostic ignored "-Wstringop-overread"
 
4506
#endif
4417
4507
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4418
4508
                  ==, ievent_size);
 
4509
#if defined(__GNUC__) and __GNUC__ >= 11
 
4510
#pragma GCC diagnostic pop
 
4511
#endif
4419
4512
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4420
4513
 
4421
4514
  bool quit_now = false;
4498
4591
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4499
4592
  const size_t ievent_size = (sizeof(struct inotify_event)
4500
4593
                              + sizeof(dummy_file_name));
 
4594
#if defined(__GNUC__) and __GNUC__ >= 11
 
4595
#pragma GCC diagnostic push
 
4596
  /* ievent is pointing into a struct which is of sufficient size */
 
4597
#pragma GCC diagnostic ignored "-Wstringop-overread"
 
4598
#endif
4501
4599
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4502
4600
                  ==, ievent_size);
 
4601
#if defined(__GNUC__) and __GNUC__ >= 11
 
4602
#pragma GCC diagnostic pop
 
4603
#endif
4503
4604
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4504
4605
 
4505
4606
  bool quit_now = false;
4574
4675
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4575
4676
  const size_t ievent_size = (sizeof(struct inotify_event)
4576
4677
                              + sizeof(dummy_file_name));
 
4678
#if defined(__GNUC__) and __GNUC__ >= 11
 
4679
#pragma GCC diagnostic push
 
4680
  /* ievent is pointing into a struct which is of sufficient size */
 
4681
#pragma GCC diagnostic ignored "-Wstringop-overread"
 
4682
#endif
4577
4683
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4578
4684
                  ==, ievent_size);
 
4685
#if defined(__GNUC__) and __GNUC__ >= 11
 
4686
#pragma GCC diagnostic pop
 
4687
#endif
4579
4688
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4580
4689
 
4581
4690
  bool quit_now = false;
4653
4762
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4654
4763
  const size_t ievent_size = (sizeof(struct inotify_event)
4655
4764
                              + sizeof(dummy_file_name));
 
4765
#if defined(__GNUC__) and __GNUC__ >= 11
 
4766
#pragma GCC diagnostic push
 
4767
  /* ievent is pointing into a struct which is of sufficient size */
 
4768
#pragma GCC diagnostic ignored "-Wstringop-overread"
 
4769
#endif
4656
4770
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4657
4771
                  ==, ievent_size);
 
4772
#if defined(__GNUC__) and __GNUC__ >= 11
 
4773
#pragma GCC diagnostic pop
 
4774
#endif
4658
4775
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4659
4776
 
4660
4777
  bool quit_now = false;
4738
4855
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4739
4856
  const size_t ievent_size = (sizeof(struct inotify_event)
4740
4857
                              + sizeof(dummy_file_name));
 
4858
#if defined(__GNUC__) and __GNUC__ >= 11
 
4859
#pragma GCC diagnostic push
 
4860
  /* ievent is pointing into a struct which is of sufficient size */
 
4861
#pragma GCC diagnostic ignored "-Wstringop-overread"
 
4862
#endif
4741
4863
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4742
4864
                  ==, ievent_size);
 
4865
#if defined(__GNUC__) and __GNUC__ >= 11
 
4866
#pragma GCC diagnostic pop
 
4867
#endif
4743
4868
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4744
4869
 
4745
4870
  bool quit_now = false;
5767
5892
  char write_data[PIPE_BUF];
5768
5893
  {
5769
5894
    /* Construct test password buffer */
5770
 
    /* Start with + since that is what the real procotol uses */
 
5895
    /* Start with + since that is what the real protocol uses */
5771
5896
    write_data[0] = '+';
5772
5897
    /* Set a special character at string end just to mark the end */
5773
5898
    write_data[sizeof(write_data)-2] = 'y';
5925
6050
  char *const filename = strdup("/nonexistent/socket");
5926
6051
  __attribute__((cleanup(string_set_clear)))
5927
6052
    string_set cancelled_filenames = {};
5928
 
  const size_t oversized = 1024*1024; /* Limit seems to be 212960 */
5929
 
  __attribute__((cleanup(cleanup_buffer)))
5930
 
    buffer password = {
5931
 
    .data=malloc(oversized),
5932
 
    .length=oversized,
5933
 
    .allocated=oversized,
 
6053
  int socketfds[2];
 
6054
 
 
6055
  /* Find a message size which triggers EMSGSIZE */
 
6056
  __attribute__((cleanup(cleanup_string)))
 
6057
    char *message_buffer = NULL;
 
6058
  size_t message_size = PIPE_BUF + 1;
 
6059
  for(ssize_t ssret = 0; ssret >= 0; message_size += 1024){
 
6060
    if(message_size >= 1024*1024*1024){ /* 1 GiB */
 
6061
      g_test_skip("Skipping EMSGSIZE test: Will not try 1GiB");
 
6062
      return;
 
6063
    }
 
6064
    message_buffer = realloc(message_buffer, message_size);
 
6065
    if(message_buffer == NULL){
 
6066
      g_test_skip("Skipping EMSGSIZE test");
 
6067
      g_test_message("Failed to malloc() %" PRIuMAX " bytes",
 
6068
                     (uintmax_t)message_size);
 
6069
      return;
 
6070
    }
 
6071
    /* Fill buffer with 'x' */
 
6072
    memset(message_buffer, 'x', message_size);
 
6073
    /* Create a new socketpair for each message size to avoid having
 
6074
       to empty the pipe by reading the message to a separate buffer
 
6075
    */
 
6076
    g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
 
6077
                               | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
 
6078
                               socketfds), ==, 0);
 
6079
    ssret = send(socketfds[1], message_buffer, message_size,
 
6080
                 MSG_NOSIGNAL);
 
6081
    error_t saved_errno = errno;
 
6082
    g_assert_cmpint(close(socketfds[0]), ==, 0);
 
6083
    g_assert_cmpint(close(socketfds[1]), ==, 0);
 
6084
 
 
6085
    if(ssret < 0){
 
6086
      if(saved_errno != EMSGSIZE) {
 
6087
        g_test_skip("Skipping EMSGSIZE test");
 
6088
        g_test_message("Error on send(%" PRIuMAX " bytes): %s",
 
6089
                       (uintmax_t)message_size,
 
6090
                       strerror(saved_errno));
 
6091
        return;
 
6092
      }
 
6093
      break;
 
6094
    } else if(ssret != (ssize_t)message_size){
 
6095
      g_test_skip("Skipping EMSGSIZE test");
 
6096
      g_test_message("Partial send(): %" PRIuMAX " of %" PRIdMAX
 
6097
                     " bytes", (uintmax_t)ssret,
 
6098
                     (intmax_t)message_size);
 
6099
      return;
 
6100
    }
 
6101
  }
 
6102
  g_test_message("EMSGSIZE triggered by %" PRIdMAX " bytes",
 
6103
                 (intmax_t)message_size);
 
6104
 
 
6105
  buffer password = {
 
6106
    .data=message_buffer,
 
6107
    .length=message_size - 2,   /* Compensate for added '+' and NUL */
 
6108
    .allocated=message_size,
5934
6109
  };
5935
 
  g_assert_nonnull(password.data);
5936
6110
  if(mlock(password.data, password.allocated) != 0){
5937
6111
    g_assert_true(errno == EPERM or errno == ENOMEM);
5938
6112
  }
5939
 
  /* Construct test password buffer */
5940
 
  /* Start with + since that is what the real procotol uses */
5941
 
  password.data[0] = '+';
5942
 
  /* Set a special character at string end just to mark the end */
5943
 
  password.data[oversized-3] = 'y';
5944
 
  /* Set NUL at buffer end, as suggested by the protocol */
5945
 
  password.data[oversized-2] = '\0';
5946
 
  /* Fill rest of password with 'x' */
5947
 
  memset(password.data+1, 'x', oversized-3);
5948
6113
 
5949
6114
  __attribute__((cleanup(cleanup_queue)))
5950
6115
    task_queue *queue = create_queue();
5951
6116
  g_assert_nonnull(queue);
5952
 
  int socketfds[2];
5953
6117
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5954
6118
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5955
6119
                             socketfds), ==, 0);
7862
8026
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
7863
8027
  test_add("/queue/create", test_create_queue);
7864
8028
  test_add("/queue/add", test_add_to_queue);
 
8029
  test_add("/queue/add/overflow", test_add_to_queue_overflow);
7865
8030
  test_add("/queue/has_question/empty",
7866
8031
           test_queue_has_question_empty);
7867
8032
  test_add("/queue/has_question/false",
8092
8257
  g_option_context_set_help_enabled(context, FALSE);
8093
8258
  g_option_context_set_ignore_unknown_options(context, TRUE);
8094
8259
 
8095
 
  gboolean run_tests = FALSE;
 
8260
  gboolean should_run_tests = FALSE;
8096
8261
  GOptionEntry entries[] = {
8097
8262
    { "test", 0, 0, G_OPTION_ARG_NONE,
8098
 
      &run_tests, "Run tests", NULL },
 
8263
      &should_run_tests, "Run tests", NULL },
8099
8264
    { NULL }
8100
8265
  };
8101
8266
  g_option_context_add_main_entries(context, entries, NULL);
8108
8273
  }
8109
8274
 
8110
8275
  g_option_context_free(context);
8111
 
  return run_tests != FALSE;
 
8276
  return should_run_tests != FALSE;
8112
8277
}
 
8278
 
 
8279
/*
 
8280
Local Variables:
 
8281
run-tests:
 
8282
(lambda ()
 
8283
  (if (not (funcall run-tests-in-test-buffer default-directory))
 
8284
      (funcall show-test-buffer-in-test-window)
 
8285
    (funcall remove-test-window)))
 
8286
run-tests-in-test-buffer:
 
8287
(lambda (dir)
 
8288
  (with-current-buffer (get-buffer-create "*Test*")
 
8289
    (setq buffer-read-only nil
 
8290
          default-directory dir)
 
8291
    (erase-buffer)
 
8292
    (compilation-mode))
 
8293
  (let ((process-result
 
8294
         (let ((inhibit-read-only t))
 
8295
           (process-file-shell-command
 
8296
            (funcall get-command-line) nil "*Test*"))))
 
8297
    (and (numberp process-result)
 
8298
         (= process-result 0))))
 
8299
get-command-line:
 
8300
(lambda ()
 
8301
  (let*
 
8302
      ((build-directory
 
8303
        (funcall find-build-directory (buffer-file-name)))
 
8304
       (local-build-directory
 
8305
        (if (fboundp 'file-local-name)
 
8306
            (file-local-name build-directory)
 
8307
          (or (file-remote-p build-directory 'localname)
 
8308
              build-directory)))
 
8309
       (command
 
8310
        (file-relative-name (file-name-sans-extension
 
8311
                             (buffer-file-name)) build-directory))
 
8312
       (qbdir (shell-quote-argument local-build-directory))
 
8313
       (qcmd (shell-quote-argument command)))
 
8314
    (format (concat "cd %s && CFLAGS=-Werror make --silent %s"
 
8315
             " && %s --test --verbose") qbdir qcmd qcmd)))
 
8316
find-build-directory:
 
8317
(lambda (try-directory &optional base-directory)
 
8318
  (let ((base-directory (or base-directory try-directory)))
 
8319
    (cond ((equal try-directory "/") base-directory)
 
8320
          ((file-readable-p
 
8321
            (concat (file-name-as-directory try-directory)
 
8322
                    "Makefile")) try-directory)
 
8323
          ((funcall find-build-directory
 
8324
                    (directory-file-name (file-name-directory
 
8325
                                          try-directory))
 
8326
                    base-directory)))))
 
8327
show-test-buffer-in-test-window:
 
8328
(lambda ()
 
8329
  (when (not (get-buffer-window-list "*Test*"))
 
8330
    (setq next-error-last-buffer (get-buffer "*Test*"))
 
8331
    (let* ((side (if (>= (window-width) 146) 'right 'bottom))
 
8332
           (display-buffer-overriding-action
 
8333
            `((display-buffer-in-side-window) (side . ,side)
 
8334
              (window-height . fit-window-to-buffer)
 
8335
              (window-width . fit-window-to-buffer))))
 
8336
      (display-buffer "*Test*"))))
 
8337
remove-test-window:
 
8338
(lambda ()
 
8339
  (let ((test-window (get-buffer-window "*Test*")))
 
8340
    (if test-window (delete-window test-window))))
 
8341
eval: (add-hook 'after-save-hook run-tests 90 t)
 
8342
End:
 
8343
*/