/mandos/release

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/release

« back to all changes in this revision

Viewing changes to plugins.d/password-prompt.c

  • Committer: Teddy Hogeborn
  • Date: 2019-07-29 16:35:53 UTC
  • mto: This revision was merged to the branch mainline in revision 384.
  • Revision ID: teddy@recompile.se-20190729163553-1i442i2cbx64c537
Make tests and man page examples match

Make the tests test_manual_page_example[1-5] match exactly what is
written in the manual page, and add comments to manual page as
reminders to keep tests and manual page examples in sync.

* mandos-ctl (Test_commands_from_options.test_manual_page_example_1):
  Remove "--verbose" option, since the manual does not have it as the
  first example, and change assertion to match.
* mandos-ctl.xml (EXAMPLE): Add comments to all examples documenting
  which test function they correspond to.  Also remove unnecessary
  quotes from option arguments in fourth example, and clarify language
  slightly in fifth example.

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
/*
3
3
 * Password-prompt - Read a password from the terminal and print it
4
4
 * 
5
 
 * Copyright © 2008,2009 Teddy Hogeborn
6
 
 * Copyright © 2008,2009 Björn Påhlsson
7
 
 * 
8
 
 * This program is free software: you can redistribute it and/or
9
 
 * modify it under the terms of the GNU General Public License as
10
 
 * published by the Free Software Foundation, either version 3 of the
11
 
 * License, or (at your option) any later version.
12
 
 * 
13
 
 * This program is distributed in the hope that it will be useful, but
 
5
 * Copyright © 2008-2019 Teddy Hogeborn
 
6
 * Copyright © 2008-2019 Björn Påhlsson
 
7
 * 
 
8
 * This file is part of Mandos.
 
9
 * 
 
10
 * Mandos is free software: you can redistribute it and/or modify it
 
11
 * under the terms of the GNU General Public License as published by
 
12
 * the Free Software Foundation, either version 3 of the License, or
 
13
 * (at your option) any later version.
 
14
 * 
 
15
 * Mandos is distributed in the hope that it will be useful, but
14
16
 * WITHOUT ANY WARRANTY; without even the implied warranty of
15
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
18
 * General Public License for more details.
17
19
 * 
18
20
 * You should have received a copy of the GNU General Public License
19
 
 * along with this program.  If not, see
20
 
 * <http://www.gnu.org/licenses/>.
 
21
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
21
22
 * 
22
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
 
23
 * Contact the authors at <mandos@recompile.se>.
23
24
 */
24
25
 
25
 
#define _GNU_SOURCE             /* getline() */
 
26
#define _GNU_SOURCE             /* getline(), asprintf() */
26
27
 
27
 
#include <termios.h>            /* struct termios, tcsetattr(),
 
28
#include <termios.h>            /* struct termios, tcsetattr(),
28
29
                                   TCSAFLUSH, tcgetattr(), ECHO */
29
 
#include <unistd.h>             /* struct termios, tcsetattr(),
30
 
                                   STDIN_FILENO, TCSAFLUSH,
31
 
                                   tcgetattr(), ECHO */
 
30
#include <unistd.h>             /* access(), struct termios,
 
31
                                   tcsetattr(), STDIN_FILENO,
 
32
                                   TCSAFLUSH, tcgetattr(), ECHO,
 
33
                                   readlink() */
32
34
#include <signal.h>             /* sig_atomic_t, raise(), struct
33
35
                                   sigaction, sigemptyset(),
34
36
                                   sigaction(), sigaddset(), SIGINT,
35
37
                                   SIGQUIT, SIGHUP, SIGTERM,
36
38
                                   raise() */
37
39
#include <stddef.h>             /* NULL, size_t, ssize_t */
38
 
#include <sys/types.h>          /* ssize_t */
 
40
#include <sys/types.h>          /* ssize_t, struct dirent, pid_t,
 
41
                                   ssize_t, open() */
39
42
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE,
40
 
                                   getopt_long, getenv() */
 
43
                                   getenv(), free() */
 
44
#include <dirent.h>             /* scandir(), alphasort() */
41
45
#include <stdio.h>              /* fprintf(), stderr, getline(),
42
 
                                   stdin, feof(), perror(), fputc(),
43
 
                                   getopt_long */
 
