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

First version of a somewhat complete D-Bus server interface.  Also
change user/group name to "_mandos".

* debian/mandos.postinst: Rename old "mandos" user and group to
                          "_mandos"; create "_mandos" user and group
                          if none exist.
* debian/mandos-client.postinst: - '' -

* initramfs-tools-hook: Try "_mandos" before "mandos" as user and
                        group name.

* mandos (_datetime_to_dbus_struct): New; was previously local.
  (Client.started): Renamed to "last_started".  All users changed.
  (Client.started): New; boolean.
  (Client.dbus_object_path): New.
  (Client.check_command): Renamed to "checker_command".  All users
                          changed.
  (Client.__init__): Set and use "self.dbus_object_path".  Set
                     "self.started".
  (Client.start): Update "self.started".  Emit "self.PropertyChanged"
                  signals for both "started" and "last_started".
  (Client.stop): Update "self.started".  Emit "self.PropertyChanged"
                 signal for "started".
  (Client.checker_callback): Take additional "command" argument.  All
                             callers changed. Emit
                             "self.PropertyChanged" signal.
  (Client.bump_timeout): Emit "self.PropertyChanged" signal for
                         "last_checked_ok".
  (Client.start_checker): Emit "self.PropertyChanged" signal for
                          "checker_running".
  (Client.stop_checker): Emit "self.PropertyChanged" signal for
                         "checker_running".
  (Client.still_valid): Bug fix: use "getattr(self, started, False)"
                        instead of "self.started" in case this client
                        object is so new that the "started" attribute
                        has not been created yet.
  (Client.IntervalChanged, Client.CheckerIsRunning, Client.GetChecker,
  Client.GetCreated, Client.GetFingerprint, Client.GetHost,
  Client.GetInterval, Client.GetName, Client.GetStarted,
  Client.GetTimeout, Client.StateChanged, Client.TimeoutChanged):
  Removed; all callers changed.
  (Client.CheckerCompleted): Add "condition" and "command" arguments.
                             All callers changed.
  (Client.GetAllProperties, Client.PropertyChanged): New.
  (Client.StillValid): Renamed to "IsStillValid".
  (Client.StartChecker): Changed to its own function to avoid the
                         return value from "Client.start_checker()".
  (Client.Stop): Changed to its own function to avoid the return value
                 from "Client.stop()".
  (main): Try "_mandos" before "mandos" as user and group name.
          Removed inner function "remove_from_clients".  New inner
          class "MandosServer".

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