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