/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: 2024-09-09 04:24:39 UTC
  • mto: This revision was merged to the branch mainline in revision 410.
  • Revision ID: teddy@recompile.se-20240909042439-j85mr20uli2hnyis
Eliminate compiler warnings

Many programs use nested functions, which now result in a linker
warning about executable stack.  Hide this warning.  Also, rewrite a
loop in the plymouth plugin to avoid warning about signed overflow.
This change also makes the plugin pick the alphabetically first
process entry instead of the last, in case many plymouth processes are
found (which should be unlikely).

* Makefile (plugin-runner, dracut-module/password-agent,
  plugins.d/password-prompt, plugins.d/mandos-client,
  plugins.d/plymouth): New target; set LDFLAGS to add "-Xlinker
  --no-warn-execstack".
* plugins.d/plymouth.c (get_pid): When no pid files are found, and we
  are looking through the process list, go though it from the start
  instead of from the end, i.e. in normal alphabetical order and not
  in reverse order.

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-2010 Teddy Hogeborn
6
 
 * Copyright © 2008-2010 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, 2021-2022 Teddy Hogeborn
 
6
 * Copyright © 2008-2019, 2021-2022 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(), asprintf() */
26
 
 
27
 
#include <termios.h>            /* struct termios, tcsetattr(),
28
 
                                   TCSAFLUSH, tcgetattr(), ECHO */
29
 
#include <unistd.h>             /* struct termios, tcsetattr(),
30
 
                                   STDIN_FILENO, TCSAFLUSH,
31
 
                                   tcgetattr(), ECHO, readlink() */
32
 
#include <signal.h>             /* sig_atomic_t, raise(), struct
33
 
                                   sigaction, sigemptyset(),
34
 
                                   sigaction(), sigaddset(), SIGINT,
35
 
                                   SIGQUIT, SIGHUP, SIGTERM,
 
26
#define _GNU_SOURCE             /* vasprintf(),
 
27
                                   program_invocation_short_name,
 
28
                                   asprintf(), getline() */
 
29
#include <sys/types.h>          /* sig_atomic_t, pid_t */
 
30
#include <stdbool.h>            /* bool, false, true */
 
31
#include <argp.h>               /* argp_program_version,
 
32
                                   argp_program_bug_address,
 
33
                                   struct argp_option,
 
34
                                   struct argp_state, argp_state_help,
 
35
                                   ARGP_HELP_STD_HELP,
 
36
                                   ARGP_HELP_EXIT_ERR,
 
37
                                   ARGP_HELP_EXIT_OK, ARGP_HELP_USAGE,
 
38
                                   argp_err_exit_status,
 
39
                                   ARGP_ERR_UNKNOWN, argp_parse(),
 
40
                                   ARGP_IN_ORDER, ARGP_NO_HELP */
 
41
#include <stdarg.h>             /* va_list, va_start(), vfprintf() */
 
42
#include <stdio.h>              /* vasprintf(), fprintf(), stderr,
 
43
                                   vfprintf(), asprintf(), getline(),
 
44
                                   stdin, feof(), clearerr(),
 
45
                                   fputc() */
 
46
#include <errno.h>              /* program_invocation_short_name,
 
47
                                   errno, ENOENT, error_t, ENOMEM,
 
48
                                   EINVAL, EBADF, ENOTTY, EFAULT,
 
49
                                   EFBIG, EIO, ENOSPC, EINTR */
 
50
#include <string.h>             /* strerror(), strrchr(), strcmp() */
 
51
#include <error.h>              /* error() */
 
52
#include <stdlib.h>             /* free(), realloc(), EXIT_SUCCESS,
 
53
                                   EXIT_FAILURE, getenv() */
 
54
#include <unistd.h>             /* access(), R_OK, ssize_t, close(),
 
55
                                   read(), STDIN_FILENO, write(),
 
56
                                   STDOUT_FILENO */
 
57
#include <dirent.h>             /* struct dirent, scandir(),
 
58
                                   alphasort() */
 
