/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-12-10 01:26:02 UTC
  • mfrom: (237.1.2 mandos)
  • Revision ID: teddy@fukt.bsnet.se-20081210012602-vhz3h75xkj24t340
First version of a somewhat complete D-Bus server interface.  Also
change user/group name to "_mandos".

* debian/mandos.postinst: Rename old "mandos" user and group to
                          "_mandos"; create "_mandos" user and group
                          if none exist.
* debian/mandos-client.postinst: - '' -

* initramfs-tools-hook: Try "_mandos" before "mandos" as user and
                        group name.

* mandos (_datetime_to_dbus_struct): New; was previously local.
  (Client.started): Renamed to "last_started".  All users changed.
  (Client.started): New; boolean.
  (Client.dbus_object_path): New.
  (Client.check_command): Renamed to "checker_command".  All users
                          changed.
  (Client.__init__): Set and use "self.dbus_object_path".  Set
                     "self.started".
  (Client.start): Update "self.started".  Emit "self.PropertyChanged"
                  signals for both "started" and "last_started".
  (Client.stop): Update "self.started".  Emit "self.PropertyChanged"
                 signal for "started".
  (Client.checker_callback): Take additional "command" argument.  All
                             callers changed. Emit
                             "self.PropertyChanged" signal.
  (Client.bump_timeout): Emit "self.PropertyChanged" signal for
                         "last_checked_ok".
  (Client.start_checker): Emit "self.PropertyChanged" signal for
                          "checker_running".
  (Client.stop_checker): Emit "self.PropertyChanged" signal for
                         "checker_running".
  (Client.still_valid): Bug fix: use "getattr(self, started, False)"
                        instead of "self.started" in case this client
                        object is so new that the "started" attribute
                        has not been created yet.
  (Client.IntervalChanged, Client.CheckerIsRunning, Client.GetChecker,
  Client.GetCreated, Client.GetFingerprint, Client.GetHost,
  Client.GetInterval, Client.GetName, Client.GetStarted,
  Client.GetTimeout, Client.StateChanged, Client.TimeoutChanged):
  Removed; all callers changed.
  (Client.CheckerCompleted): Add "condition" and "command" arguments.
                             All callers changed.
  (Client.GetAllProperties, Client.PropertyChanged): New.
  (Client.StillValid): Renamed to "IsStillValid".
  (Client.StartChecker): Changed to its own function to avoid the
                         return value from "Client.start_checker()".
  (Client.Stop): Changed to its own function to avoid the return value
                 from "Client.stop()".
  (main): Try "_mandos" before "mandos" as user and group name.
          Removed inner function "remove_from_clients".  New inner
          class "MandosServer".

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