46
                                   stdin, feof(), fputc(), vfprintf(),
 
47
                                   vasprintf() */
44
48
#include <errno.h>              /* errno, EBADF, ENOTTY, EINVAL,
45
49
                                   EFAULT, EFBIG, EIO, ENOSPC, EINTR
46
50
                                */
 
51
#include <error.h>              /* error() */
47
52
#include <iso646.h>             /* or, not */
48
53
#include <stdbool.h>            /* bool, false, true */
49
 
#include <string.h>             /* strlen, rindex, strncmp, strcmp */
 
54
#include <inttypes.h>           /* strtoumax() */
 
55
#include <sys/stat.h>           /* struct stat, lstat(), open() */
 
56
#include <string.h>             /* strlen, rindex, memcmp, strerror()
 
57
                                 */
50
58
#include <argp.h>               /* struct argp_option, struct
51
59
                                   argp_state, struct argp,
52
60
                                   argp_parse(), error_t,
54
62
                                   ARGP_ERR_UNKNOWN */
55
63
#include <sysexits.h>           /* EX_SOFTWARE, EX_OSERR,
56
64
                                   EX_UNAVAILABLE, EX_IOERR, EX_OK */
 
65
#include <fcntl.h>              /* open() */
 
66
#include <stdarg.h>             /* va_list, va_start(), ... */
57
67
 
58
68
volatile sig_atomic_t quit_now = 0;
59
69
int signal_received;
60
70
bool debug = false;
61
71
const char *argp_program_version = "password-prompt " VERSION;
62
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
 
72
const char *argp_program_bug_address = "<mandos@recompile.se>";
 
73
 
 
74
/* Needed for conflict resolution */
 
75
const char plymouth_name[] = "plymouthd";
 
76
 
 
77
/* Function to use when printing errors */
 
78
__attribute__((format (gnu_printf, 3, 4)))
 
79
void error_plus(int status, int errnum, const char *formatstring,
 
80
                ...){
 
81
  va_list ap;
 
82
  char *text;
 
83
  int ret;
 
84
  
 
85
  va_start(ap, formatstring);
 
86
  ret = vasprintf(&text, formatstring, ap);
 
87
  if(ret == -1){
 
88
    fprintf(stderr, "Mandos plugin %s: ",
 
89
            program_invocation_short_name);
 
90
    vfprintf(stderr, formatstring, ap);
 
91
    fprintf(stderr, ": %s\n", strerror(errnum));
 
92
    error(status, errno, "vasprintf while printing error");
 
93
    return;
 
94
  }
 
95
  fprintf(stderr, "Mandos plugin ");
 
96
  error(status, errnum, "%s", text);
 
97
  free(text);
 
98
}
63
99
 
