/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-11-17 18:43:11 UTC
  • Revision ID: teddy@recompile.se-20241117184311-ox25kvngy62h209g
Debian package: Avoid suggesting a C compiler unnecessarily

The list of suggested packages, meant to enable the "mandos" program
to find the correct value of SO_BINDTODEVICE by using a C compiler,
are not necessary when Python 3.3 or later is used, since it has the
SO_BINDTODEVICE constant defined in the "socket" module.  Also, Python
2.6 or older has the same constant in the old "IN" module.  Therefore,
we should suggest these Python versions as alternatives to a C
compiler, so that a C compiler is not installed unnecessarily.

debian/control (Package: mandos/Suggests): Add "python3 (>= 3.3)" and
"python (<= 2.6)" as alternatives to "libc6-dev | libc-dev" and
"c-compiler".

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 */
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 <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() */
 
85
#include <stdint.h>             /* SIZE_MAX, uint32_t */
84
86
#include <sys/mman.h>           /* munlock(), mlock() */
85
87
#include <fcntl.h>              /* O_CLOEXEC, O_NONBLOCK, fcntl(),
86
88
                                   F_GETFD, F_GETFL, FD_CLOEXEC,
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,
868
871
  }
869
872
  close(pipefds[1]);
870
873
 
 
874
  if(pid == -1){
 
875
    error(0, errno, "Failed to fork()");
 
876
    close(pipefds[0]);
 
877
    return false;
 
878
  }
 
879
 
