/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to plugins.d/password-prompt.c

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