/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 at recompile
  • Date: 2020-04-05 21:30:59 UTC
  • Revision ID: teddy@recompile.se-20200405213059-fb2a61ckqynrmatk
Fix file descriptor leak in mandos-client

When the local network has Mandos servers announcing themselves using
real, globally reachable, IPv6 addresses (i.e. not link-local
addresses), but there is no router on the local network providing IPv6
RA (Router Advertisement) packets, the client cannot reach the server
by normal means, since the client only has a link-local IPv6 address,
and has no usable route to reach the server's global IPv6 address.
(This is not a common situation, and usually only happens when the
router itself reboots and runs a Mandos client, since it cannot then
give RA packets to itself.)  The client code has a solution for
this, which consists of adding a temporary local route to reach the
address of the server during communication, and removing this
temporary route afterwards.

This solution with a temporary route works, but has a file descriptor
leak; it leaks one file descriptor for each addition and for each
removal of a route.  If one server requiring an added route is present
on the network, but no servers gives a password, making the client
retry after the default ten seconds, and we furthermore assume a
default 1024 open files limit, the client runs out of file descriptors
after about 90 minutes, after which time the client process will be
useless and fail to retrieve any passwords, necessitating manual
password entry via the keyboard.

Fix this by eliminating the file descriptor leak in the client.

* plugins.d/mandos-client.c (add_delete_local_route): Do
  close(devnull) also in parent process, also if fork() fails, and on
  any failure in child process.

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 */
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 <stdint.h>             /* SIZE_MAX */
 
77
#include <unistd.h>             /* uid_t, gid_t, close(), pipe2(),
 
78
                                   fork(), _exit(), dup2(),
 
79
                                   STDOUT_FILENO, setresgid(),
 
80
                                   setresuid(), execv(), ssize_t,
 
81
                                   read(), dup3(), getuid(), dup(),
 
82
                                   STDERR_FILENO, pause(), write(),
 
83
                                   rmdir(), unlink(), getpid() */
86
84
#include <sys/mman.h>           /* munlock(), mlock() */
87
85
#include <fcntl.h>              /* O_CLOEXEC, O_NONBLOCK, fcntl(),
88
86
                                   F_GETFD, F_GETFL, FD_CLOEXEC,
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,
1098
1095
  } ievent_buffer;
1099
1096
  struct inotify_event *const ievent = &ievent_buffer.event;
1100
1097
 
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
1098
  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
