/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-08-29 05:53:59 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080829055359-wkdasnyxtylmnxus
* mandos.xml (EXAMPLE): Replaced all occurences of command name with
                        "&COMMANDNAME;".

* plugins.d/password-prompt.c (main): Improved some documentation
                                      strings.  Do perror() of
                                      tcgetattr() fails.  Add debug
                                      output if interrupted by signal.
                                      Loop over write() instead of
                                      using fwrite() when outputting
                                      password.  Add debug output if
                                      getline() returns 0, unless it
                                      was caused by a signal.  Add
                                      exit status code to debug
                                      output.

* plugins.d/password-prompt.xml: Changed all single quotes to double
                                 quotes for consistency.  Removed
                                 <?xml-stylesheet>.
  (ENTITY TIMESTAMP): New.  Automatically updated by Emacs time-stamp
                      by using Emacs local variables.
  (/refentry/refentryinfo/title): Changed to "Mandos Manual".
  (/refentry/refentryinfo/productname): Changed to "Mandos".
  (/refentry/refentryinfo/date): New; set to "&TIMESTAMP;".
  (/refentry/refentryinfo/copyright): Split copyright holders.
  (/refentry/refnamediv/refpurpose): Improved wording.
  (SYNOPSIS): Fix to use correct markup.  Add short options.
  (DESCRIPTION, OPTIONS): Improved wording.
  (OPTIONS): Improved wording.  Use more correct markup.  Document
             short options.
  (EXIT STATUS): Add text.
  (ENVIRONMENT): Document use of "cryptsource" and "crypttarget".
  (FILES): REMOVED.
  (BUGS): Add text.
  (EXAMPLE): Added some examples.
  (SECURITY): Added text.
  (SEE ALSO): Remove reference to mandos(8).  Add reference to
              crypttab(5).

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