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