64
100
static void termination_handler(int signum){
65
101
  if(quit_now){
69
105
  signal_received = signum;
70
106
}
71
107
 
 
108
bool conflict_detection(void){
 
109
 
 
110
  /* plymouth conflicts with password-prompt since both want to read
 
111
     from the terminal.  Password-prompt will exit if it detects
 
112
     plymouth since plymouth performs the same functionality.
 
113
   */
 
114
  if(access("/run/plymouth/pid", R_OK) == 0){
 
115
    return true;
 
116
  }
 
117
  
 
118
  __attribute__((nonnull))
 
119
  int is_plymouth(const struct dirent *proc_entry){
 
120
    int ret;
 
121
    int cl_fd;
 
122
    {
 
123
      uintmax_t proc_id;
 
124
      char *tmp;
 
125
      errno = 0;
 
126
      proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
 
127
      
 
128
      if(errno != 0 or *tmp != '\0'
 
129
         or proc_id != (uintmax_t)((pid_t)proc_id)){
 
130
        return 0;
 
131
      }
 
132
    }
 
133
    
 
134
    char *cmdline_filename;
 
135
    ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
 
136
                   proc_entry->d_name);
 
137
    if(ret == -1){
 
138
      error_plus(0, errno, "asprintf");
 
139
      return 0;
 
140
    }
 
141
    
 
142
    /* Open /proc/<pid>/cmdline */
 
143
    cl_fd = open(cmdline_filename, O_RDONLY);
 
144
    free(cmdline_filename);
 
145
    if(cl_fd == -1){
 
146
      if(errno != ENOENT){
 
147
        error_plus(0, errno, "open");
 
148
      }
 
149
      return 0;
 
150
    }
 
151
    
 
152
    char *cmdline = NULL;
 
153
    {
 
154
      size_t cmdline_len = 0;
 
155
      size_t cmdline_allocated = 0;
 
156
      char *tmp;
 
157
      const size_t blocksize = 1024;
 
158
      ssize_t sret;
 
159
      do {
 
160
        /* Allocate more space? */
 
161
        if(cmdline_len + blocksize + 1 > cmdline_allocated){
 
162
          tmp = realloc(cmdline, cmdline_allocated + blocksize + 1);
 
163
          if(tmp == NULL){
 
164
            error_plus(0, errno, "realloc");
 
165
            free(cmdline);
 
166
            close(cl_fd);
 
167
            return 0;
 
168
          }
 
169
          cmdline = tmp;
 
170
          cmdline_allocated += blocksize;
 
171
        }
 
172
        
 
173
        /* Read data */
 
174
        sret = read(cl_fd, cmdline + cmdline_len,
 
175
                    cmdline_allocated - cmdline_len);
 
176
        if(sret == -1){
 
177
          error_plus(0, errno, "read");
 
178
          free(cmdline);
 
179
          close(cl_fd);
 
180
          return 0;
 
181
        }
 
182
        cmdline_len += (size_t)sret;
 
183
      } while(sret != 0);
 
184
      ret = close(cl_fd);
 
185
      if(ret == -1){
 
186
        error_plus(0, errno, "close");
 
187
        free(cmdline);
 
188
        return 0;
 
189
      }
 
190
      cmdline[cmdline_len] = '\0'; /* Make sure it is terminated */
 
191
    }
 
192
    /* we now have cmdline */
 
193
    
 
194
    /* get basename */
 
195
    char *cmdline_base = strrchr(cmdline, '/');
 
196
    if(cmdline_base != NULL){
 
197
      cmdline_base += 1;                /* skip the slash */
 
198
    } else {
 
199
      cmdline_base = cmdline;
 
200
    }
 
201
    
 
202
    if(strcmp(cmdline_base, plymouth_name) != 0){
 
203
      if(debug){
 
204
        fprintf(stderr, "\"%s\" is not \"%s\"\n", cmdline_base,
 
205
                plymouth_name);
 
206
      }
 
207
      free(cmdline);
 
208
      return 0;
 
209
    }
 
210
    if(debug){
 
211
      fprintf(stderr, "\"%s\" equals \"%s\"\n", cmdline_base,
 
212
              plymouth_name);
 
213
    }
 
214
    free(cmdline);
 
215
    return 1;
 
216
  }
 
217
  
 
218
  struct dirent **direntries = NULL;
 
219
  int ret;
 
220
  ret = scandir("/proc", &direntries, is_plymouth, alphasort);
 
221
  if(ret == -1){
 
222
    error_plus(1, errno, "scandir");
 
223
  }
 
224
  {
 
225
    int i = ret;
 
226
    while(i--){
 
227
      free(direntries[i]);
 
228
    }
 
229
  }
 
230
  free(direntries);
 
231
  return ret > 0;
 
232
}
 
233
 
 
234
 
