/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/passprompt.c

  • Committer: Teddy Hogeborn
  • Date: 2008-08-02 10:48:24 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080802104824-fx0miwp9o4g9r31e
* plugbasedclient.c (struct process): New fields "eof", "completed",
                                      and "status".
  (handle_sigchld): New function.
  (main): Initialize "dir" to NULL to only closedir() it if necessary.
          Move "process_list" to be a global variable to be accessible
          by "handle_sigchld".  Make "handle_sigchld" handle SIGCHLD.
          Remove redundant check for NULL "dir".  Free "filename" when
          no longer used.  Block SIGCHLD around fork()/exec().
          Restore normal signals in child.  Only loop while running
          processes exist.  Print process buffer when the process is
          done and it has emitted EOF, not when it only emits EOF.
          Remove processes from list which exit non-cleanly.  In
          cleaning up, closedir() if necessary.  Bug fix: set next
          pointer correctly when freeing process list.

* plugins.d/passprompt.c (main): Do not ignore SIGQUIT.

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() */
38
 
#include <stddef.h>             /* NULL, size_t, ssize_t */
39
 
#include <sys/types.h>          /* ssize_t, struct dirent, pid_t,
40
 
                                   ssize_t, open() */
 
35
                                   SIGQUIT, SIGHUP, SIGTERM */
 
36
#include <stddef.h>             /* NULL, size_t */
 
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
 
                                 */
57
 
#include <argp.h>               /* struct argp_option, struct
58
 
                                   argp_state, struct argp,
59
 
                                   argp_parse(), error_t,
60
 
                                   ARGP_KEY_ARG, ARGP_KEY_END,
61
 
                                   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(), ... */
 
46
#include <string.h>             /* strlen, rindex, strncmp, strcmp */
 
47
#include <getopt.h>             /* getopt_long */
66
48
 
67
 
volatile sig_atomic_t quit_now = 0;
68
 
int signal_received;
 
49
volatile bool quit_now = false;
69
50
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
 
 
 
51
 
 
52
void termination_handler(__attribute__((unused))int signum){
 
53
  quit_now = true;
 
54
}
229
55
 
