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