/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-05 21:14:05 UTC
  • Revision ID: teddy@recompile.se-20190805211405-9m6hecekaihpttz9
Override lintian warnings about upgrading from old versions

There are some really things which are imperative that we fix in case
someone were to upgrade from a really old version.  We want to keep
these fixes in the postinst maintainer scripts, even though lintian
complains about such old upgrades not being supported by Debian in
general.  We prefer the code being there, for the sake of the users.

* debian/mandos-client.lintian-overrides
  (maintainer-script-supports-ancient-package-version): New.
  debian/mandos.lintian-overrides
  (maintainer-script-supports-ancient-package-version): - '' -

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* -*- coding: utf-8; lexical-binding: t -*- */
 
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)))))); -*- */
2
2
/*
3
3
 * Mandos password agent - Simple password agent to run Mandos client
4
4
 *
5
 
 * Copyright © 2019-2022 Teddy Hogeborn
6
 
 * Copyright © 2019-2022 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
51
                                   ENAMETOOLONG, ENOENT, ENOTDIR,
61
 
                                   ENOMEM, EEXIST, ECHILD, EPERM,
 
52
                                   EEXIST, ECHILD, EPERM, ENOMEM,
62
53
                                   EAGAIN, EINTR, ENOBUFS, EADDRINUSE,
63
54
                                   ECONNREFUSED, ECONNRESET,
64
55
                                   ETOOMANYREFS, EMSGSIZE, EBADF,
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,
98
95
                                   IN_EXCL_UNLINK, IN_ONLYDIR,
99
96
                                   struct inotify_event */
100
97
#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() */
 
98
#include <stdio.h>              /* asprintf(), FILE, fopen(),
 
99
                                   getline(), sscanf(), feof(),
 
100
                                   ferror(), fclose(), stderr,
 
101
                                   rename(), fdopen(), fprintf(),
 
102
                                   fscanf() */
105
103
#include <glib.h>    /* GKeyFile, g_key_file_free(), g_key_file_new(),
106
104
                        GError, g_key_file_load_from_file(),
107
105
                        G_KEY_FILE_NONE, TRUE, G_FILE_ERROR_NOENT,
112
110
                        g_assert_null(), g_assert_false(),
113
111
                        g_assert_cmpint(), g_assert_cmpuint(),
114
112
                        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(),
 
113
                        g_test_init(), g_test_add(), g_test_run(),
 
114
                        GOptionContext, g_option_context_new(),
118
115
                        g_option_context_set_help_enabled(), FALSE,
119
116
                        g_option_context_set_ignore_unknown_options(),
120
117
                        gboolean, GOptionEntry, G_OPTION_ARG_NONE,
151
148
  mono_microsecs next_run;
152
149
} __attribute__((designated_init)) task_queue;
153
150
 
154
 
/* "task_func" - A function type for task functions
 
151
/* "func_type" - A function type for task functions
155
152
 
156
153
   I.e. functions for the code which runs when a task is run, all have
157
154
   this type */
654
651
 
655
652
__attribute__((nonnull, warn_unused_result))
656
653
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
654
  const size_t needed_size = sizeof(task_context)*(queue->length + 1);
