/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: 2011-12-31 20:07:11 UTC
  • mfrom: (535.1.9 wireless-network-hook)
  • Revision ID: teddy@recompile.se-20111231200711-6dli3r8drftem57r
Merge new wireless network hook.  Fix bridge network hook to use
hardware addresses instead of interface names.  Implement and document
new "CONNECT" environment variable for network hooks.

Show diffs side-by-side

added added

removed removed

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