/mandos/release

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/release

« back to all changes in this revision

Viewing changes to plugins.d/password-prompt.c

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