871
880
  if(not add_to_queue(queue, (task_context){
872
881
        .func=wait_for_mandos_client_exit,
873
882
        .pid=pid,
1089
1098
  } ievent_buffer;
1090
1099
  struct inotify_event *const ievent = &ievent_buffer.event;
1091
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
1092
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
1093
1110
  if(read_length == 0){ /* EOF */
1094
1111
    error(0, 0, "Got EOF from inotify fd for directory %s", filename);
1095
1112
    *quit_now = true;
1187
1204
  bool *const password_is_read = task.password_is_read;
1188
1205
 
1189
1206
  /* We use the GLib "Key-value file parser" functions to parse the
1190
 
     question file.  See <https://www.freedesktop.org/wiki/Software
1191
 
     /systemd/PasswordAgents/> for specification of contents */
 
1207
     question file.  See <https://systemd.io/PASSWORD_AGENTS/> for
 
1208
     specification of contents */
1192
1209
  __attribute__((nonnull))
1193
1210
    void cleanup_g_key_file(GKeyFile **key_file){
1194
1211
    if(*key_file != NULL){
1471
1488
    if(send_buffer == NULL){
1472
1489
      error(0, errno, "Failed to allocate send_buffer");
1473
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
1474
1496
      if(mlock(send_buffer, send_buffer_length) != 0){
 
1497
#if defined(__GNUC__) and __GNUC__ >= 5
 
1498
#pragma GCC diagnostic pop
 
1499
#endif
1475
1500
        /* Warn but do not treat as fatal error */
1476
1501
        if(errno != EPERM and errno != ENOMEM){
1477
1502
          error(0, errno, "Failed to lock memory for password"
1484
1509
         not. You may but don't have to include a final NUL byte in
1485
1510
         your message.
1486
1511
 
1487
 
         — <https://www.freedesktop.org/wiki/Software/systemd/
1488
 
         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)
1489
1514
      */
1490
1515
      send_buffer[0] = '+';     /* Prefix with "+" */
1491
1516
      /* Always add an extra NUL */
1496
1521
      errno = 0;
1497
1522
      ssize_t ssret = send(fd, send_buffer, send_buffer_length,
1498
1523
                           MSG_NOSIGNAL);
1499
 
      const error_t saved_errno = errno;
 
1524
      const error_t saved_errno = (ssret < 0) ? errno : 0;
1500
1525
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 25)
1501
1526
      explicit_bzero(send_buffer, send_buffer_length);
1502
1527
#else
1520
1545
          /* Retry, below */
1521
1546
          break;
1522
1547
        case EMSGSIZE:
1523
 
          error(0, 0, "Password of size %" PRIuMAX " is too big",
1524
 
                (uintmax_t)password->length);
 
1548
          error(0, saved_errno, "Password of size %" PRIuMAX
 
1549
                " is too big", (uintmax_t)password->length);
1525
1550
#if __GNUC__ < 7
1526
1551
          /* FALLTHROUGH */
1527
1552
#else
1529
1554
#endif
1530
1555
        case 0:
1531
1556
          if(ssret >= 0 and ssret < (ssize_t)send_buffer_length){
1532
 
            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);
1533
1560
          }
1534
1561
#if __GNUC__ < 7
1535
1562
          /* FALLTHROUGH */
2190
2217
    }
2191
2218
    exit(EXIT_SUCCESS);
2192
2219
  }
 
2220
  if(pid == -1){
 
2221
    error(EXIT_FAILURE, errno, "Failed to fork()");
 
2222
  }
 
2223
 
2193
2224
  int status;
2194
2225
  waitpid(pid, &status, 0);
2195
2226
  if(WIFEXITED(status) and (WEXITSTATUS(status) == EXIT_SUCCESS)){
2638
2669
  bool password_is_read = false;
2639
2670
  const char helper_directory[] = "/nonexistent";
2640
2671
  const char *const argv[] = { "/bin/sh", "-c",
2641
 
    "echo -n ${MANDOSPLUGINHELPERDIR}", NULL };
 
2672
    "printf %s \"${MANDOSPLUGINHELPERDIR}\"", NULL };
2642
2673
 
2643
2674
  const bool success = start_mandos_client(queue, epoll_fd,
2644
2675
                                           &mandos_client_exited,
4167
4198
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4168
4199
  const size_t ievent_size = (sizeof(struct inotify_event)
4169
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
4170
4206
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4171
4207
                  ==, ievent_size);
 
4208
#if defined(__GNUC__) and __GNUC__ >= 11
 
4209
#pragma GCC diagnostic pop
 
4210
#endif
4172
4211
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4173
4212
 
4174
4213
  bool quit_now = false;
4262
4301
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4263
4302
  const size_t ievent_size = (sizeof(struct inotify_event)
4264
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
4265
4309
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4266
4310
                  ==, ievent_size);
 
4311
#if defined(__GNUC__) and __GNUC__ >= 11
 
4312
#pragma GCC diagnostic pop
 
4313
#endif
4267
4314
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4268
4315
 
4269
4316
  bool quit_now = false;
4359
4406
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4360
4407
  const size_t ievent_size = (sizeof(struct inotify_event)
4361
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
4362
4414
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4363
4415
                  ==, ievent_size);
 
4416
#if defined(__GNUC__) and __GNUC__ >= 11
 
4417
#pragma GCC diagnostic pop
 
4418
#endif
4364
4419
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4365
4420
 
4366
4421
  bool quit_now = false;
4444
4499
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4445
4500
  const size_t ievent_size = (sizeof(struct inotify_event)
4446
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
4447
4507
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4448
4508
                  ==, ievent_size);
 
4509
#if defined(__GNUC__) and __GNUC__ >= 11
 
4510
#pragma GCC diagnostic pop
 
4511
#endif
4449
4512
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4450
4513
 
4451
4514
  bool quit_now = false;
4528
4591
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4529
4592
  const size_t ievent_size = (sizeof(struct inotify_event)
4530
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
4531
4599
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4532
4600
                  ==, ievent_size);
 
4601
#if defined(__GNUC__) and __GNUC__ >= 11
 
4602
#pragma GCC diagnostic pop
 
4603
#endif
4533
4604
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4534
4605
 
4535
4606
  bool quit_now = false;
4604
4675
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4605
4676
  const size_t ievent_size = (sizeof(struct inotify_event)
4606
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
4607
4683
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4608
4684
                  ==, ievent_size);
 
4685
#if defined(__GNUC__) and __GNUC__ >= 11
 
4686
#pragma GCC diagnostic pop
 
4687
#endif
4609
4688
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4610
4689
 
4611
4690
  bool quit_now = false;
4683
4762
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4684
4763
  const size_t ievent_size = (sizeof(struct inotify_event)
4685
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
4686
4770
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4687
4771
                  ==, ievent_size);
 
4772
#if defined(__GNUC__) and __GNUC__ >= 11
 
4773
#pragma GCC diagnostic pop
 
4774
#endif
4688
4775
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4689
4776
 
4690
4777
  bool quit_now = false;
4768
4855
  memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4769
4856
  const size_t ievent_size = (sizeof(struct inotify_event)
4770
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
4771
4863
  g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4772
4864
                  ==, ievent_size);
 
4865
#if defined(__GNUC__) and __GNUC__ >= 11
 
4866
#pragma GCC diagnostic pop
 
4867
#endif
4773
4868
  g_assert_cmpint(close(pipefds[1]), ==, 0);
4774
4869
 
4775
4870
  bool quit_now = false;
5797
5892
  char write_data[PIPE_BUF];
5798
5893
  {
5799
5894
    /* Construct test password buffer */
5800
 
    /* Start with + since that is what the real procotol uses */
 
5895
    /* Start with + since that is what the real protocol uses */
5801
5896
    write_data[0] = '+';
5802
5897
    /* Set a special character at string end just to mark the end */
5803
5898
    write_data[sizeof(write_data)-2] = 'y';
5955
6050
  char *const filename = strdup("/nonexistent/socket");
5956
6051
  __attribute__((cleanup(string_set_clear)))
5957
6052
    string_set cancelled_filenames = {};
5958
 
  const size_t oversized = 1024*1024; /* Limit seems to be 212960 */
5959
 
  __attribute__((cleanup(cleanup_buffer)))
5960
 
    buffer password = {
5961
 
    .data=malloc(oversized),
5962
 
    .length=oversized,
5963
 
    .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,
5964
6109
  };
5965
 
  g_assert_nonnull(password.data);
5966
6110
  if(mlock(password.data, password.allocated) != 0){
5967
6111
    g_assert_true(errno == EPERM or errno == ENOMEM);
5968
6112
  }
5969
 
  /* Construct test password buffer */
5970
 
  /* Start with + since that is what the real procotol uses */
5971
 
  password.data[0] = '+';
5972
 
  /* Set a special character at string end just to mark the end */
5973
 
  password.data[oversized-3] = 'y';
5974
 
  /* Set NUL at buffer end, as suggested by the protocol */
5975
 
  password.data[oversized-2] = '\0';
5976
 
  /* Fill rest of password with 'x' */
5977
 
  memset(password.data+1, 'x', oversized-3);
5978
6113
 
5979
6114
  __attribute__((cleanup(cleanup_queue)))
5980
6115
    task_queue *queue = create_queue();
5981
6116
  g_assert_nonnull(queue);
5982
 
  int socketfds[2];
5983
6117
  g_assert_cmpint(socketpair(PF_LOCAL, SOCK_DGRAM
5984
6118
                             | SOCK_NONBLOCK | SOCK_CLOEXEC, 0,
5985
6119
                             socketfds), ==, 0);
8141
8275
  g_option_context_free(context);
8142
8276
  return should_run_tests != FALSE;
8143
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
*/