/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-2018 Teddy Hogeborn
6
 
 * Copyright © 2008-2018 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
 
      case -3:                  /* --usage */
278
 
        argp_state_help(state, state->out_stream,
279
 
                        ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
280
 
      case 'V':                 /* --version */
281
 
        fprintf(state->out_stream, "%s\n", argp_program_version);
282
 
        exit(argp_err_exit_status);
 
92
      case ARGP_KEY_ARG:
 
93
        argp_usage (state);
 
94
        break;
 
95
      case ARGP_KEY_END:
283
96
        break;
284
97
      default:
285
98
        return ARGP_ERR_UNKNOWN;
286
99
      }
287
 
      return errno;
 
100
      return 0;
288
101
    }
289
 
    
 
102
  
290
103
    struct argp argp = { .options = options, .parser = parse_opt,
291
104
                         .args_doc = "",
292
 
                         .doc = "Mandos password-prompt -- Read and"
293
 
                         " output a password" };
294
 
    ret = argp_parse(&argp, argc, argv,
295
 
                     ARGP_IN_ORDER | ARGP_NO_HELP, NULL, NULL);
296
 
    switch(ret){
297
 
    case 0:
298
 
      break;
299
 
    case ENOMEM:
300
 
    default:
301
 
      errno = ret;
302
 
      error_plus(0, errno, "argp_parse");
303
 
      return EX_OSERR;
304
 
    case EINVAL:
305
 
      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;
306
110
    }
307
111
  }
