/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-16 03:29:08 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080816032908-ihw7c05r2mnyk389
Add feature to specify custom environment variables for plugins.

* plugin-runner.c (plugin): New members "environ" and "envc" to
                            contain possible custom environment.
  (getplugin): Return NULL on failure instead of doing exit(); all
               callers changed.
  (add_to_char_array): New helper function for "add_argument" and
                       "add_environment".
  (addargument): Renamed to "add_argument".  Return bool.  Call
                 "add_to_char_array" to actually do things.
  (add_environment): New; analogous to "add_argument".
  (addcustomargument): Renamed to "add_to_argv" to avoid confusion
                       with "add_argument".
  (main): New options "--global-envs" and "--envs-for" to specify
          custom environment for plugins.  Print environment for
          plugins in debug mode.  Use asprintf instead of strcpy and
          strcat.  Use execve() for plugins with custom environments.
          Free environment for plugin when freeing plugin list.

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