/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 plugin-runner.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:
2
2
/*
3
3
 * Mandos plugin runner - Run Mandos plugins
4
4
 *
5
 
 * Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
 
5
 * Copyright © 2008 Teddy Hogeborn
 
6
 * Copyright © 2008 Björn Påhlsson
6
7
 * 
7
8
 * This program is free software: you can redistribute it and/or
8
9
 * modify it under the terms of the GNU General Public License as
27
28
#include <stdlib.h>             /* malloc(), exit(), EXIT_FAILURE,
28
29
                                   EXIT_SUCCESS, realloc() */
29
30
#include <stdbool.h>            /* bool, true, false */
30
 
#include <stdio.h>              /* perror, popen(), fileno(),
31
 
                                   fprintf(), stderr, STDOUT_FILENO */
 
31
#include <stdio.h>              /* perror, fileno(), fprintf(),
 
32
                                   stderr, STDOUT_FILENO */
32
33
#include <sys/types.h>          /* DIR, opendir(), stat(), struct
33
34
                                   stat, waitpid(), WIFEXITED(),
34
35
                                   WEXITSTATUS(), wait(), pid_t,
46
47
                                   fcntl(), setuid(), setgid(),
47
48
                                   F_GETFD, F_SETFD, FD_CLOEXEC,
48
49
                                   access(), pipe(), fork(), close()
49
 
                                   dup2, STDOUT_FILENO, _exit(),
 
50
                                   dup2(), STDOUT_FILENO, _exit(),
50
51
                                   execv(), write(), read(),
51
52
                                   close() */
52
53
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
69
70
#define PDIR "/lib/mandos/plugins.d"
70
71
#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
71
72
 
72
 
const char *argp_program_version = "plugin-runner 1.0";
 
73
const char *argp_program_version = "plugin-runner " VERSION;
73
74
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
74
75
 
75
 
struct plugin;
76
 
 
77
76
typedef struct plugin{
78
77
  char *name;                   /* can be NULL or any plugin name */
79
78
  char **argv;
96
95
 
97
96
static plugin *plugin_list = NULL;
98
97
 
99
 
/* Gets a existing plugin based on name,
 
98
/* Gets an existing plugin based on name,
100
99
   or if none is found, creates a new one */
101
100
static plugin *getplugin(char *name){
102
101
  /* Check for exiting plugin with that name */
118
117
      return NULL;
119
118
    }
120
119
  }
121
 
 
 
120
  
122
121
  *new_plugin = (plugin) { .name = copy_name,
123
122
                           .argc = 1,
124
123
                           .disabled = false,
132
131
  }
133
132
  new_plugin->argv[0] = copy_name;
134
133
  new_plugin->argv[1] = NULL;
135
 
 
 
134
  
136
135
  new_plugin->environ = malloc(sizeof(char *));
137
136
  if(new_plugin->environ == NULL){
138
137
    free(copy_name);
141
140
    return NULL;
142
141
  }
143
142
  new_plugin->environ[0] = NULL;
144
 
 
 
143
  
145
144
  /* Append the new plugin to the list */
146
145
  plugin_list = new_plugin;
147
146
  return new_plugin;
179
178
}
180
179
 
181
180
/* Add to a plugin's environment */
182
 
