/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

  • Committer: Teddy Hogeborn
  • Date: 2015-05-23 20:18:34 UTC
  • mto: (237.7.304 trunk)
  • mto: This revision was merged to the branch mainline in revision 325.
  • Revision ID: teddy@recompile.se-20150523201834-e89ex4ito93yni8x
mandos: Use multiprocessing module to run checkers.

For a long time, the Mandos server has occasionally logged the message
"ERROR: Child process vanished".  This was never a fatal error, but it
has been annoying and slightly worrying, since a definite cause was
not found.  One potential cause could be the "multiprocessing" and
"subprocess" modules conflicting w.r.t. SIGCHLD.  To avoid this,
change the running of checkers from using subprocess.Popen
asynchronously to instead first create a multiprocessing.Process()
(which is asynchronous) calling a function, and have that function
then call subprocess.call() (which is synchronous).  In this way, the
only thing using any asynchronous subprocesses is the multiprocessing
module.

This makes it necessary to change one small thing in the D-Bus API,
since the subprocesses.call() function does not expose the raw wait(2)
status value.

DBUS-API (CheckerCompleted): Change the second value provided by this
                             D-Bus signal from the raw wait(2) status
                             to the actual terminating signal number.
mandos (subprocess_call_pipe): New function to be called by
                               multiprocessing.Process (starting a
                               separate process).
(Client.last_checker signal): New attribute for signal which
                              terminated last checker.  Like
                              last_checker_status, only not accessible
                              via D-Bus.
(Client.checker_callback): Take new "connection" argument and use it
                           to get returncode; set last_checker_signal.
                           Return False so gobject does not call this
                           callback again.
(Client.start_checker): Start checker using a multiprocessing.Process
                        instead of a subprocess.Popen.
(ClientDBus.checker_callback): Take new "connection" argument.        Call
                               Client.checker_callback early to have
                               it set last_checker_status and
                               last_checker_signal; use those.  Change
                               second value provided to D-Bus signal
                               CheckerCompleted to use
                               last_checker_signal if checker was
                               terminated by signal.
mandos-monitor: Update to reflect DBus API change.
(MandosClientWidget.checker_completed): Take "signal" instead of
                                        "condition" argument.  Use it
                                        accordingly.  Remove dead code
                                        (os.WCOREDUMP case).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*  -*- coding: utf-8 -*- */
 
1
/*  -*- coding: utf-8; mode: c; mode: orgtbl -*- */
2
2
/*
3
 
 * Passprompt - Read a password from the terminal and print it
 
3
 * Password-prompt - Read a password from the terminal and print it
4
4
 * 
5
 
 * Copyright © 2008,2009 Teddy Hogeborn
6
 
 * Copyright © 2008,2009 Björn Påhlsson
 
5
 * Copyright © 2008-2014 Teddy Hogeborn
 
6
 * Copyright © 2008-2014 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 <https://www.fukt.bsnet.se/~belorn/> and
23
 
 * <https://www.fukt.bsnet.se/~teddy/>.
 
22
 * Contact the authors at <mandos@recompile.se>.
24
23
 */
25
24
 
26
 
#define _GNU_SOURCE             /* getline() */
 
25
#define _GNU_SOURCE             /* getline(), asprintf() */
27
26
 
28
 
#include <termios.h>            /* struct termios, tcsetattr(),
 
27
#include <termios.h>            /* struct termios, tcsetattr(),
29
28
                                   TCSAFLUSH, tcgetattr(), ECHO */
30
29
#include <unistd.h>             /* struct termios, tcsetattr(),
31
30
                                   STDIN_FILENO, TCSAFLUSH,
32
 
                                   tcgetattr(), ECHO */
 
31
                                   tcgetattr(), ECHO, readlink() */
33
32
#include <signal.h>             /* sig_atomic_t, raise(), struct
34
33
                                   sigaction, sigemptyset(),
35
34
                                   sigaction(), sigaddset(), SIGINT,
36
 
                                   SIGQUIT, SIGHUP, SIGTERM */
 
35
                                   SIGQUIT, SIGHUP, SIGTERM,
 
36
                                   raise() */
37
37
#include <stddef.h>             /* NULL, size_t, ssize_t */
38
 
#include <sys/types.h>          /* ssize_t */
 
38
#include <sys/types.h>          /* ssize_t, struct dirent, pid_t,
 
39
                                   ssize_t, open() */
39
40
#include <stdlib.h>             /* EXIT_SUCCESS, EXIT_FAILURE,
40
 
                                   getopt_long, getenv() */
 
41
                                   getenv(), free() */
 
42
#include <dirent.h>             /* scandir(), alphasort() */
41
43
#include <stdio.h>              /* fprintf(), stderr, getline(),
42
 
                                   stdin, feof(), perror(), fputc(),
43
 
                                   stdout, getopt_long */
44
 
#include <errno.h>              /* errno, EINVAL */
 
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() */
45
50
#include <iso646.h>             /* or, not */
46
51
#include <stdbool.h>            /* bool, false, true */
47
 
#include <string.h>             /* strlen, rindex, strncmp, strcmp */
 
52
#include <inttypes.h>           /* strtoumax() */
 