1099
  if(read_length == 0){ /* EOF */
1111
1100
    error(0, 0, "Got EOF from inotify fd for directory %s", filename);
1112
1101
    *quit_now = true;
1204
1193
  bool *const password_is_read = task.password_is_read;
1205
1194
 
1206
1195
  /* 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 */
 
1196
     question file.  See <https://www.freedesktop.org/wiki/Software
 
1197
     /systemd/PasswordAgents/> for specification of contents */
1209
1198
  __attribute__((nonnull))
1210
1199
    void cleanup_g_key_file(GKeyFile **key_file){
1211
1200
    if(*key_file != NULL){
1488
1477
    if(send_buffer == NULL){
1489
1478
      error(0, errno, "Failed to allocate send_buffer");
1490
1479
    } 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
1480
      if(mlock(send_buffer, send_buffer_length) != 0){
1497
 
#if defined(__GNUC__) and __GNUC__ >= 5
1498
 
#pragma GCC diagnostic pop
1499
 
#endif
1500
1481
        /* Warn but do not treat as fatal error */
1501
1482
        if(errno != EPERM and errno != ENOMEM){
1502
1483
          error(0, errno, "Failed to lock memory for password"
1509
1490
         not. You may but don't have to include a final NUL byte in
1510
1491
         your message.
1511
1492
 
1512
 
         — <https://systemd.io/PASSWORD_AGENTS/> (Tue, 15 Sep 2020
1513
 
         14:24:20 GMT)
 
1493
         — <https://www.freedesktop.org/wiki/Software/systemd/
 
1494
         PasswordAgents/> (Wed 08 Oct 2014 02:14:28 AM UTC)
1514
1495
      */
1515
1496
      send_buffer[0] = '+';     /* Prefix with "+" */
1516
1497
      /* Always add an extra NUL */
1521
1502
      errno = 0;
1522
1503
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1523
1504
                           MSG_NOSIGNAL);
1524
 
      const error_t saved_errno = (ssret < 0) ? errno : 0;
 
1505
      const error_t saved_errno = errno;
1525
1506
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1526
1507
      explicit_bzero(send_buffer, send_buffer_length);
1527
1508
#else
1545
1526
          /* Retry, below */
1546
1527
          break;
1547
1528
        case EMSGSIZE:
1548
 
          error(0, saved_errno, "Password of size %" PRIuMAX
1549
 
                " is too big", (uintmax_t)password->length);
 
1529
          error(0, 0, "Password of size %" PRIuMAX " is too big",
 
1530
                (uintmax_t)password->length);
1550
1531
#if __GNUC__ < 7
1551
1532
          /* FALLTHROUGH */
1552
1533
#else
1554
1535
#endif
1555
1536
        case 0:
1556
1537
          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);
 
1538
            error(0, 0, "Password only partially sent to socket");
1560
1539
          }
1561
1540
#if __GNUC__ < 7
1562
1541
          /* FALLTHROUGH */
2669
2648
  bool password_is_read = false;
2670
2649
  const char helper_directory[] = "/nonexistent";
2671
2650
  const char *const argv[] = { "/bin/sh", "-c",
2672
 
    "printf %s \"${MANDOSPLUGINHELPERDIR}\"", NULL };
 
2651
    "echo -n ${MANDOSPLUGINHELPERDIR}", NULL };
2673
2652
 
2674
2653
  const bool success = start_mandos_client(queue, epoll_fd,
2675
2654
                                           &mandos_client_exited,
4198
4177
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4199
4178
  const size_t ievent_size = (sizeof(struct inotify_event)
4200
4179
                              + 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
4180
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4207
4181
                  ==, ievent_size);
4208
 
#if defined(__GNUC__) and __GNUC__ >= 11
4209
 
#pragma GCC diagnostic pop
4210
 
#endif
4211
4182
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4212
4183
 
4213
4184
  bool quit_now = false;
4301
4272
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4302
4273
  const size_t ievent_size = (sizeof(struct inotify_event)
4303
4274
                              + 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
4275
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4310
4276
                  ==, ievent_size);
4311
 
#if defined(__GNUC__) and __GNUC__ >= 11
4312
 
#pragma GCC diagnostic pop
4313
 
#endif
4314
4277
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4315
4278
 
4316
4279
  bool quit_now = false;
4406
4369
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4407
4370
  const size_t ievent_size = (sizeof(struct inotify_event)
4408
4371
                              + 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
4372
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4415
4373
                  ==, ievent_size);
4416
 
#if defined(__GNUC__) and __GNUC__ >= 11
4417
 
#pragma GCC diagnostic pop
4418
 
#endif
4419
4374
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4420
4375
 
4421
4376
  bool quit_now = false;
4499
4454
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4500
4455
  const size_t ievent_size = (sizeof(struct inotify_event)
4501
4456
                              + 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
4457
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4508
4458
                  ==, ievent_size);
4509
 
#if defined(__GNUC__) and __GNUC__ >= 11
4510
 
#pragma GCC diagnostic pop
4511
 
#endif
4512
4459
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4513
4460
 
4514
4461
  bool quit_now = false;
4591
4538
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4592
4539
  const size_t ievent_size = (sizeof(struct inotify_event)
4593
4540
                              + 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
4541
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4600
4542
                  ==, ievent_size);
4601
 
#if defined(__GNUC__) and __GNUC__ >= 11
4602
 
#pragma GCC diagnostic pop
4603
 
#endif
4604
4543
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4605
4544
 
4606
4545
  bool quit_now = false;
4675
4614
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4676
4615
  const size_t ievent_size = (sizeof(struct inotify_event)
4677
4616
                              + 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
4617
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4684
4618
                  ==, ievent_size);
4685
 
#if defined(__GNUC__) and __GNUC__ >= 11
4686
 
#pragma GCC diagnostic pop
4687
 
#endif
4688
4619
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4689
4620
 
4690
4621
  bool quit_now = false;
4762
4693
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4763
4694
  const size_t ievent_size = (sizeof(struct inotify_event)
4764
4695
                              + 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
4696
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4771
4697
                  ==, ievent_size);
4772
 
#if defined(__GNUC__) and __GNUC__ >= 11
4773
 
#pragma GCC diagnostic pop
4774
 
#endif
4775
4698
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4776
4699
 
4777
4700
  bool quit_now = false;
4855
4778
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4856
4779
  const size_t ievent_size = (sizeof(struct inotify_event)
4857
4780
                              + 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
4781
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4864
4782
                  ==, ievent_size);
4865
 
#if defined(__GNUC__) and __GNUC__ >= 11
4866
 
#pragma GCC diagnostic pop
4867
 
#endif
4868
4783
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4869
4784
 
4870
4785
  bool quit_now = false;
5892
5807
  char write_data[PIPE_BUF];
5893
5808
  {
5894
5809
    /* Construct test password buffer */
5895
 
    /* Start with + since that is what the real protocol uses */
 
5810
    /* Start with + since that is what the real procotol uses */
5896
5811
    write_data[0] = '+';
5897
5812
    /* Set a special character at string end just to mark the end */
5898
5813
    write_data[sizeof(write_data)-2] = 'y';
6043
5958
                                           test_fixture *fixture,
6044
5959
                                           __attribute__((unused))
6045
5960
                                           gconstpointer user_data){
 
5961
#ifndef __amd64__
 
5962
  g_test_skip("Skipping EMSGSIZE test on non-AMD64 platform");
 
5963
#else
6046
5964
  __attribute__((cleanup(cleanup_close)))
6047
5965
    const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
6048
5966
  g_assert_cmpint(epoll_fd, >=, 0);
6050
5968
  char *const filename = strdup("/nonexistent/socket");
6051
5969
  __attribute__((cleanup(string_set_clear)))
6052
5970
    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,
 
5971
  const size_t oversized = 1024*1024; /* Limit seems to be 212960 */
 
5972
  __attribute__((cleanup(cleanup_buffer)))
 
5973
    buffer password = {
 
5974
    .data=malloc(oversized),
 
5975
    .length=oversized,
 
5976
    .allocated=oversized,
6109
5977
  };
 
5978
  g_assert_nonnull(password.data);
6110
5979
  if(mlock(password.data, password.allocated) != 0){
6111
5980
    g_assert_true(errno == EPERM or errno == ENOMEM);
6112
5981
  }
 
5982
  /* Construct test password buffer */
 
5983
  /* Start with + since that is what the real procotol uses */
 
5984
  password.data[0] = '+';
 
5985
  /* Set a special character at string end just to mark the end */
 
5986
  password.data[oversized-3] = 'y';
 
5987
  /* Set NUL at buffer end, as suggested by the protocol */
 
5988
  password.data[oversized-2] = '\0';
 
5989
  /* Fill rest of password with 'x' */
 
5990
  memset(password.data+1, 'x', oversized-3);
6113
5991
 
6114
5992
  __attribute__((cleanup(cleanup_queue)))
6115
5993
    task_queue *queue = create_queue();
6116
5994
  g_assert_nonnull(queue);
 
5995
  int socketfds[2];
6117
5996
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
6118
5997
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
6119
5998
                             socketfds), ==, 0);
6140
6019
  g_assert_cmpuint((unsigned int)queue->length, ==, 0);
6141
6020
  g_assert_true(string_set_contains(cancelled_filenames,
6142
6021
                                    question_filename));
 
6022
#endif
6143
6023
}
6144
6024
 
6145
6025
static void test_send_password_to_socket_retry(__attribute__((unused))
8275
8155
  g_option_context_free(context);
8276
8156
  return should_run_tests != FALSE;
8277
8157
}
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
 
*/