static bool add_environment(plugin *p, const char *def){
 
181
static bool add_environment(plugin *p, const char *def, bool replace){
183
182
  if(p == NULL){
184
183
    return false;
185
184
  }
 
185
  /* namelen = length of name of environment variable */
 
186
  size_t namelen = (size_t)(strchrnul(def, '=') - def);
 
187
  /* Search for this environment variable */
 
188
  for(char **e = p->environ; *e != NULL; e++){
 
189
    if(strncmp(*e, def, namelen + 1) == 0){
 
190
      /* It already exists */
 
191
      if(replace){
 
192
        char *new = realloc(*e, strlen(def) + 1);
 
193
        if(new == NULL){
 
194
          return false;
 
195
        }
 
196
        *e = new;
 
197
        strcpy(*e, def);
 
198
      }
 
199
      return true;
 
200
    }
 
201
  }
186
202
  return add_to_char_array(def, &(p->environ), &(p->envc));
187
203
}
188
204
 
191
207
 * Descriptor Flags".
192
208
 * *Note File Descriptor Flags:(libc)Descriptor Flags.
193
209
 */
194
 
static int set_cloexec_flag(int fd)
195
 
{
 
210
static int set_cloexec_flag(int fd){
196
211
  int ret = fcntl(fd, F_GETFD, 0);
197
212
  /* If reading the flags failed, return error indication now. */
198
213
  if(ret < 0){
205
220
 
206
221
/* Mark processes as completed when they exit, and save their exit
207
222
   status. */
208
 
void handle_sigchld(__attribute__((unused)) int sig){
 
223
static void handle_sigchld(__attribute__((unused)) int sig){
209
224
  while(true){
210
225
    plugin *proc = plugin_list;
211
226
    int status;
221
236
      /* No child processes */
222
237
      break;
223
238
    }
224
 
 
 
239
    
225
240
    /* A child exited, find it in process_list */
226
241
    while(proc != NULL and proc->pid != pid){
227
242
      proc = proc->next;
236
251
}
237
252
 
238
253
/* Prints out a password to stdout */
239
 
bool print_out_password(const char *buffer, size_t length){
 
254
static bool print_out_password(const char *buffer, size_t length){
240
255
  ssize_t ret;
241
 
  if(length>0 and buffer[length-1] == '\n'){
242
 
    length--;
243
 
  }
244
256
  for(size_t written = 0; written < length; written += (size_t)ret){
245
257
    ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buffer + written,
246
258
                                   length - written));
327
339
    { .name = "global-options", .key = 'g',
328
340
      .arg = "OPTION[,OPTION[,...]]",
329
341
      .doc = "Options passed to all plugins" },
330
 
    { .name = "global-envs", .key = 'e',
 
342
    { .name = "global-env", .key = 'G',
331
343
      .arg = "VAR=value",
332
344
      .doc = "Environment variable passed to all plugins" },
333
345
    { .name = "options-for", .key = 'o',
334
346
      .arg = "PLUGIN:OPTION[,OPTION[,...]]",
335
347
      .doc = "Options passed only to specified plugin" },
336
 
    { .name = "envs-for", .key = 'f',
 
348
    { .name = "env-for", .key = 'E',
337
349
      .arg = "PLUGIN:ENV=value",
338
350
      .doc = "Environment variable passed to specified plugin" },
339
351
    { .name = "disable", .key = 'd',
340
352
      .arg = "PLUGIN",
341
353
      .doc = "Disable a specific plugin", .group = 1 },
 
354
    { .name = "enable", .key = 'e',
 
355
      .arg = "PLUGIN",
 
356
      .doc = "Enable a specific plugin", .group = 1 },
342
357
    { .name = "plugin-dir", .key = 128,
343
358
      .arg = "DIRECTORY",
344
359
      .doc = "Specify a different plugin directory", .group = 2 },
356
371
    { .name = NULL }
357
372
  };
358
373
  
359
 
  error_t parse_opt (int key, char *arg, __attribute__((unused)) struct argp_state *state) {
360
 
    /* Get the INPUT argument from `argp_parse', which we know is a
361
 
       pointer to our plugin list pointer. */
 
374
  error_t parse_opt (int key, char *arg, __attribute__((unused))
 
375
                     struct argp_state *state) {
362
376
    switch (key) {
363
377
    case 'g':                   /* --global-options */
364
378
      if (arg != NULL){
374
388
        }
375
389
      }
376
390
      break;
377
 
    case 'e':                   /* --global-envs */
 
391
    case 'G':                   /* --global-env */
378
392
      if(arg == NULL){
379
393
        break;
380
394
      }
381
 
      {
382
 
        char *envdef = strdup(arg);
383
 
        if(envdef == NULL){
384
 
          break;
385
 
        }
386
 
        if(not add_environment(getplugin(NULL), envdef)){
387
 
          perror("add_environment");
388
 
        }
 
395
      if(not add_environment(getplugin(NULL), arg, true)){
 
396
        perror("add_environment");
389
397
      }
390
398
      break;
391
399
    case 'o':                   /* --options-for */
392
400
      if (arg != NULL){
393
401
        char *p_name = strsep(&arg, ":");
394
 
        if(p_name[0] == '\0'){
 
402
        if(p_name[0] == '\0' or arg == NULL){
395
403
          break;
396
404
        }
397
405
        char *opt = strsep(&arg, ":");
398
 
        if(opt[0] == '\0'){
 
406
        if(opt[0] == '\0' or opt == NULL){
399
407
          break;
400
408
        }
401
 
        if(opt != NULL){
402
 
          char *p;
403
 
          while((p = strsep(&opt, ",")) != NULL){
404
 
            if(p[0] == '\0'){
405
 
              continue;
406
 
            }
407
 
            if(not add_argument(getplugin(p_name), p)){
408
 
              perror("add_argument");
409
 
              return ARGP_ERR_UNKNOWN;
410
 
            }
 
409
        char *p;
 
410
        while((p = strsep(&opt, ",")) != NULL){
 
411
          if(p[0] == '\0'){
 
412
            continue;
 
413
          }
 
414
          if(not add_argument(getplugin(p_name), p)){
 
415
            perror("add_argument");
 
416
            return ARGP_ERR_UNKNOWN;
411
417
          }
412
418
        }
413
419
      }
414
420
      break;
415
 
    case 'f':                   /* --envs-for */
 
421
    case 'E':                   /* --env-for */
416
422
      if(arg == NULL){
417
423
        break;
418
424
      }
421
427
        if(envdef == NULL){
422
428
          break;
423
429
        }
424
 
        char *p_name = strndup(arg, (size_t) (envdef-arg));
425
 
        if(p_name == NULL){
426
 
          break;
427
 
        }
428
 
        envdef++;
429
 
        if(not add_environment(getplugin(p_name), envdef)){
 
430
        *envdef = '\0';
 
431
        if(not add_environment(getplugin(arg), envdef+1, true)){
430
432
          perror("add_environment");
431
433
        }
432
434
      }
440
442
        p->disabled = true;
441
443
      }
442
444
      break;
 
445
    case 'e':                   /* --enable */
 
446
      if (arg != NULL){
 
447
        plugin *p = getplugin(arg);
 
448
        if(p == NULL){
 
449
          return ARGP_ERR_UNKNOWN;
 
450
        }
 
451
        p->disabled = false;
 
452
      }
 
453
      break;
443
454
    case 128:                   /* --plugin-dir */
 
455
      free(plugindir);
444
456
      plugindir = strdup(arg);
445
457
      if(plugindir == NULL){
446
458
        perror("strdup");
447
459
      }      
448
460
      break;
449
461
    case 129:                   /* --config-file */
 
462
      /* This is already done by parse_opt_config_file() */
 
463
      break;
 
464
    case 130:                   /* --userid */
 
465
      uid = (uid_t)strtol(arg, NULL, 10);
 
466
      break;
 
467
    case 131:                   /* --groupid */
 
468
      gid = (gid_t)strtol(arg, NULL, 10);
 
469
      break;
 
470
    case 132:                   /* --debug */
 
471
      debug = true;
 
472
      break;
 
473
    case ARGP_KEY_ARG:
 
474
      /* Cryptsetup always passes an argument, which is an empty
 
475
         string if "none" was specified in /etc/crypttab.  So if
 
476
         argument was empty, we ignore it silently. */
 
477
      if(arg[0] != '\0'){
 
478
        fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg);
 
479
      }
 
480
      break;
 
481
    case ARGP_KEY_END:
 
482
      break;
 
483
    default:
 
484
      return ARGP_ERR_UNKNOWN;
 
485
    }
 
486
    return 0;
 
487
  }
 
488
  
 
489
  /* This option parser is the same as parse_opt() above, except it
 
490
     ignores everything but the --config-file option. */
 
491
  error_t parse_opt_config_file (int key, char *arg,
 
492
                                 __attribute__((unused))
 
493
                                 struct argp_state *state) {
 
494
    switch (key) {
 
495
    case 'g':                   /* --global-options */
 
496
    case 'G':                   /* --global-env */
 
497
    case 'o':                   /* --options-for */
 
498
    case 'E':                   /* --env-for */
 
499
    case 'd':                   /* --disable */
 
500
    case 'e':                   /* --enable */
 
501
    case 128:                   /* --plugin-dir */
 
502
      break;
 
503
    case 129:                   /* --config-file */
 
504
      free(argfile);
450
505
      argfile = strdup(arg);
451
506
      if(argfile == NULL){
452
507
        perror("strdup");
453
508
      }
454
509
      break;      
455
510
    case 130:                   /* --userid */
456
 
      uid = (uid_t)strtol(arg, NULL, 10);
457
 
      break;
458
511
    case 131:                   /* --groupid */
459
 
      gid = (gid_t)strtol(arg, NULL, 10);
460
 
      break;
461
512
    case 132:                   /* --debug */
462
 
      debug = true;
463
 
      break;
464
513
    case ARGP_KEY_ARG:
465
 
      fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg);
466
 
      break;
467
514
    case ARGP_KEY_END:
468
515
      break;
469
516
    default:
472
519
    return 0;
473
520
  }
474
521
  
475
 
  struct argp argp = { .options = options, .parser = parse_opt,
476
 
                       .args_doc = "[+PLUS_SEPARATED_OPTIONS]",
 
522
  struct argp argp = { .options = options,
 
523
                       .parser = parse_opt_config_file,
 
524
                       .args_doc = "",
477
525
                       .doc = "Mandos plugin runner -- Run plugins" };
478
526
  
479
 
  ret = argp_parse (&argp, argc, argv, 0, 0, NULL);
 
527
  /* Parse using the parse_opt_config_file in order to get the custom
 
528
     config file location, if any. */
 
529
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
480
530
  if (ret == ARGP_ERR_UNKNOWN){
481
531
    fprintf(stderr, "Unknown error while parsing arguments\n");
482
532
    exitstatus = EXIT_FAILURE;
483
533
    goto fallback;
484
534
  }
485
 
 
486
 
  /* Opens the configfile if aviable */
 
535
  
 
536
  /* Reset to the normal argument parser */
 
537
  argp.parser = parse_opt;
 
538
  
 
539
  /* Open the configfile if available */
487
540
  if (argfile == NULL){
488
541
    conffp = fopen(AFILE, "r");
489
542
  } else {
507
560
    custom_argv[0] = argv[0];
508
561
    custom_argv[1] = NULL;
509
562
 
510
 
    /* for each line in the config file, strip whitespace and ignore commented text */
 
563
    /* for each line in the config file, strip whitespace and ignore
 
564
       commented text */
511
565
    while(true){
512
566
      sret = getline(&org_line, &size, conffp);
513
567
      if(sret == -1){
542
596
      }
543
597
    }
544
598
    free(org_line);
545
 
  } else{
 
599
  } else {
546
600
    /* Check for harmful errors and go to fallback. Other errors might
547
601
       not affect opening plugins */
548
602
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
554
608
  /* If there was any arguments from configuration file,
555
609
     pass them to parser as command arguments */
556
610
  if(custom_argv != NULL){
557
 
    ret = argp_parse (&argp, custom_argc, custom_argv, 0, 0, NULL);
 
611
    ret = argp_parse (&argp, custom_argc, custom_argv, ARGP_IN_ORDER,
 
612
                      0, NULL);
558
613
    if (ret == ARGP_ERR_UNKNOWN){
559
614
      fprintf(stderr, "Unknown error while parsing arguments\n");
560
615
      exitstatus = EXIT_FAILURE;
562
617
    }
563
618
  }
564
619
  
 
620
  /* Parse actual command line arguments, to let them override the
 
621
     config file */
 
622
  ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL);
 
623
  if (ret == ARGP_ERR_UNKNOWN){
 
624
    fprintf(stderr, "Unknown error while parsing arguments\n");
 
625
    exitstatus = EXIT_FAILURE;
 
626
    goto fallback;
 
627
  }
 
628
  
565
629
  if(debug){
566
630
    for(plugin *p = plugin_list; p != NULL; p=p->next){
567
631
      fprintf(stderr, "Plugin: %s has %d arguments\n",
575
639
      }
576
640
    }
577
641
  }
578
 
 
 
642
  
579
643
  /* Strip permissions down to nobody */
580
644
  ret = setuid(uid);
581
645
  if (ret == -1){
585
649
  if (ret == -1){
586
650
    perror("setgid");
587
651
  }
588
 
 
 
652
  
589
653
  if (plugindir == NULL){
590
654
    dir = opendir(PDIR);
591
655
  } else {
612
676
  }
613
677
  
614
678
  FD_ZERO(&rfds_all);
615
 
 
 
679
  
616
680
  /* Read and execute any executable in the plugin directory*/
617
681
  while(true){
618
682
    dirst = readdir(dir);
619
683
    
620
 
    // All directory entries have been processed
 
684
    /* All directory entries have been processed */
621
685
    if(dirst == NULL){
622
686
      if (errno == EBADF){
623
687
        perror("readdir");
629
693
    
630
694
    d_name_len = strlen(dirst->d_name);
631
695
    
632
 
    // Ignore dotfiles, backup files and other junk
 
696
    /* Ignore dotfiles, backup files and other junk */
633
697
    {
634
698
      bool bad_name = false;
635
699
      
637
701
      
638
702
      const char const *bad_suffixes[] = { "~", "#", ".dpkg-new",
639
703
                                           ".dpkg-old",
 
704
                                           ".dpkg-bak",
640
705
                                           ".dpkg-divert", NULL };
641
706
      for(const char **pre = bad_prefixes; *pre != NULL; pre++){
642
707
        size_t pre_len = strlen(*pre);
673
738
    }
674
739
 
675
740
    char *filename;
676
 
    ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
 
741
    if(plugindir == NULL){
 
742
      ret = asprintf(&filename, PDIR "/%s", dirst->d_name);
 
743
    } else {
 
744
      ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name);
 
745
    }
677
746
    if(ret < 0){
678
747
      perror("asprintf");
679
748
      continue;
721
790
        }
722
791
        /* Add global environment variables */
723
792
        for(char **e = g->environ; *e != NULL; e++){
724
 
          if(not add_environment(p, *e)){
 
793
          if(not add_environment(p, *e, false)){
725
794
            perror("add_environment");
726
795
          }
727
796
        }
732
801
       process, too. */
733
802
    if(p->environ[0] != NULL){
734
803
      for(char **e = environ; *e != NULL; e++){
735
 
        char *copy = strdup(*e);
736
 
        if(copy == NULL){
737
 
          perror("strdup");
738
 
          continue;
739
 
        }
740
 
        if(not add_environment(p, copy)){
 
804
        if(not add_environment(p, *e, false)){
741
805
          perror("add_environment");
742
806
        }
743
807
      }
770
834
      exitstatus = EXIT_FAILURE;
771
835
      goto fallback;
772
836
    }
773
 
    // Starting a new process to be watched
 
837
    /* Starting a new process to be watched */
774
838
    pid_t pid = fork();
775
839
    if(pid == -1){
776
840
      perror("fork");
784
848
        perror("sigaction");
785
849
        _exit(EXIT_FAILURE);
786
850
      }
787
 
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
 
851
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
788
852
      if(ret < 0){
789
853
        perror("sigprocmask");
790
854
        _exit(EXIT_FAILURE);
791
855
      }
792
 
 
 
856
      
793
857
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
794
858
      if(ret == -1){
795
859
        perror("dup2");
845
909
    if (maxfd < new_plugin->fd){
846
910
      maxfd = new_plugin->fd;
847
911
    }
848
 
    
849
912
  }
850
913
  
851
914
  closedir(dir);
852
915
  dir = NULL;
853
 
 
 
916
  
854
917
  for(plugin *p = plugin_list; p != NULL; p = p->next){
855
918
    if(p->pid != 0){
856
919
      break;
861
924
      free_plugin_list();
862
925
    }
863
926
  }
864
 
 
 
927
  
865
928
  /* Main loop while running plugins exist */
866
929
  while(plugin_list){
867
930
    fd_set rfds = rfds_all;
873
936
    }
874
937
    /* OK, now either a process completed, or something can be read
875
938
       from one of them */
876
 
    for(plugin *proc = plugin_list; proc != NULL; proc = proc->next){
 
939
    for(plugin *proc = plugin_list; proc != NULL;){
877
940
      /* Is this process completely done? */
878
941
      if(proc->eof and proc->completed){
879
942
        /* Only accept the plugin output if it exited cleanly */
906
969
            exitstatus = EXIT_FAILURE;
907
970
            goto fallback;
908
971
          }
 
972
          
 
973
          plugin *next_plugin = proc->next;
909
974
          free_plugin(proc);
 
975
          proc = next_plugin;
 
976
          
910
977
          /* We are done modifying process list, so unblock signal */
911
978
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
912
979
                             NULL);
919
986
          if(plugin_list == NULL){
920
987
            break;
921
988
          }
 
989
          
922
990
          continue;
923
991
        }
924
992
        
925
993
        /* This process exited nicely, so print its buffer */
926
 
 
 
994
        
927
995
        bool bret = print_out_password(proc->buffer,
928
996
                                       proc->buffer_length);
929
997
        if(not bret){
936
1004
      /* This process has not completed.  Does it have any output? */
937
1005
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
938
1006
        /* This process had nothing to say at this time */
 
1007
        proc = proc->next;
939
1008
        continue;
940
1009
      }
941
1010
      /* Before reading, make the process' data buffer large enough */
954
1023
                 BUFFER_SIZE);
955
1024
      if(ret < 0){
956
1025
        /* Read error from this process; ignore the error */
 
1026
        proc = proc->next;
957
1027
        continue;
958
1028
      }
959
1029
      if(ret == 0){
974
1044
    bool bret;
975
1045
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
976
1046
    char *passwordbuffer = getpass("Password: ");
977
 
    bret = print_out_password(passwordbuffer, strlen(passwordbuffer));
 
1047
    size_t len = strlen(passwordbuffer);
 
1048
    /* Strip trailing newline */
 
1049
    if(len > 0 and passwordbuffer[len-1] == '\n'){
 
1050
      passwordbuffer[len-1] = '\0'; /* not strictly necessary */
 
1051
      len--;
 
1052
    }
 
1053
    bret = print_out_password(passwordbuffer, len);
978
1054
    if(not bret){
979
1055
      perror("print_out_password");
980
1056
      exitstatus = EXIT_FAILURE;
987
1063
    perror("sigaction");
988
1064
    exitstatus = EXIT_FAILURE;
989
1065
  }
990
 
 
 
1066
  
991
1067
  if(custom_argv != NULL){
992
1068
    for(char **arg = custom_argv+1; *arg != NULL; arg++){
993
1069
      free(*arg);
999
1075
    closedir(dir);
1000
1076
  }
1001
1077
  
1002
 
  /* Free the process list and kill the processes */
 
1078
  /* Kill the processes */
1003
1079
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1004
1080
    if(p->pid != 0){
1005
1081
      close(p->fd);
1018
1094
  if(errno != ECHILD){
1019
1095
    perror("wait");
1020
1096
  }
1021
 
 
 
1097
  
1022
1098
  free_plugin_list();
1023
1099
  
1024
1100
  free(plugindir);