/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 plugins.d/password-prompt.c

  • Committer: Teddy Hogeborn
  • Date: 2008-08-29 05:53:59 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080829055359-wkdasnyxtylmnxus
* mandos.xml (EXAMPLE): Replaced all occurences of command name with
                        "&COMMANDNAME;".

* plugins.d/password-prompt.c (main): Improved some documentation
                                      strings.  Do perror() of
                                      tcgetattr() fails.  Add debug
                                      output if interrupted by signal.
                                      Loop over write() instead of
                                      using fwrite() when outputting
                                      password.  Add debug output if
                                      getline() returns 0, unless it
                                      was caused by a signal.  Add
                                      exit status code to debug
                                      output.

* plugins.d/password-prompt.xml: Changed all single quotes to double
                                 quotes for consistency.  Removed
                                 <?xml-stylesheet>.
  (ENTITY TIMESTAMP): New.  Automatically updated by Emacs time-stamp
                      by using Emacs local variables.
  (/refentry/refentryinfo/title): Changed to "Mandos Manual".
  (/refentry/refentryinfo/productname): Changed to "Mandos".
  (/refentry/refentryinfo/date): New; set to "&TIMESTAMP;".
  (/refentry/refentryinfo/copyright): Split copyright holders.
  (/refentry/refnamediv/refpurpose): Improved wording.
  (SYNOPSIS): Fix to use correct markup.  Add short options.
  (DESCRIPTION, OPTIONS): Improved wording.
  (OPTIONS): Improved wording.  Use more correct markup.  Document
             short options.
  (EXIT STATUS): Add text.
  (ENVIRONMENT): Document use of "cryptsource" and "crypttarget".
  (FILES): REMOVED.
  (BUGS): Add text.
  (EXAMPLE): Added some examples.
  (SECURITY): Added text.
  (SEE ALSO): Remove reference to mandos(8).  Add reference to
              crypttab(5).

Show diffs side-by-side

added added

removed removed

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