665
655
  if(needed_size > (queue->allocated)){
666
656
    task_context *const new_tasks = realloc(queue->tasks,
871
861
  }
872
862
  close(pipefds[1]);
873
863
 
874
 
  if(pid == -1){
875
 
    error(0, errno, "Failed to fork()");
876
 
    close(pipefds[0]);
877
 
    return false;
878
 
  }
879
 
 
880
864
  if(not add_to_queue(queue, (task_context){
881
865
        .func=wait_for_mandos_client_exit,
882
866
        .pid=pid,
1098
1082
  } ievent_buffer;
1099
1083
  struct inotify_event *const ievent = &ievent_buffer.event;
1100
1084
 
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
1106
1085
  const ssize_t read_length = read(fd, ievent, ievent_size);
1107
 
#if defined(__GNUC__) and __GNUC__ >= 7
1108
 
#pragma GCC diagnostic pop
1109
 
#endif
1110
1086
  if(read_length == 0){ /* EOF */
1111
1087
    error(0, 0, "Got EOF from inotify fd for directory %s", filename);
1112
1088
    *quit_now = true;
1204
1180
  bool *const password_is_read = task.password_is_read;
1205
1181
 
1206
1182
  /* We use the GLib "Key-value file parser" functions to parse the
1207
 
     question file.  See <https://systemd.io/PASSWORD_AGENTS/> for
1208
 
     specification of contents */
 
1183
     question file.  See <https://www.freedesktop.org/wiki/Software
 
1184
     /systemd/PasswordAgents/> for specification of contents */
1209
1185
  __attribute__((nonnull))
1210
1186
    void cleanup_g_key_file(GKeyFile **key_file){
1211
1187
    if(*key_file != NULL){
1488
1464
    if(send_buffer == NULL){
1489
1465
      error(0, errno, "Failed to allocate send_buffer");
1490
1466
    } 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
1496
1467
      if(mlock(send_buffer, send_buffer_length) != 0){
1497
 
#if defined(__GNUC__) and __GNUC__ >= 5
1498
 
#pragma GCC diagnostic pop
1499
 
#endif
1500
1468
        /* Warn but do not treat as fatal error */
1501
1469
        if(errno != EPERM and errno != ENOMEM){
1502
1470
          error(0, errno, "Failed to lock memory for password"
1509
1477
         not. You may but don't have to include a final NUL byte in
1510
1478
         your message.
1511
1479
 
1512
 
         — <https://systemd.io/PASSWORD_AGENTS/> (Tue, 15 Sep 2020
1513
 
         14:24:20 GMT)
 
1480
         — <https://www.freedesktop.org/wiki/Software/systemd/
 
1481
         PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
1514
1482
      */
1515
1483
      send_buffer[0] = '+';     /* Prefix with "+" */
1516
1484
      /* Always add an extra NUL */
1521
1489
      errno = 0;
1522
1490
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1523
1491
                           MSG_NOSIGNAL);
1524
 
      const error_t saved_errno = (ssret < 0) ? errno : 0;
 
1492
      const error_t saved_errno = errno;
1525
1493
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1526
1494
      explicit_bzero(send_buffer, send_buffer_length);
1527
1495
#else
1545
1513
          /* Retry, below */
1546
1514
          break;
1547
1515
        case EMSGSIZE:
1548
 
          error(0, saved_errno, "Password of size %" PRIuMAX
1549
 
                " is too big", (uintmax_t)password->length);
 
1516
          error(0, 0, "Password of size %" PRIuMAX " is too big",
 
1517
                (uintmax_t)password->length);
1550
1518
#if __GNUC__ < 7
1551
1519
          /* FALLTHROUGH */
1552
1520
#else
1554
1522
#endif
1555
1523
        case 0:
1556
1524
          if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
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);
 
1525
            error(0, 0, "Password only partially sent to socket");
1560
1526
          }
1561
1527
#if __GNUC__ < 7
1562
1528
          /* FALLTHROUGH */
1918
1884
  g_assert_true(queue->tasks[0].func == dummy_func);
1919
1885
}
1920
1886
 
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
 
 
1944
1887
static void dummy_func(__attribute__((unused))
1945
1888
                       const task_context task,
1946
1889
                       __attribute__((unused))
2217
2160
    }
2218
2161
    exit(EXIT_SUCCESS);
2219
2162
  }
2220
 
  if(pid == -1){
2221
 
    error(EXIT_FAILURE, errno, "Failed to fork()");
2222
 
  }
2223
 
 
2224
2163
  int status;
2225
2164
  waitpid(pid, &status, 0);
2226
2165
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2669
2608
  bool password_is_read = false;
2670
2609
  const char helper_directory[] = "/nonexistent";
2671
2610
  const char *const argv[] = { "/bin/sh", "-c",
2672
 
    "printf %s \"${MANDOSPLUGINHELPERDIR}\"", NULL };
 
2611
    "echo -n ${MANDOSPLUGINHELPERDIR}", NULL };
2673
2612
 
2674
2613
  const bool success = start_mandos_client(queue, epoll_fd,
2675
2614
                                           &mandos_client_exited,
4198
4137
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4199
4138
  const size_t ievent_size = (sizeof(struct inotify_event)
4200
4139
                              + 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
4206
4140
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4207
4141
                  ==, ievent_size);
4208
 
#if defined(__GNUC__) and __GNUC__ >= 11
4209
 
#pragma GCC diagnostic pop
4210
 
#endif
4211
4142
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4212
4143
 
4213
4144
  bool quit_now = false;
4301
4232
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4302
4233
  const size_t ievent_size = (sizeof(struct inotify_event)
4303
4234
                              + 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
4309
4235
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4310
4236
                  ==, ievent_size);
4311
 
#if defined(__GNUC__) and __GNUC__ >= 11
4312
 
#pragma GCC diagnostic pop
4313
 
#endif
4314
4237
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4315
4238
 
4316
4239
  bool quit_now = false;
4406
4329
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4407
4330
  const size_t ievent_size = (sizeof(struct inotify_event)
4408
4331
                              + 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
4414
4332
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4415
4333
                  ==, ievent_size);
4416
 
#if defined(__GNUC__) and __GNUC__ >= 11
4417
 
#pragma GCC diagnostic pop
4418
 
#endif
4419
4334
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4420
4335
 
4421
4336
  bool quit_now = false;
4499
4414
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4500
4415
  const size_t ievent_size = (sizeof(struct inotify_event)
4501
4416
                              + 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
4507
4417
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4508
4418
                  ==, ievent_size);
4509
 
#if defined(__GNUC__) and __GNUC__ >= 11
4510
 
#pragma GCC diagnostic pop
4511
 
#endif
4512
4419
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4513
4420
 
4514
4421
  bool quit_now = false;
4591
4498
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4592
4499
  const size_t ievent_size = (sizeof(struct inotify_event)
4593
4500
                              + 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
4599
4501
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4600
4502
                  ==, ievent_size);
4601
 
#if defined(__GNUC__) and __GNUC__ >= 11
4602
 
#pragma GCC diagnostic pop
4603
 
#endif
4604
4503
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4605
4504
 
4606
4505
  bool quit_now = false;
4675
4574
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4676
4575
  const size_t ievent_size = (sizeof(struct inotify_event)
4677
4576
                              + 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
4683
4577
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4684
4578
                  ==, ievent_size);
4685
 
#if defined(__GNUC__) and __GNUC__ >= 11
4686
 
#pragma GCC diagnostic pop
4687
 
#endif
4688
4579
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4689
4580
 
4690
4581
  bool quit_now = false;
4762
4653
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4763
4654
  const size_t ievent_size = (sizeof(struct inotify_event)
4764
4655
                              + 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
4770
4656
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4771
4657
                  ==, ievent_size);
4772
 
#if defined(__GNUC__) and __GNUC__ >= 11
4773
 
#pragma GCC diagnostic pop
4774
 
#endif
4775
4658
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4776
4659
 
4777
4660
  bool quit_now = false;
4855
4738
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4856
4739
  const size_t ievent_size = (sizeof(struct inotify_event)
4857
4740
                              + 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
4863
4741
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4864
4742
                  ==, ievent_size);
4865
 
#if defined(__GNUC__) and __GNUC__ >= 11
4866
 
#pragma GCC diagnostic pop
4867
 
#endif
4868
4743
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4869
4744
 
4870
4745
  bool quit_now = false;
5892
5767
  char write_data[PIPE_BUF];
5893
5768
  {
5894
5769
    /* Construct test password buffer */
5895
 
    /* Start with + since that is what the real protocol uses */
 
5770
    /* Start with + since that is what the real procotol uses */
5896
5771
    write_data[0] = '+';
5897
5772
    /* Set a special character at string end just to mark the end */
5898
5773
    write_data[sizeof(write_data)-2] = 'y';
6050
5925
  char *const filename = strdup("/nonexistent/socket");
6051
5926
  __attribute__((cleanup(string_set_clear)))
6052
5927
    string_set cancelled_filenames = {};
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,
 
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,
6109
5934
  };
 
5935
  g_assert_nonnull(password.data);
6110
5936
  if(mlock(password.data, password.allocated) != 0){
6111
5937
    g_assert_true(errno == EPERM or errno == ENOMEM);
6112
5938
  }
 
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);
6113
5948
 
6114
5949
  __attribute__((cleanup(cleanup_queue)))
6115
5950
    task_queue *queue = create_queue();
6116
5951
  g_assert_nonnull(queue);
 
5952
  int socketfds[2];
6117
5953
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6118
5954
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6119
5955
                             socketfds), ==, 0);