59
#include <inttypes.h>           /* uintmax_t, strtoumax() */
 
60
#include <iso646.h>             /* or, and, not */
 
61
#include <fcntl.h>              /* open(), O_RDONLY */
 
62
#include <stddef.h>             /* NULL, size_t */
 
63
#include <termios.h>            /* struct termios, tcgetattr(),
 
64
                                   tcflag_t, ECHO, tcsetattr(),
 
65
                                   TCSAFLUSH */
 
66
#include <signal.h>             /* struct sigaction, sigemptyset(),
 
67
                                   sigaddset(), SIGINT, SIGHUP,
 
68
                                   SIGTERM, SIG_IGN, SIG_DFL,
36
69
                                   raise() */
37
 
#include <stddef.h>             /* NULL, size_t, ssize_t */
38
 
#include <sys/types.h>          /* ssize_t, struct dirent, pid_t, ssize_t */
39
 
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE,
40
 
                                   getenv(), free() */
41
 
#include <dirent.h>             /* scandir(), alphasort() */
42
 
#include <stdio.h>              /* fprintf(), stderr, getline(),
43
 
                                   stdin, feof(), fputc()
44
 
                                */
45
 
#include <errno.h>              /* errno, EBADF, ENOTTY, EINVAL,
46
 
                                   EFAULT, EFBIG, EIO, ENOSPC, EINTR
47
 
                                */
48
 
#include <error.h>              /* error() */
49
 
#include <iso646.h>             /* or, not */
50
 
#include <stdbool.h>            /* bool, false, true */
51
 
#include <inttypes.h>           /* strtoumax() */
52
 
#include <sys/stat.h>           /* struct stat, lstat() */
53
 
#include <string.h>             /* strlen, rindex, memcmp */
54
 
#include <argp.h>               /* struct argp_option, struct
55
 
                                   argp_state, struct argp,
56
 
                                   argp_parse(), error_t,
57
 
                                   ARGP_KEY_ARG, ARGP_KEY_END,
58
 
                                   ARGP_ERR_UNKNOWN */
59
 
#include <sysexits.h>           /* EX_SOFTWARE, EX_OSERR,
60
 
                                   EX_UNAVAILABLE, EX_IOERR, EX_OK */
 
70
#include <sysexits.h>           /* EX_OSERR, EX_USAGE, EX_UNAVAILABLE,
 
71
                                   EX_IOERR, EX_OSFILE, EX_OK */
61
72
 
62
73
volatile sig_atomic_t quit_now = 0;
63
74
int signal_received;
64
75
bool debug = false;
65
76
const char *argp_program_version = "password-prompt " VERSION;
66
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
67
 
 
68
 
/* Needed for conflic resolution */
69
 
const char plymouthd_path[] = "/sbin/plymouth";
70
 
 
 
77
const char *argp_program_bug_address = "<mandos@recompile.se>";
 
78
 
 
79
/* Needed for conflict resolution */
 
80
const char plymouth_name[] = "plymouthd";
 
81
 
 
82
/* Function to use when printing errors */
 
83
__attribute__((format (gnu_printf, 3, 4)))
 
84
void error_plus(int status, int errnum, const char *formatstring,
 
85
                ...){
 
86
  va_list ap;
 
87
  char *text;
 
88
  int ret;
 
89
  
 
90
  va_start(ap, formatstring);
 
91
  ret = vasprintf(&text, formatstring, ap);
 
92
  if(ret == -1){
 
93
    fprintf(stderr, "Mandos plugin %s: ",
 
94
            program_invocation_short_name);
 
95
    vfprintf(stderr, formatstring, ap);
 
96
    fprintf(stderr, ": %s\n", strerror(errnum));
 
97
    error(status, errno, "vasprintf while printing error");
 
98
    return;
 
99
  }
 
100
  fprintf(stderr, "Mandos plugin ");
 
101
  error(status, errnum, "%s", text);
 
102
  free(text);
 
103
}
71
104
 