53
#include <sys/stat.h>           /* struct stat, lstat(), open() */
 
54
#include <string.h>             /* strlen, rindex, memcmp, strerror()
 
55
                                 */
48
56
#include <argp.h>               /* struct argp_option, struct
49
57
                                   argp_state, struct argp,
50
58
                                   argp_parse(), error_t,
51
59
                                   ARGP_KEY_ARG, ARGP_KEY_END,
52
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(), ... */
53
65
 
54
 
volatile bool quit_now = false;
 
66
volatile sig_atomic_t quit_now = 0;
 
67
int signal_received;
55
68
bool debug = false;
56
69
const char *argp_program_version = "password-prompt " VERSION;
57
 
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
58
 
 
59
 
static void termination_handler(__attribute__((unused))int signum){
60
 
  quit_now = true;
61
 
}
 
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
 
62
222
 
63
223
int main(int argc, char **argv){
64
 
  ssize_t ret;
 
224
  ssize_t sret;
 
225
  int ret;
65
226
  size_t n;
66
227
  struct termios t_new, t_old;
67
228
  char *buffer = NULL;
77
238
        .doc = "Prefix shown before the prompt", .group = 2 },
78
239
      { .name = "debug", .key = 128,
79
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 },
80
250
      { .name = NULL }
81
251
    };
82
252
    
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) {
 
253
    __attribute__((nonnull(3)))
 
254
    error_t parse_opt (int key, char *arg, struct argp_state *state){
 
255
      errno = 0;
 
256
      switch (key){
87
257
      case 'p':
88
258
        prefix = arg;
89
259
        break;
90
260
      case 128:
91
261
        debug = true;
92
262
        break;
93
 
      case ARGP_KEY_ARG:
94
 
        argp_usage (state);
95
 
        break;
96
 
      case ARGP_KEY_END:
 
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);
97
276
        break;
98
277
      default:
99
278
        return ARGP_ERR_UNKNOWN;
100
279
      }
101
 
      return 0;
 
280
      return errno;
102
281
    }
103
282
    
104
283
    struct argp argp = { .options = options, .parser = parse_opt,
105
284
                         .args_doc = "",
106
285
                         .doc = "Mandos password-prompt -- Read and"
107
286
                         " output a password" };
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;
 
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;
112
299
    }
113
300
  }
114
301
  
