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