72
105
static void termination_handler(int signum){
73
106
  if(quit_now){
79
112
 
80
113
bool conflict_detection(void){
81
114
 
82
 
  /* plymouth conflicts with password-promt since both want to control the
83
 
     associated terminal. Password-prompt exit since plymouth perferms the same
84
 
     functionallity.
 
115
  /* plymouth conflicts with password-prompt since both want to read
 
116
     from the terminal.  Password-prompt will exit if it detects
 
117
     plymouth since plymouth performs the same functionality.
85
118
   */
 
119
  if(access("/run/plymouth/pid", R_OK) == 0){
 
120
    return true;
 
121
  }
 
122
  
 
123
  __attribute__((nonnull))
86
124
  int is_plymouth(const struct dirent *proc_entry){
87
125
    int ret;
 
126
    int cl_fd;
88
127
    {
89
 
      uintmax_t maxvalue;
 
128
      uintmax_t proc_id;
90
129
      char *tmp;
91
130
      errno = 0;
92
 
      maxvalue = strtoumax(proc_entry->d_name, &tmp, 10);
 
131
      proc_id = strtoumax(proc_entry->d_name, &tmp, 10);
93
132
      
94
133
      if(errno != 0 or *tmp != '\0'
95
 
         or maxvalue != (uintmax_t)((pid_t)maxvalue)){
 
134
         or proc_id != (uintmax_t)((pid_t)proc_id)){
96
135
        return 0;
97
136
      }
98
137
    }
99
 
    char exe_target[sizeof(plymouthd_path)];
100
 
    char *exe_link;
101
 
    ret = asprintf(&exe_link, "/proc/%s/exe", proc_entry->d_name);
 
138
    
 
139
    char *cmdline_filename;
 
140
    ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
 
141
                   proc_entry->d_name);
102
142
    if(ret == -1){
103
 
      error(0, errno, "asprintf");
 
143
      error_plus(0, errno, "asprintf");
104
144
      return 0;
105
145
    }
106
 
  
107
 
    struct stat exe_stat;
108
 
    ret = lstat(exe_link, &exe_stat);
109
 
    if(ret == -1){
110
 
      free(exe_link);
 
146
    
 
147
    /* Open /proc/<pid>/cmdline */
 
148
    cl_fd = open(cmdline_filename, O_RDONLY);
 
149
    free(cmdline_filename);
 
150
    if(cl_fd == -1){
111
151
      if(errno != ENOENT){
112
 
        error(0, errno, "lstat");
113
 
      }
114
 
      return 0;
115
 
    }
116
 
  
117
 
    if(not S_ISLNK(exe_stat.st_mode)
118
 
       or exe_stat.st_uid != 0
119
 
       or exe_stat.st_gid != 0){
120
 
      free(exe_link);
121
 
      return 0;
122
 
    }
123
 
  
124
 
    ssize_t sret = readlink(exe_link, exe_target, sizeof(exe_target));
125
 
    free(exe_link);
126
 
    if((sret != (ssize_t)sizeof(plymouthd_path)-1) or
127
 
       (memcmp(plymouthd_path, exe_target,
128
 
               sizeof(plymouthd_path)-1) != 0)){
129
 
      return 0;
130
 
    }
 
152
        error_plus(0, errno, "open");
 
153
      }
 
154
      return 0;
 
155
    }
 
156
    
 
157
    char *cmdline = NULL;
 
158
    {
 
159
      size_t cmdline_len = 0;
 
160
      size_t cmdline_allocated = 0;
 
161
      char *tmp;
 
162
      const size_t blocksize = 1024;
 
163
      ssize_t sret;
 
164
      do {
 
165
        /* Allocate more space? */
 
166
        if(cmdline_len + blocksize + 1 > cmdline_allocated){
 
167
          tmp = realloc(cmdline, cmdline_allocated + blocksize + 1);
 
168
          if(tmp == NULL){
 
169
            error_plus(0, errno, "realloc");
 
170
            free(cmdline);
 
171
            close(cl_fd);
 
172
            return 0;
 
173
          }
 
174
          cmdline = tmp;
 
175
          cmdline_allocated += blocksize;
 
176
        }
 
177
        
 
178
        /* Read data */
 
179
        sret = read(cl_fd, cmdline + cmdline_len,
 
180
                    cmdline_allocated - cmdline_len);
 
181
        if(sret == -1){
 
182
          error_plus(0, errno, "read");
 
183
          free(cmdline);
 
184
          close(cl_fd);
 
185
          return 0;
 
186
        }
 
187
        cmdline_len += (size_t)sret;
 
188
      } while(sret != 0);
 
189
      ret = close(cl_fd);
 
190
      if(ret == -1){
 
191
        error_plus(0, errno, "close");
 
192
        free(cmdline);
 
193
        return 0;
 
194
      }
 
195
      cmdline[cmdline_len] = '\0'; /* Make sure it is terminated */
 
196
    }
 
197
    /* we now have cmdline */
 
198
    
 
199
    /* get basename */
 
200
    char *cmdline_base = strrchr(cmdline, '/');
 
201
    if(cmdline_base != NULL){
 
202
      cmdline_base += 1;                /* skip the slash */
 
203
    } else {
 
204
      cmdline_base = cmdline;
 
205
    }
 
206
    
 
207
    if(strcmp(cmdline_base, plymouth_name) != 0){
 
208
      if(debug){
 
209
        fprintf(stderr, "\"%s\" is not \"%s\"\n", cmdline_base,
 
210
                plymouth_name);
 
211
      }
 
212
      free(cmdline);
 
213
      return 0;
 
214
    }
 
215
    if(debug){
 
216
      fprintf(stderr, "\"%s\" equals \"%s\"\n", cmdline_base,
 
217
              plymouth_name);
 
218
    }
 
219
    free(cmdline);
131
220
    return 1;
132
221
  }
133
 
 
134
 
  struct dirent **direntries;
 
222
  
 
223
  struct dirent **direntries = NULL;
135
224
  int ret;
136
225
  ret = scandir("/proc", &direntries, is_plymouth, alphasort);
137
 
  if (ret == -1){
138
 
    error(1, errno, "scandir");
139
 
  }
140
 
  if (ret < 0){
141
 
    return 1;
142
 
  } else {
143
 
    return 0;
144
 
  }
 
226
  if(ret == -1){
 
227
    error_plus(1, errno, "scandir");
 
228
  }
 
229
  {
 
230
    int i = ret;
 
231
    while(i--){
 
232
      free(direntries[i]);
 
233
    }
 
234
  }
 
235
  free(direntries);
 
236
  return ret > 0;
145
237
}
146
238
 