8026
7862
  test_add("/parse_arguments/mixed", test_parse_arguments_mixed);
8027
7863
  test_add("/queue/create", test_create_queue);
8028
7864
  test_add("/queue/add", test_add_to_queue);
8029
 
  test_add("/queue/add/overflow", test_add_to_queue_overflow);
8030
7865
  test_add("/queue/has_question/empty",
8031
7866
           test_queue_has_question_empty);
8032
7867
  test_add("/queue/has_question/false",
8257
8092
  g_option_context_set_help_enabled(context, FALSE);
8258
8093
  g_option_context_set_ignore_unknown_options(context, TRUE);
8259
8094
 
8260
 
  gboolean should_run_tests = FALSE;
 
8095
  gboolean run_tests = FALSE;
8261
8096
  GOptionEntry entries[] = {
8262
8097
    { "test", 0, 0, G_OPTION_ARG_NONE,
8263
 
      &should_run_tests, "Run tests", NULL },
 
8098
      &run_tests, "Run tests", NULL },
8264
8099
    { NULL }
8265
8100
  };
8266
8101
  g_option_context_add_main_entries(context, entries, NULL);
8273
8108
  }
8274
8109
 
8275
8110
  g_option_context_free(context);
8276
 
  return should_run_tests != FALSE;
 
8111
  return run_tests != FALSE;
8277
8112
}
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
 
*/