/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-16 03:29:08 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080816032908-ihw7c05r2mnyk389
Add feature to specify custom environment variables for plugins.

* plugin-runner.c (plugin): New members "environ" and "envc" to
                            contain possible custom environment.
  (getplugin): Return NULL on failure instead of doing exit(); all
               callers changed.
  (add_to_char_array): New helper function for "add_argument" and
                       "add_environment".
  (addargument): Renamed to "add_argument".  Return bool.  Call
                 "add_to_char_array" to actually do things.
  (add_environment): New; analogous to "add_argument".
  (addcustomargument): Renamed to "add_to_argv" to avoid confusion
                       with "add_argument".
  (main): New options "--global-envs" and "--envs-for" to specify
          custom environment for plugins.  Print environment for
          plugins in debug mode.  Use asprintf instead of strcpy and
          strcat.  Use execve() for plugins with custom environments.
          Free environment for plugin when freeing plugin list.

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