147
239
 
152
244
  struct termios t_new, t_old;
153
245
  char *buffer = NULL;
154
246
  char *prefix = NULL;
 
247
  char *prompt = NULL;
155
248
  int status = EXIT_SUCCESS;
156
249
  struct sigaction old_action,
157
250
    new_action = { .sa_handler = termination_handler,
161
254
      { .name = "prefix", .key = 'p',
162
255
        .arg = "PREFIX", .flags = 0,
163
256
        .doc = "Prefix shown before the prompt", .group = 2 },
 
257
      { .name = "prompt", .key = 129,
 
258
        .arg = "PROMPT", .flags = 0,
 
259
        .doc = "The prompt to show", .group = 2 },
164
260
      { .name = "debug", .key = 128,
165
261
        .doc = "Debug mode", .group = 3 },
166
262
      /*
175
271
      { .name = NULL }
176
272
    };
177
273
    
 
274
    __attribute__((nonnull(3)))
178
275
    error_t parse_opt (int key, char *arg, struct argp_state *state){
179
276
      errno = 0;
180
277
      switch (key){
181
 
      case 'p':
 
278
      case 'p':                 /* --prefix */
182
279
        prefix = arg;
183
280
        break;
184
 
      case 128:
 
281
      case 128:                 /* --debug */
185
282
        debug = true;
186
283
        break;
 
284
      case 129:                 /* --prompt */
 
285
        prompt = arg;
 
286
        break;
187
287
        /*
188
288
         * These reproduce what we would get without ARGP_NO_HELP
189
289
         */
191
291
        argp_state_help(state, state->out_stream,
192
292
                        (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
193
293
                        & ~(unsigned int)ARGP_HELP_EXIT_OK);
 
294
        __builtin_unreachable();
194
295
      case -3:                  /* --usage */
195
296
        argp_state_help(state, state->out_stream,
196
297
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
 
298
        __builtin_unreachable();
197
299
      case 'V':                 /* --version */
198
300
        fprintf(state->out_stream, "%s\n", argp_program_version);
199
301
        exit(argp_err_exit_status);
216
318
    case ENOMEM:
217
319
    default:
218
320
      errno = ret;
219
 
      error(0, errno, "argp_parse");
 
321
      error_plus(0, errno, "argp_parse");
220
322
      return EX_OSERR;
221
323
    case EINVAL:
222
324
      return EX_USAGE;
227
329
    fprintf(stderr, "Starting %s\n", argv[0]);
228
330
  }
229
331
 
230
 
  if (conflict_detection()){
 
332
  if(conflict_detection()){
231
333
    if(debug){
232
 
      fprintf(stderr, "Stopping %s because of conflict", argv[0]);
 
334
      fprintf(stderr, "Stopping %s because of conflict\n", argv[0]);
233
335
    }
234
336
    return EXIT_FAILURE;
235
337
  }
240
342
  
241
343
  if(tcgetattr(STDIN_FILENO, &t_old) != 0){
242
344
    int e = errno;
243
 
    error(0, errno, "tcgetattr");
 
345
    error_plus(0, errno, "tcgetattr");
244
346
    switch(e){
245
347
    case EBADF:
246
348
    case ENOTTY:
253
355
  sigemptyset(&new_action.sa_mask);
254
356
  ret = sigaddset(&new_action.sa_mask, SIGINT);
255
357
  if(ret == -1){
256
 
    error(0, errno, "sigaddset");
 
358
    error_plus(0, errno, "sigaddset");
257
359
    return EX_OSERR;
258
360
  }
259
361
  ret = sigaddset(&new_action.sa_mask, SIGHUP);
260
362
  if(ret == -1){
261
 
    error(0, errno, "sigaddset");
 
363
    error_plus(0, errno, "sigaddset");
262
364
    return EX_OSERR;
263
365
  }
264
366
  ret = sigaddset(&new_action.sa_mask, SIGTERM);
265
367
  if(ret == -1){
266
 
    error(0, errno, "sigaddset");
 
368
    error_plus(0, errno, "sigaddset");
267
369
    return EX_OSERR;
268
370
  }
269
371
  /* Need to check if the handler is SIG_IGN before handling:
272
374
  */
273
375
  ret = sigaction(SIGINT, NULL, &old_action);
274
376
  if(ret == -1){
275
 
    error(0, errno, "sigaction");
 
377
    error_plus(0, errno, "sigaction");
276
378
    return EX_OSERR;
277
379
  }
278
380
  if(old_action.sa_handler != SIG_IGN){
279
381
    ret = sigaction(SIGINT, &new_action, NULL);
280
382
    if(ret == -1){
281
 
      error(0, errno, "sigaction");
 
383
      error_plus(0, errno, "sigaction");
282
384
      return EX_OSERR;
283
385
    }
284
386
  }
285
387
  ret = sigaction(SIGHUP, NULL, &old_action);
286
388
  if(ret == -1){
287
 
    error(0, errno, "sigaction");
 
389
    error_plus(0, errno, "sigaction");
288
390
    return EX_OSERR;
289
391
  }
290
392
  if(old_action.sa_handler != SIG_IGN){
291
393
    ret = sigaction(SIGHUP, &new_action, NULL);
292
394
    if(ret == -1){
293
 
      error(0, errno, "sigaction");
 
395
      error_plus(0, errno, "sigaction");
294
396
      return EX_OSERR;
295
397
    }
296
398
  }
297
399
  ret = sigaction(SIGTERM, NULL, &old_action);
298
400
  if(ret == -1){
299
 
    error(0, errno, "sigaction");
 
401
    error_plus(0, errno, "sigaction");
300
402
    return EX_OSERR;
301
403
  }
302
404
  if(old_action.sa_handler != SIG_IGN){
303
405
    ret = sigaction(SIGTERM, &new_action, NULL);
304
406
    if(ret == -1){
305
 
      error(0, errno, "sigaction");
 
407
      error_plus(0, errno, "sigaction");
306
408
      return EX_OSERR;
307
409
    }
308
410
  }
316
418
  t_new.c_lflag &= ~(tcflag_t)ECHO;
317
419
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
318
420
    int e = errno;
319
 
    error(0, errno, "tcsetattr-echo");
 
421
    error_plus(0, errno, "tcsetattr-echo");
320
422
    switch(e){
321
423
    case EBADF:
322
424
    case ENOTTY:
342
444
    if(prefix){
343
445
      fprintf(stderr, "%s ", prefix);
344
446
    }
345
 
    {
 
447
    if(prompt != NULL){
 
448
      fprintf(stderr, "%s: ", prompt);
 
449
    } else {
346
450
      const char *cryptsource = getenv("CRYPTTAB_SOURCE");
347
451
      const char *crypttarget = getenv("CRYPTTAB_NAME");
348
452
      /* Before cryptsetup 1.1.0~rc2 */
386
490
        sret = write(STDOUT_FILENO, buffer + written, n - written);
387
491
        if(sret < 0){
388
492
          int e = errno;
389
 
          error(0, errno, "write");
 
493
          error_plus(0, errno, "write");
390
494
          switch(e){
391
495
          case EBADF:
392
496
          case EFAULT:
408
512
      sret = close(STDOUT_FILENO);
409
513
      if(sret == -1){
410
514
        int e = errno;
411
 
        error(0, errno, "close");
 
515
        error_plus(0, errno, "close");
412
516
        switch(e){
413
517
        case EBADF:
414
518
          status = EX_OSFILE;
423
527
    }
424
528
    if(sret < 0){
425
529
      int e = errno;
426
 
      if(errno != EINTR and not feof(stdin)){
427
 
        error(0, errno, "getline");
428
 
        switch(e){
429
 
        case EBADF:
430
 
          status = EX_UNAVAILABLE;
431
 
        case EIO:
432
 
        case EINVAL:
433
 
        default:
434
 
          status = EX_IOERR;
 
530
      if(errno != EINTR){
 
531
        if(not feof(stdin)){
 
532
          error_plus(0, errno, "getline");
 
533
          switch(e){
 
534
          case EBADF:
 
535
            status = EX_UNAVAILABLE;
 
536
            break;
 
537
          case EIO:
 
538
          case EINVAL:
 
539
          default:
 
540
            status = EX_IOERR;
 
541
            break;
 
542
          }
435
543
          break;
 
544
        } else {
 
545
          clearerr(stdin);
436
546
        }
437
 
        break;
438
547
      }
439
548
    }
440
 
    /* if(sret == 0), then the only sensible thing to do is to retry to
441
 
       read from stdin */
 
549
    /* if(sret == 0), then the only sensible thing to do is to retry
 
550
       to read from stdin */
442
551
    fputc('\n', stderr);
443
552
    if(debug and not quit_now){
444
553
      /* If quit_now is nonzero, we were interrupted by a signal, and
453
562
    fprintf(stderr, "Restoring terminal attributes\n");
454
563
  }
455
564
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
456
 
    error(0, errno, "tcsetattr+echo");
 
565
    error_plus(0, errno, "tcsetattr+echo");
457
566
  }
458
567
  
459
568
  if(quit_now){
461
570
    old_action.sa_handler = SIG_DFL;
462
571
    ret = sigaction(signal_received, &old_action, NULL);
463
572
    if(ret == -1){
464
 
      error(0, errno, "sigaction");
 
573
      error_plus(0, errno, "sigaction");
465
574
    }
466
575
    raise(signal_received);
467
576
  }