/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/passprompt.c

  • Committer: Teddy Hogeborn
  • Date: 2008-07-20 06:33:48 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080720063348-jscgy5p0itrgvlo8
* mandos-clients.conf ([foo]): Uncommented.
  ([foo]/secret): New.
  ([foo]/secfile): Commented out.
  ([foo]/checker): Changed to "fping -q -- %%(fqdn)s".
  ([foo]/timeout): New.

* server.py: New modeline for Python and Emacs.  Set a logging format.
  (Client.__init__): Bug fix: Choose either the value from the options
                     object or pass the argument through string_to_delta
                     for both "timeout" and "interval".
  (Client.checker_callback): Bug fix: Do not log spurious "Checker for
                             <foo> failed" messages.
  (Client.start_checker): Moved "Starting checker" log message down to
                          just before actually starting the subprocess.
                          Do not redirect the subprocesses' stdout to a
                          pipe.
  (peer_certificate, fingerprint): Added docstrings.
  (entry_group_state_changed): Call "killme()" instead of
                               "main_loop.quit()".
  (daemon, killme): New functions.
  (exitstatus, main_loop_started): New global variables.
  (__main__): Removed the "--cert", "--key", "--ca", and "--crl"
              options.  Removed the sleep command from the default
              checker.  Add a console logger in debug mode.  Call
              "killme()" instead of "main_loop.quit()" when there are no
              more clients.  Call "daemon()" if not in debug mode.
              Register "cleanup()" to run at exit.  Ignore some
              signals.  Catch DBusException to detect another running
              server and exit cleanly.  Exit with "exitstatus".
  (cleanup): New function.

Show diffs side-by-side

added added

removed removed

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