230
56
int main(int argc, char **argv){
231
 
  ssize_t sret;
232
 
  int ret;
 
57
  ssize_t ret;
233
58
  size_t n;
234
59
  struct termios t_new, t_old;
235
60
  char *buffer = NULL;
238
63
  struct sigaction old_action,
239
64
    new_action = { .sa_handler = termination_handler,
240
65
                   .sa_flags = 0 };
241
 
  {
242
 
    struct argp_option options[] = {
243
 
      { .name = "prefix", .key = 'p',
244
 
        .arg = "PREFIX", .flags = 0,
245
 
        .doc = "Prefix shown before the prompt", .group = 2 },
246
 
      { .name = "debug", .key = 128,
247
 
        .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
 
      { .name = NULL }
258
 
    };
259
 
    
260
 
    __attribute__((nonnull(3)))
261
 
    error_t parse_opt (int key, char *arg, struct argp_state *state){
262
 
      errno = 0;
263
 
      switch (key){
264
 
      case 'p':
265
 
        prefix = arg;
266
 
        break;
267
 
      case 128:
268
 
        debug = true;
269
 
        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);
283
 
        break;
284
 
      default:
285
 
        return ARGP_ERR_UNKNOWN;
286
 
      }
287
 
      return errno;
 
66
 
 
67
  while (true){
 
68
    static struct option long_options[] = {
 
69
      {"debug", no_argument, (int *)&debug, 1},
 
70
      {"prefix", required_argument, 0, 'p'},
 
71
      {0, 0, 0, 0} };
 
72
 
 
73
    int option_index = 0;
 
74
    ret = getopt_long (argc, argv, "p:", long_options, &option_index);
 
75
 
 
76
    if (ret == -1){
 
77
      break;
288
78
    }
289
 
    
290
 
    struct argp argp = { .options = options, .parser = parse_opt,
291
 
                         .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);
 
79
      
296
80
    switch(ret){
297
81
    case 0:
298
82
      break;
299
 
    case ENOMEM:
 
83
    case 'p':
 
84
      prefix = optarg;
 
85
      break;
300
86
    default:
301
 
      errno = ret;
302
 
      error_plus(0, errno, "argp_parse");
303
 
      return EX_OSERR;
304
 
    case EINVAL:
305
 
      return EX_USAGE;
 
87
      fprintf(stderr, "bad arguments\n");
 
88
      exit(EXIT_FAILURE);
306
89
    }
307
90
  }
308
 
  
309
 
  if(debug){
 
91
      
 
92
  if (debug){
310
93
    fprintf(stderr, "Starting %s\n", argv[0]);
311
94
  }
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){
 
95
  if (debug){
321
96
    fprintf(stderr, "Storing current terminal attributes\n");
322
97
  }
323
98
  
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
 
    }
 
99
  if (tcgetattr(STDIN_FILENO, &t_old) != 0){
 
100
    return EXIT_FAILURE;
334
101
  }
335
102
  
336
103
  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
 
  */
356
 
  ret = sigaction(SIGINT, NULL, &old_action);
357
 
  if(ret == -1){
358
 
    error_plus(0, errno, "sigaction");
359
 
    return EX_OSERR;
360
 
  }
361
 
  if(old_action.sa_handler != SIG_IGN){
362
 
    ret = sigaction(SIGINT, &new_action, NULL);
363
 
    if(ret == -1){
364
 
      error_plus(0, errno, "sigaction");
365
 
      return EX_OSERR;
366
 
    }
367
 
  }
368
 
  ret = sigaction(SIGHUP, NULL, &old_action);
369
 
  if(ret == -1){
370
 
    error_plus(0, errno, "sigaction");
371
 
    return EX_OSERR;
372
 
  }
373
 
  if(old_action.sa_handler != SIG_IGN){
374
 
    ret = sigaction(SIGHUP, &new_action, NULL);
375
 
    if(ret == -1){
376
 
      error_plus(0, errno, "sigaction");
377
 
      return EX_OSERR;
378
 
    }
379
 
  }
380
 
  ret = sigaction(SIGTERM, NULL, &old_action);
381
 
  if(ret == -1){
382
 
    error_plus(0, errno, "sigaction");
383
 
    return EX_OSERR;
384
 
  }
385
 
  if(old_action.sa_handler != SIG_IGN){
386
 
    ret = sigaction(SIGTERM, &new_action, NULL);
387
 
    if(ret == -1){
388
 
      error_plus(0, errno, "sigaction");
389
 
      return EX_OSERR;
390
 
    }
391
 
  }
392
 
  
393
 
  
394
 
  if(debug){
 
104
  sigaddset(&new_action.sa_mask, SIGINT);
 
105
  sigaddset(&new_action.sa_mask, SIGHUP);
 
106
  sigaddset(&new_action.sa_mask, SIGTERM);
 
107
  sigaction(SIGINT, NULL, &old_action);
 
108
  if (old_action.sa_handler != SIG_IGN)
 
109
    sigaction(SIGINT, &new_action, NULL);
 
110
  sigaction(SIGHUP, NULL, &old_action);
 
111
  if (old_action.sa_handler != SIG_IGN)
 
112
    sigaction(SIGHUP, &new_action, NULL);
 
113
  sigaction(SIGTERM, NULL, &old_action);
 
114
  if (old_action.sa_handler != SIG_IGN)
 
115
    sigaction(SIGTERM, &new_action, NULL);
 
116
 
 
117
  
 
118
  if (debug){
395
119
    fprintf(stderr, "Removing echo flag from terminal attributes\n");
396
120
  }
397
121
  
398
122
  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
 
    }
 
123
  t_new.c_lflag &= ~ECHO;
 
124
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
 
125
    perror("tcsetattr-echo");
 
126
    return EXIT_FAILURE;
411
127
  }
412
 
  
413
 
  if(debug){
 
128
 
 
129
  if (debug){
414
130
    fprintf(stderr, "Waiting for input from stdin \n");
415
131
  }
416
132
  while(true){
417
 
    if(quit_now){
418
 
      if(debug){
419
 
        fprintf(stderr, "Interrupted by signal, exiting.\n");
420
 
      }
 
133
    if (quit_now){
421
134
      status = EXIT_FAILURE;
422
135
      break;
423
136
    }
424
137
 
425
138
    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){
 
139
      fprintf(stderr, "%s Password: ", prefix);
 
140
    } else {
 
141
      fprintf(stderr, "Password: ");
 
142
    }      
 
143
    ret = getline(&buffer, &n, stdin);
 
144
    if (ret > 0){
 
145
      fprintf(stdout, "%s", buffer);
459
146
      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
147
      break;
506
148
    }
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
 
        }
 
149
    // ret == 0 makes no other sence than to retry to read from stdin
 
150
    if (ret < 0){
 
151
      if (errno != EINTR and not feof(stdin)){
 
152
        perror("getline");
 
153
        status = EXIT_FAILURE;
521
154
        break;
522
155
      }
523
156
    }
524
 
    /* if(sret == 0), then the only sensible thing to do is to retry
525
 
       to read from stdin */
526
157
    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
158
  }
533
 
  
534
 
  free(buffer);
535
 
  
536
 
  if(debug){
 
159
 
 
160
  if (debug){
537
161
    fprintf(stderr, "Restoring terminal attributes\n");
538
162
  }
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);
 
163
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
 
164
    perror("tcsetattr+echo");
 
165
  }
 
166
 
 
167
  if (debug){
 
168
    fprintf(stderr, "%s is exiting\n", argv[0]);
559
169
  }
560
170
  
561
171
  return status;