308
 
  
309
 
  if(debug){
 
112
    
 
113
  if (debug){
310
114
    fprintf(stderr, "Starting %s\n", argv[0]);
311
115
  }
312
 
 
313
 
  if(conflict_detection()){
314
 
    if(debug){
315
 
      fprintf(stderr, "Stopping %s because of conflict\n", argv[0]);
316
 
    }
317
 
    return EXIT_FAILURE;
318
 
  }
319
 
  
320
 
  if(debug){
 
116
  if (debug){
321
117
    fprintf(stderr, "Storing current terminal attributes\n");
322
118
  }
323
119
  
324
 
  if(tcgetattr(STDIN_FILENO, &t_old) != 0){
325
 
    int e = errno;
326
 
    error_plus(0, errno, "tcgetattr");
327
 
    switch(e){
328
 
    case EBADF:
329
 
    case ENOTTY:
330
 
      return EX_UNAVAILABLE;
331
 
    default:
332
 
      return EX_OSERR;
333
 
    }
 
120
  if (tcgetattr(STDIN_FILENO, &t_old) != 0){
 
121
    return EXIT_FAILURE;
334
122
  }
335
123
  
336
124
  sigemptyset(&new_action.sa_mask);
337
 
  ret = sigaddset(&new_action.sa_mask, SIGINT);
338
 
  if(ret == -1){
339
 
    error_plus(0, errno, "sigaddset");
340
 
    return EX_OSERR;
341
 
  }
342
 
  ret = sigaddset(&new_action.sa_mask, SIGHUP);
343
 
  if(ret == -1){
344
 
    error_plus(0, errno, "sigaddset");
345
 
    return EX_OSERR;
346
 
  }
347
 
  ret = sigaddset(&new_action.sa_mask, SIGTERM);
348
 
  if(ret == -1){
349
 
    error_plus(0, errno, "sigaddset");
350
 
    return EX_OSERR;
351
 
  }
352
 
  /* Need to check if the handler is SIG_IGN before handling:
353
 
     | [[info:libc:Initial Signal Actions]] |
354
 
     | [[info:libc:Basic Signal Handling]]  |
355
 
  */
 
125
  sigaddset(&new_action.sa_mask, SIGINT);
 
126
  sigaddset(&new_action.sa_mask, SIGHUP);
 
127
  sigaddset(&new_action.sa_mask, SIGTERM);
356
128
  ret = sigaction(SIGINT, NULL, &old_action);
357
129
  if(ret == -1){
358
 
    error_plus(0, errno, "sigaction");
359
 
    return EX_OSERR;
 
130
    perror("sigaction");
 
131
    return EXIT_FAILURE;
360
132
  }
361
 
  if(old_action.sa_handler != SIG_IGN){
 
133
  if (old_action.sa_handler != SIG_IGN){
362
134
    ret = sigaction(SIGINT, &new_action, NULL);
363
135
    if(ret == -1){
364
 
      error_plus(0, errno, "sigaction");
365
 
      return EX_OSERR;
 
136
      perror("sigaction");
 
137
      return EXIT_FAILURE;
366
138
    }
367
139
  }
368
140
  ret = sigaction(SIGHUP, NULL, &old_action);
369
141
  if(ret == -1){
370
 
    error_plus(0, errno, "sigaction");
371
 
    return EX_OSERR;
 
142
    perror("sigaction");
 
143
    return EXIT_FAILURE;
372
144
  }
373
 
  if(old_action.sa_handler != SIG_IGN){
 
145
  if (old_action.sa_handler != SIG_IGN){
374
146
    ret = sigaction(SIGHUP, &new_action, NULL);
375
147
    if(ret == -1){
376
 
      error_plus(0, errno, "sigaction");
377
 
      return EX_OSERR;
 
148
      perror("sigaction");
 
149
      return EXIT_FAILURE;
378
150
    }
379
151
  }
380
152
  ret = sigaction(SIGTERM, NULL, &old_action);
381
153
  if(ret == -1){
382
 
    error_plus(0, errno, "sigaction");
383
 
    return EX_OSERR;
 
154
    perror("sigaction");
 
155
    return EXIT_FAILURE;
384
156
  }
385
 
  if(old_action.sa_handler != SIG_IGN){
 
157
  if (old_action.sa_handler != SIG_IGN){
386
158
    ret = sigaction(SIGTERM, &new_action, NULL);
387
159
    if(ret == -1){
388
 
      error_plus(0, errno, "sigaction");
389
 
      return EX_OSERR;
 
160
      perror("sigaction");
 
161
      return EXIT_FAILURE;
390
162
    }
391
163
  }
392
164
  
393
165
  
394
 
  if(debug){
 
166
  if (debug){
395
167
    fprintf(stderr, "Removing echo flag from terminal attributes\n");
396
168
  }
397
169
  
398
170
  t_new = t_old;
399
 
  t_new.c_lflag &= ~(tcflag_t)ECHO;
400
 
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
401
 
    int e = errno;
402
 
    error_plus(0, errno, "tcsetattr-echo");
403
 
    switch(e){
404
 
    case EBADF:
405
 
    case ENOTTY:
406
 
      return EX_UNAVAILABLE;
407
 
    case EINVAL:
408
 
    default:
409
 
      return EX_OSERR;
410
 
    }
 
171
  t_new.c_lflag &= ~ECHO;
 
172
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
 
173
    perror("tcsetattr-echo");
 
174
    return EXIT_FAILURE;
411
175
  }
412
 
  
413
 
  if(debug){
 
176
 
 
177
  if (debug){
414
178
    fprintf(stderr, "Waiting for input from stdin \n");
415
179
  }
416
180
  while(true){
417
 
    if(quit_now){
418
 
      if(debug){
419
 
        fprintf(stderr, "Interrupted by signal, exiting.\n");
420
 
      }
 
181
    if (quit_now){
421
182
      status = EXIT_FAILURE;
422
183
      break;
423
184
    }
424
185
 
425
186
    if(prefix){
426
 
      fprintf(stderr, "%s ", prefix);
427
 
    }
428
 
    {
429
 
      const char *cryptsource = getenv("CRYPTTAB_SOURCE");
430
 
      const char *crypttarget = getenv("CRYPTTAB_NAME");
431
 
      /* Before cryptsetup 1.1.0~rc2 */
432
 
      if(cryptsource == NULL){
433
 
        cryptsource = getenv("cryptsource");
434
 
      }
435
 
      if(crypttarget == NULL){
436
 
        crypttarget = getenv("crypttarget");
437
 
      }
438
 
      const char *const prompt1 = "Unlocking the disk";
439
 
      const char *const prompt2 = "Enter passphrase";
440
 
      if(cryptsource == NULL){
441
 
        if(crypttarget == NULL){
442
 
          fprintf(stderr, "%s to unlock the disk: ", prompt2);
443
 
        } else {
444
 
          fprintf(stderr, "%s (%s)\n%s: ", prompt1, crypttarget,
445
 
                  prompt2);
446
 
        }
447
 
      } else {
448
 
        if(crypttarget == NULL){
449
 
          fprintf(stderr, "%s %s\n%s: ", prompt1, cryptsource,
450
 
                  prompt2);
451
 
        } else {
452
 
          fprintf(stderr, "%s %s (%s)\n%s: ", prompt1, cryptsource,
453
 
                  crypttarget, prompt2);
454
 
        }
455
 
      }
456
 
    }
457
 
    sret = getline(&buffer, &n, stdin);
458
 
    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);
459
194
      status = EXIT_SUCCESS;
460
 
      /* Make n = data size instead of allocated buffer size */
461
 
      n = (size_t)sret;
462
 
      /* Strip final newline */
463
 
      if(n > 0 and buffer[n-1] == '\n'){
464
 
        buffer[n-1] = '\0';     /* not strictly necessary */
465
 
        n--;
466
 
      }
467
 
      size_t written = 0;
468
 
      while(written < n){
469
 
        sret = write(STDOUT_FILENO, buffer + written, n - written);
470
 
        if(sret < 0){
471
 
          int e = errno;
472
 
          error_plus(0, errno, "write");
473
 
          switch(e){
474
 
          case EBADF:
475
 
          case EFAULT:
476
 
          case EINVAL:
477
 
          case EFBIG:
478
 
          case EIO:
479
 
          case ENOSPC:
480
 
          default:
481
 
            status = EX_IOERR;
482
 
            break;
483
 
          case EINTR:
484
 
            status = EXIT_FAILURE;
485
 
            break;
486
 
          }
487
 
          break;
488
 
        }
489
 
        written += (size_t)sret;
490
 
      }
491
 
      sret = close(STDOUT_FILENO);
492
 
      if(sret == -1){
493
 
        int e = errno;
494
 
        error_plus(0, errno, "close");
495
 
        switch(e){
496
 
        case EBADF:
497
 
          status = EX_OSFILE;
498
 
          break;
499
 
        case EIO:
500
 
        default:
501
 
          status = EX_IOERR;
502
 
          break;
503
 
        }
504
 
      }
505
195
      break;
506
196
    }
507
 
    if(sret < 0){
508
 
      int e = errno;
509
 
      if(errno != EINTR and not feof(stdin)){
510
 
        error_plus(0, errno, "getline");
511
 
        switch(e){
512
 
        case EBADF:
513
 
          status = EX_UNAVAILABLE;
514
 
          break;
515
 
        case EIO:
516
 
        case EINVAL:
517
 
        default:
518
 
          status = EX_IOERR;
519
 
          break;
520
 
        }
 
197
    if (ret < 0){
 
198
      if (errno != EINTR and not feof(stdin)){
 
199
        perror("getline");
 
200
        status = EXIT_FAILURE;
521
201
        break;
522
202
      }
523
203
    }
524
 
    /* if(sret == 0), then the only sensible thing to do is to retry
525
 
       to read from stdin */
 
204
    /* if(ret == 0), then the only sensible thing to do is to retry to
 
205
       read from stdin */
526
206
    fputc('\n', stderr);
527
 
    if(debug and not quit_now){
528
 
      /* If quit_now is nonzero, we were interrupted by a signal, and
529
 
         will print that later, so no need to show this too. */
530
 
      fprintf(stderr, "getline() returned 0, retrying.\n");
531
 
    }
532
207
  }
533
208
  
534
 
  free(buffer);
535
 
  
536
 
  if(debug){
 
209
  if (debug){
537
210
    fprintf(stderr, "Restoring terminal attributes\n");
538
211
  }
539
 
  if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
540
 
    error_plus(0, errno, "tcsetattr+echo");
541
 
  }
542
 
  
543
 
  if(quit_now){
544
 
    sigemptyset(&old_action.sa_mask);
545
 
    old_action.sa_handler = SIG_DFL;
546
 
    ret = sigaction(signal_received, &old_action, NULL);
547
 
    if(ret == -1){
548
 
      error_plus(0, errno, "sigaction");
549
 
    }
550
 
    raise(signal_received);
551
 
  }
552
 
  
553
 
  if(debug){
554
 
    fprintf(stderr, "%s is exiting with status %d\n", argv[0],
555
 
            status);
556
 
  }
557
 
  if(status == EXIT_SUCCESS or status == EX_OK){
558
 
    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]);
559
218
  }
560
219
  
561
220
  return status;