72
235
int main(int argc, char **argv){
73
 
  ssize_t ret;
 
236
  ssize_t sret;
 
237
  int ret;
74
238
  size_t n;
75
239
  struct termios t_new, t_old;
76
240
  char *buffer = NULL;
77
241
  char *prefix = NULL;
 
242
  char *prompt = NULL;
78
243
  int status = EXIT_SUCCESS;
79
244
  struct sigaction old_action,
80
245
    new_action = { .sa_handler = termination_handler,
84
249
      { .name = "prefix", .key = 'p',
85
250
        .arg = "PREFIX", .flags = 0,
86
251
        .doc = "Prefix shown before the prompt", .group = 2 },
 
252
      { .name = "prompt", .key = 129,
 
253
        .arg = "PROMPT", .flags = 0,
 
254
        .doc = "The prompt to show", .group = 2 },
87
255
      { .name = "debug", .key = 128,
88
256
        .doc = "Debug mode", .group = 3 },
 
257
      /*
 
258
       * These reproduce what we would get without ARGP_NO_HELP
 
259
       */
 
260
      { .name = "help", .key = '?',
 
261
        .doc = "Give this help list", .group = -1 },
 
262
      { .name = "usage", .key = -3,
 
263
        .doc = "Give a short usage message", .group = -1 },
 
264
      { .name = "version", .key = 'V',
 
265
        .doc = "Print program version", .group = -1 },
89
266
      { .name = NULL }
90
267
    };
91
268
    
 
269
    __attribute__((nonnull(3)))
92
270
    error_t parse_opt (int key, char *arg, struct argp_state *state){
 
271
      errno = 0;
93
272
      switch (key){
94
 
      case 'p':
 
273
      case 'p':                 /* --prefix */
95
274
        prefix = arg;
96
275
        break;
97
 
      case 128:
 
276
      case 128:                 /* --debug */
98
277
        debug = true;
99
278
        break;
100
 
      case ARGP_KEY_ARG:
101
 
        argp_usage(state);
 
279
      case 129:                 /* --prompt */
 
280
        prompt = arg;
102
281
        break;
103
 
      case ARGP_KEY_END:
 
282
        /*
 
283
         * These reproduce what we would get without ARGP_NO_HELP
 
284
         */
 
285
      case '?':                 /* --help */
 
286
        argp_state_help(state, state->out_stream,
 
287
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
 
288
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
289
        __builtin_unreachable();
 
290
      case -3:                  /* --usage */
 
291
        argp_state_help(state, state->out_stream,
 
292
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
293
        __builtin_unreachable();
 
294
      case 'V':                 /* --version */
 
295
        fprintf(state->out_stream, "%s\n", argp_program_version);
 
296
        exit(argp_err_exit_status);
104
297
        break;
105
298
      default:
106
299
        return ARGP_ERR_UNKNOWN;
107
300
      }
108
 
      return 0;
 
301
      return errno;
109
302
    }
110
303
    
111
304
    struct argp argp = { .options = options, .parser = parse_opt,
112
305
                         .args_doc = "",
113
306
                         .doc = "Mandos password-prompt -- Read and"
114
307
                         " output a password" };
115
 
    ret = argp_parse(&argp, argc, argv, 0, 0, NULL);
116
 
    if(ret == ARGP_ERR_UNKNOWN){
117
 
      fprintf(stderr, "Unknown error while parsing arguments\n");
118
 
      return EX_SOFTWARE;
 
308
    ret = argp_parse(&argp, argc, argv,
 
309
                     ARGP_IN_ORDER | ARGP_NO_HELP, NULL, NULL);
 
310
    switch(ret){
 
311
    case 0:
 
312
      break;
 
313
    case ENOMEM:
 
314
    default:
 
315
      errno = ret;
 
316
      error_plus(0, errno, "argp_parse");
 
317
      return EX_OSERR;
 
318
    case EINVAL:
 
319
      return EX_USAGE;
119
320
    }
120
321
  }
121
322
  
122
323
  if(debug){
123
324
    fprintf(stderr, "Starting %s\n", argv[0]);
124
325
  }
 
326
 
 
327
  if(conflict_detection()){
 
328
    if(debug){
 
329
      fprintf(stderr, "Stopping %s because of conflict\n", argv[0]);
 
330
    }
 
331
    return EXIT_FAILURE;
 
332
  }
 
333
  
125
334
  if(debug){
126
335
    fprintf(stderr, "Storing current terminal attributes\n");
127
336
  }
128
337
  
129
338
  if(tcgetattr(STDIN_FILENO, &t_old) != 0){
130
339
    int e = errno;
131
 
    perror("tcgetattr");
 
340
    error_plus(0, errno, "tcgetattr");
132
341
    switch(e){
133
342
    case EBADF:
134
343
    case ENOTTY:
141
350
  sigemptyset(&new_action.sa_mask);
142
351
  ret = sigaddset(&new_action.sa_mask, SIGINT);
143
352
  if(ret == -1){
144
 
    perror("sigaddset");
 
353
    error_plus(0, errno, "sigaddset");
145
354
    return EX_OSERR;
146
355
  }
147
356
  ret = sigaddset(&new_action.sa_mask, SIGHUP);
148
357
  if(ret == -1){
149
 
    perror("sigaddset");
 
358
    error_plus(0, errno, "sigaddset");
150
359
    return EX_OSERR;
151
360
  }
152
361
  ret = sigaddset(&new_action.sa_mask, SIGTERM);
153
362
  if(ret == -1){
154
 
    perror("sigaddset");
 
363
    error_plus(0, errno, "sigaddset");
155
364
    return EX_OSERR;
156
365
  }
157
366
  /* Need to check if the handler is SIG_IGN before handling:
160
369
  */
161
370
  ret = sigaction(SIGINT, NULL, &old_action);
162
371
  if(ret == -1){
163
 
    perror("sigaction");
 
372
    error_plus(0, errno, "sigaction");
164
373
    return EX_OSERR;
165
374
  }
166
375
  if(old_action.sa_handler != SIG_IGN){
167
376
    ret = sigaction(SIGINT, &new_action, NULL);
168
377
    if(ret == -1){
169
 
      perror("sigaction");
 
378
      error_plus(0, errno, "sigaction");
170
379
      return EX_OSERR;
171
380
    }
172
381
  }
173
382
  ret = sigaction(SIGHUP, NULL, &old_action);
174
383
  if(ret == -1){
175
 
    perror("sigaction");
 
384
    error_plus(0, errno, "sigaction");
176
385
    return EX_OSERR;
177
386
  }
178
387
  if(old_action.sa_handler != SIG_IGN){
179
388
    ret = sigaction(SIGHUP, &new_action, NULL);
180
389
    if(ret == -1){
181
 
      perror("sigaction");
 
390
      error_plus(0, errno, "sigaction");
182
391
      return EX_OSERR;
183
392
    }
184
393
  }
185
394
  ret = sigaction(SIGTERM, NULL, &old_action);
186
395
  if(ret == -1){
187
 
    perror("sigaction");
 
396
    error_plus(0, errno, "sigaction");
188
397
    return EX_OSERR;
189
398
  }
190
399
  if(old_action.sa_handler != SIG_IGN){
191
400
    ret = sigaction(SIGTERM, &new_action, NULL);
192
401
    if(ret == -1){
193
 
      perror("sigaction");
 
402
      error_plus(0, errno, "sigaction");
194
403
      return EX_OSERR;
195
404
    }
196
405
  }
204
413
  t_new.c_lflag &= ~(tcflag_t)ECHO;
205
414
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
206
415
    int e = errno;
207
 
    perror("tcsetattr-echo");
 
416
    error_plus(0, errno, "tcsetattr-echo");
208
417
    switch(e){
209
418
    case EBADF:
210
419
    case ENOTTY:
230
439
    if(prefix){
231
440
      fprintf(stderr, "%s ", prefix);
232
441
    }
233
 
    {
234
 
      const char *cryptsource = getenv("cryptsource");
235
 
      const char *crypttarget = getenv("crypttarget");
236
 
      const char *const prompt
237
 
        = "Enter passphrase to unlock the disk";
 
442
    if(prompt != NULL){
 
443
      fprintf(stderr, "%s: ", prompt);
 
444
    } else {
 
445
      const char *cryptsource = getenv("CRYPTTAB_SOURCE");
 
446
      const char *crypttarget = getenv("CRYPTTAB_NAME");
 
447
      /* Before cryptsetup 1.1.0~rc2 */
 
448
      if(cryptsource == NULL){
 
449
        cryptsource = getenv("cryptsource");
 
450
      }
 
451
      if(crypttarget == NULL){
 
452
        crypttarget = getenv("crypttarget");
 
453
      }
 
454
      const char *const prompt1 = "Unlocking the disk";
 
455
      const char *const prompt2 = "Enter passphrase";
238
456
      if(cryptsource == NULL){
239
457
        if(crypttarget == NULL){
240
 
          fprintf(stderr, "%s: ", prompt);
 
458
          fprintf(stderr, "%s to unlock the disk: ", prompt2);
241
459
        } else {
242
 
          fprintf(stderr, "%s (%s): ", prompt, crypttarget);
 
460
          fprintf(stderr, "%s (%s)\n%s: ", prompt1, crypttarget,
 
461
                  prompt2);
243
462
        }
244
463
      } else {
245
464
        if(crypttarget == NULL){
246
 
          fprintf(stderr, "%s %s: ", prompt, cryptsource);
 
465
          fprintf(stderr, "%s %s\n%s: ", prompt1, cryptsource,
 
466
                  prompt2);
247
467
        } else {
248
 
          fprintf(stderr, "%s %s (%s): ", prompt, cryptsource,
249
 
                  crypttarget);
 
468
          fprintf(stderr, "%s %s (%s)\n%s: ", prompt1, cryptsource,
 
469
                  crypttarget, prompt2);
250
470
        }
251
471
      }
252
472
    }
253
 
    ret = getline(&buffer, &n, stdin);
254
 
    if(ret > 0){
 
473
    sret = getline(&buffer, &n, stdin);
 
474
    if(sret > 0){
255
475
      status = EXIT_SUCCESS;
256
476
      /* Make n = data size instead of allocated buffer size */
257
 
      n = (size_t)ret;
 
477
      n = (size_t)sret;
258
478
      /* Strip final newline */
259
479
      if(n > 0 and buffer[n-1] == '\n'){
260
480
        buffer[n-1] = '\0';     /* not strictly necessary */
262
482
      }
263
483
      size_t written = 0;
264
484
      while(written < n){
265
 
        ret = write(STDOUT_FILENO, buffer + written, n - written);
266
 
        if(ret < 0){
 
485
        sret = write(STDOUT_FILENO, buffer + written, n - written);
 
486
        if(sret < 0){
267
487
          int e = errno;
268
 
          perror("write");
 
488
          error_plus(0, errno, "write");
269
489
          switch(e){
270
490
          case EBADF:
271
491
          case EFAULT:
282
502
          }
283
503
          break;
284
504
        }
285
 
        written += (size_t)ret;
 
505
        written += (size_t)sret;
286
506
      }
287
 
      ret = close(STDOUT_FILENO);
288
 
      if(ret == -1){
 
507
      sret = close(STDOUT_FILENO);
 
508
      if(sret == -1){
289
509
        int e = errno;
290
 
        perror("close");
 
510
        error_plus(0, errno, "close");
291
511
        switch(e){
292
512
        case EBADF:
293
513
          status = EX_OSFILE;
300
520
      }
301
521
      break;
302
522
    }
303
 
    if(ret < 0){
 
523
    if(sret < 0){
304
524
      int e = errno;
305
 
      if(errno != EINTR and not feof(stdin)){
306
 
        perror("getline");
307
 
        switch(e){
308
 
        case EBADF:
309
 
          status = EX_UNAVAILABLE;
310
 
        case EIO:
311
 
        case EINVAL:
312
 
        default:
313
 
          status = EX_IOERR;
 
525
      if(errno != EINTR){
 
526
        if(not feof(stdin)){
 
527
          error_plus(0, errno, "getline");
 
528
          switch(e){
 
529
          case EBADF:
 
530
            status = EX_UNAVAILABLE;
 
531
            break;
 
532
          case EIO:
 
533
          case EINVAL:
 
534
          default:
 
535
            status = EX_IOERR;
 
536
            break;
 
537
          }
314
538
          break;
 
539
        } else {
 
540
          clearerr(stdin);
315
541
        }
316
 
        break;
317
542
      }
318
543
    }
319
 
    /* if(ret == 0), then the only sensible thing to do is to retry to
320
 
       read from stdin */
 
544
    /* if(sret == 0), then the only sensible thing to do is to retry
 
545
       to read from stdin */
321
546
    fputc('\n', stderr);
322
547
    if(debug and not quit_now){
323
548
      /* If quit_now is nonzero, we were interrupted by a signal, and
332
557
    fprintf(stderr, "Restoring terminal attributes\n");
333
558
  }
334
559
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
335
 
    perror("tcsetattr+echo");
 
560
    error_plus(0, errno, "tcsetattr+echo");
336
561
  }
337
562
  
338
563
  if(quit_now){
340
565
    old_action.sa_handler = SIG_DFL;
341
566
    ret = sigaction(signal_received, &old_action, NULL);
342
567
    if(ret == -1){
343
 
      perror("sigaction");
 
568
      error_plus(0, errno, "sigaction");
344
569
    }
345
570
    raise(signal_received);
346
571
  }