115
 
  if (debug){
 
302
  if(debug){
116
303
    fprintf(stderr, "Starting %s\n", argv[0]);
117
304
  }
118
 
  if (debug){
 
305
 
 
306
  if(conflict_detection()){
 
307
    if(debug){
 
308
      fprintf(stderr, "Stopping %s because of conflict\n", argv[0]);
 
309
    }
 
310
    return EXIT_FAILURE;
 
311
  }
 
312
  
 
313
  if(debug){
119
314
    fprintf(stderr, "Storing current terminal attributes\n");
120
315
  }
121
316
  
122
 
  if (tcgetattr(STDIN_FILENO, &t_old) != 0){
123
 
    perror("tcgetattr");
124
 
    return EXIT_FAILURE;
 
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
    }
125
327
  }
126
328
  
127
329
  sigemptyset(&new_action.sa_mask);
128
 
  sigaddset(&new_action.sa_mask, SIGINT);
129
 
  sigaddset(&new_action.sa_mask, SIGHUP);
130
 
  sigaddset(&new_action.sa_mask, SIGTERM);
 
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
  */
131
349
  ret = sigaction(SIGINT, NULL, &old_action);
132
350
  if(ret == -1){
133
 
    perror("sigaction");
134
 
    return EXIT_FAILURE;
 
351
    error_plus(0, errno, "sigaction");
 
352
    return EX_OSERR;
135
353
  }
136
 
  if (old_action.sa_handler != SIG_IGN){
 
354
  if(old_action.sa_handler != SIG_IGN){
137
355
    ret = sigaction(SIGINT, &new_action, NULL);
138
356
    if(ret == -1){
139
 
      perror("sigaction");
140
 
      return EXIT_FAILURE;
 
357
      error_plus(0, errno, "sigaction");
 
358
      return EX_OSERR;
141
359
    }
142
360
  }
143
361
  ret = sigaction(SIGHUP, NULL, &old_action);
144
362
  if(ret == -1){
145
 
    perror("sigaction");
146
 
    return EXIT_FAILURE;
 
363
    error_plus(0, errno, "sigaction");
 
364
    return EX_OSERR;
147
365
  }
148
 
  if (old_action.sa_handler != SIG_IGN){
 
366
  if(old_action.sa_handler != SIG_IGN){
149
367
    ret = sigaction(SIGHUP, &new_action, NULL);
150
368
    if(ret == -1){
151
 
      perror("sigaction");
152
 
      return EXIT_FAILURE;
 
369
      error_plus(0, errno, "sigaction");
 
370
      return EX_OSERR;
153
371
    }
154
372
  }
155
373
  ret = sigaction(SIGTERM, NULL, &old_action);
156
374
  if(ret == -1){
157
 
    perror("sigaction");
158
 
    return EXIT_FAILURE;
 
375
    error_plus(0, errno, "sigaction");
 
376
    return EX_OSERR;
159
377
  }
160
 
  if (old_action.sa_handler != SIG_IGN){
 
378
  if(old_action.sa_handler != SIG_IGN){
161
379
    ret = sigaction(SIGTERM, &new_action, NULL);
162
380
    if(ret == -1){
163
 
      perror("sigaction");
164
 
      return EXIT_FAILURE;
 
381
      error_plus(0, errno, "sigaction");
 
382
      return EX_OSERR;
165
383
    }
166
384
  }
167
385
  
168
386
  
169
 
  if (debug){
 
387
  if(debug){
170
388
    fprintf(stderr, "Removing echo flag from terminal attributes\n");
171
389
  }
172
390
  
173
391
  t_new = t_old;
174
 
  t_new.c_lflag &= ~ECHO;
175
 
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
176
 
    perror("tcsetattr-echo");
177
 
    return EXIT_FAILURE;
 
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
    }
178
404
  }
179
 
 
180
 
  if (debug){
 
405
  
 
406
  if(debug){
181
407
    fprintf(stderr, "Waiting for input from stdin \n");
182
408
  }
183
409
  while(true){
184
 
    if (quit_now){
 
410
    if(quit_now){
185
411
      if(debug){
186
412
        fprintf(stderr, "Interrupted by signal, exiting.\n");
187
413
      }
193
419
      fprintf(stderr, "%s ", prefix);
194
420
    }
195
421
    {
196
 
      const char *cryptsource = getenv("cryptsource");
197
 
      const char *crypttarget = getenv("crypttarget");
198
 
      const char *const prompt
199
 
        = "Enter passphrase to unlock the disk";
 
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";
200
433
      if(cryptsource == NULL){
201
434
        if(crypttarget == NULL){
202
 
          fprintf(stderr, "%s: ", prompt);
 
435
          fprintf(stderr, "%s to unlock the disk: ", prompt2);
203
436
        } else {
204
 
          fprintf(stderr, "%s (%s): ", prompt, crypttarget);
 
437
          fprintf(stderr, "%s (%s)\n%s: ", prompt1, crypttarget,
 
438
                  prompt2);
205
439
        }
206
440
      } else {
207
441
        if(crypttarget == NULL){
208
 
          fprintf(stderr, "%s %s: ", prompt, cryptsource);
 
442
          fprintf(stderr, "%s %s\n%s: ", prompt1, cryptsource,
 
443
                  prompt2);
209
444
        } else {
210
 
          fprintf(stderr, "%s %s (%s): ", prompt, cryptsource,
211
 
                  crypttarget);
 
445
          fprintf(stderr, "%s %s (%s)\n%s: ", prompt1, cryptsource,
 
446
                  crypttarget, prompt2);
212
447
        }
213
448
      }
214
449
    }
215
 
    ret = getline(&buffer, &n, stdin);
216
 
    if (ret > 0){
 
450
    sret = getline(&buffer, &n, stdin);
 
451
    if(sret > 0){
217
452
      status = EXIT_SUCCESS;
218
453
      /* Make n = data size instead of allocated buffer size */
219
 
      n = (size_t)ret;
 
454
      n = (size_t)sret;
220
455
      /* Strip final newline */
221
 
      if(n>0 and buffer[n-1] == '\n'){
 
456
      if(n > 0 and buffer[n-1] == '\n'){
222
457
        buffer[n-1] = '\0';     /* not strictly necessary */
223
458
        n--;
224
459
      }
225
460
      size_t written = 0;
226
461
      while(written < n){
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;
 
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
        }
234
497
      }
235
498
      break;
236
499
    }
237
 
    if (ret < 0){
238
 
      if (errno != EINTR and not feof(stdin)){
239
 
        perror("getline");
240
 
        status = EXIT_FAILURE;
 
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
        }
241
514
        break;
242
515
      }
243
516
    }
244
 
    /* if(ret == 0), then the only sensible thing to do is to retry to
245
 
       read from stdin */
 
517
    /* if(sret == 0), then the only sensible thing to do is to retry
 
518
       to read from stdin */
246
519
    fputc('\n', stderr);
247
520
    if(debug and not quit_now){
248
 
      /* If quit_now is true, we were interrupted by a signal, and
 
521
      /* If quit_now is nonzero, we were interrupted by a signal, and
249
522
         will print that later, so no need to show this too. */
250
523
      fprintf(stderr, "getline() returned 0, retrying.\n");
251
524
    }
253
526
  
254
527
  free(buffer);
255
528
  
256
 
  if (debug){
 
529
  if(debug){
257
530
    fprintf(stderr, "Restoring terminal attributes\n");
258
531
  }
259
 
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
260
 
    perror("tcsetattr+echo");
261
 
  }
262
 
  
263
 
  if (debug){
 
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){
264
547
    fprintf(stderr, "%s is exiting with status %d\n", argv[0],
265
548
            status);
266
549
  }
267
 
  if(status == EXIT_SUCCESS){
 
550
  if(status == EXIT_SUCCESS or status == EX_OK){
268
551
    fputc('\n', stderr);
269
552
  }
270
553