/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 },
358
373
  
359
374
  error_t parse_opt (int key, char *arg, __attribute__((unused))
360
375
                     struct argp_state *state) {
361
 
    /* Get the INPUT argument from `argp_parse', which we know is a
362
 
       pointer to our plugin list pointer. */
363
376
    switch (key) {
364
377
    case 'g':                   /* --global-options */
365
378
      if (arg != NULL){
375
388
        }
376
389
      }
377
390
      break;
378
 
    case 'e':                   /* --global-envs */
 
391
    case 'G':                   /* --global-env */
379
392
      if(arg == NULL){
380
393
        break;
381
394
      }
382
 
      {
383
 
        char *envdef = strdup(arg);
384
 
        if(envdef == NULL){
385
 
          break;
386
 
        }
387
 
        if(not add_environment(getplugin(NULL), envdef)){
388
 
          perror("add_environment");
389
 
        }
 
395
      if(not add_environment(getplugin(NULL), arg, true)){
 
396
        perror("add_environment");
390
397
      }
391
398
      break;
392
399
    case 'o':                   /* --options-for */
393
400
      if (arg != NULL){
394
401
        char *p_name = strsep(&arg, ":");
395
 
        if(p_name[0] == '\0'){
 
402
        if(p_name[0] == '\0' or arg == NULL){
396
403
          break;
397
404
        }
398
405
        char *opt = strsep(&arg, ":");
399
 
        if(opt[0] == '\0'){
 
406
        if(opt[0] == '\0' or opt == NULL){
400
407
          break;
401
408
        }
402
 
        if(opt != NULL){
403
 
          char *p;
404
 
          while((p = strsep(&opt, ",")) != NULL){
405
 
            if(p[0] == '\0'){
406
 
              continue;
407
 
            }
408
 
            if(not add_argument(getplugin(p_name), p)){
409
 
              perror("add_argument");
410
 
              return ARGP_ERR_UNKNOWN;
411
 
            }
 
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;
412
417
          }
413
418
        }
414
419
      }
415
420
      break;
416
 
    case 'f':                   /* --envs-for */
 
421
    case 'E':                   /* --env-for */
417
422
      if(arg == NULL){
418
423
        break;
419
424
      }
422
427
        if(envdef == NULL){
423
428
          break;
424
429
        }
425
 
        char *p_name = strndup(arg, (size_t) (envdef-arg));
426
 
        if(p_name == NULL){
427
 
          break;
428
 
        }
429
 
        envdef++;
430
 
        if(not add_environment(getplugin(p_name), envdef)){
 
430
        *envdef = '\0';
 
431
        if(not add_environment(getplugin(arg), envdef+1, true)){
431
432
          perror("add_environment");
432
433
        }
433
434
      }
441
442
        p->disabled = true;
442
443
      }
443
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;
444
454
    case 128:                   /* --plugin-dir */
 
455
      free(plugindir);
445
456
      plugindir = strdup(arg);
446
457
      if(plugindir == NULL){
447
458
        perror("strdup");
448
459
      }      
449
460
      break;
450
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);
451
505
      argfile = strdup(arg);
452
506
      if(argfile == NULL){
453
507
        perror("strdup");
454
508
      }
455
509
      break;      
456
510
    case 130:                   /* --userid */
457
 
      uid = (uid_t)strtol(arg, NULL, 10);
458
 
      break;
459
511
    case 131:                   /* --groupid */
460
 
      gid = (gid_t)strtol(arg, NULL, 10);
461
 
      break;
462
512
    case 132:                   /* --debug */
463
 
      debug = true;
464
 
      break;
465
513
    case ARGP_KEY_ARG:
466
 
      fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg);
467
 
      break;
468
514
    case ARGP_KEY_END:
469
515
      break;
470
516
    default:
473
519
    return 0;
474
520
  }
475
521
  
476
 
  struct argp argp = { .options = options, .parser = parse_opt,
477
 
                       .args_doc = "[+PLUS_SEPARATED_OPTIONS]",
 
522
  struct argp argp = { .options = options,
 
523
                       .parser = parse_opt_config_file,
 
524
                       .args_doc = "",
478
525
                       .doc = "Mandos plugin runner -- Run plugins" };
479
526
  
480
 
  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);
481
530
  if (ret == ARGP_ERR_UNKNOWN){
482
531
    fprintf(stderr, "Unknown error while parsing arguments\n");
483
532
    exitstatus = EXIT_FAILURE;
484
533
    goto fallback;
485
534
  }
486
 
 
487
 
  /* 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 */
488
540
  if (argfile == NULL){
489
541
    conffp = fopen(AFILE, "r");
490
542
  } else {
544
596
      }
545
597
    }
546
598
    free(org_line);
547
 
  } else{
 
599
  } else {
548
600
    /* Check for harmful errors and go to fallback. Other errors might
549
601
       not affect opening plugins */
550
602
    if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){
556
608
  /* If there was any arguments from configuration file,
557
609
     pass them to parser as command arguments */
558
610
  if(custom_argv != NULL){
559
 
    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);
560
613
    if (ret == ARGP_ERR_UNKNOWN){
561
614
      fprintf(stderr, "Unknown error while parsing arguments\n");
562
615
      exitstatus = EXIT_FAILURE;
564
617
    }
565
618
  }
566
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
  
567
629
  if(debug){
568
630
    for(plugin *p = plugin_list; p != NULL; p=p->next){
569
631
      fprintf(stderr, "Plugin: %s has %d arguments\n",
577
639
      }
578
640
    }
579
641
  }
580
 
 
 
642
  
581
643
  /* Strip permissions down to nobody */
582
644
  ret = setuid(uid);
583
645
  if (ret == -1){
587
649
  if (ret == -1){
588
650
    perror("setgid");
589
651
  }
590
 
 
 
652
  
591
653
  if (plugindir == NULL){
592
654
    dir = opendir(PDIR);
593
655
  } else {
614
676
  }
615
677
  
616
678
  FD_ZERO(&rfds_all);
617
 
 
 
679
  
618
680
  /* Read and execute any executable in the plugin directory*/
619
681
  while(true){
620
682
    dirst = readdir(dir);
621
683
    
622
 
    // All directory entries have been processed
 
684
    /* All directory entries have been processed */
623
685
    if(dirst == NULL){
624
686
      if (errno == EBADF){
625
687
        perror("readdir");
631
693
    
632
694
    d_name_len = strlen(dirst->d_name);
633
695
    
634
 
    // Ignore dotfiles, backup files and other junk
 
696
    /* Ignore dotfiles, backup files and other junk */
635
697
    {
636
698
      bool bad_name = false;
637
699
      
639
701
      
640
702
      const char const *bad_suffixes[] = { "~", "#", ".dpkg-new",
641
703
                                           ".dpkg-old",
 
704
                                           ".dpkg-bak",
642
705
                                           ".dpkg-divert", NULL };
643
706
      for(const char **pre = bad_prefixes; *pre != NULL; pre++){
644
707
        size_t pre_len = strlen(*pre);
675
738
    }
676
739
 
677
740
    char *filename;
678
 
    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
    }
679
746
    if(ret < 0){
680
747
      perror("asprintf");
681
748
      continue;
723
790
        }
724
791
        /* Add global environment variables */
725
792
        for(char **e = g->environ; *e != NULL; e++){
726
 
          if(not add_environment(p, *e)){
 
793
          if(not add_environment(p, *e, false)){
727
794
            perror("add_environment");
728
795
          }
729
796
        }
734
801
       process, too. */
735
802
    if(p->environ[0] != NULL){
736
803
      for(char **e = environ; *e != NULL; e++){
737
 
        char *copy = strdup(*e);
738
 
        if(copy == NULL){
739
 
          perror("strdup");
740
 
          continue;
741
 
        }
742
 
        if(not add_environment(p, copy)){
 
804
        if(not add_environment(p, *e, false)){
743
805
          perror("add_environment");
744
806
        }
745
807
      }
772
834
      exitstatus = EXIT_FAILURE;
773
835
      goto fallback;
774
836
    }
775
 
    // Starting a new process to be watched
 
837
    /* Starting a new process to be watched */
776
838
    pid_t pid = fork();
777
839
    if(pid == -1){
778
840
      perror("fork");
786
848
        perror("sigaction");
787
849
        _exit(EXIT_FAILURE);
788
850
      }
789
 
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
 
851
      ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
790
852
      if(ret < 0){
791
853
        perror("sigprocmask");
792
854
        _exit(EXIT_FAILURE);
793
855
      }
794
 
 
 
856
      
795
857
      ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
796
858
      if(ret == -1){
797
859
        perror("dup2");
847
909
    if (maxfd < new_plugin->fd){
848
910
      maxfd = new_plugin->fd;
849
911
    }
850
 
    
851
912
  }
852
913
  
853
914
  closedir(dir);
854
915
  dir = NULL;
855
 
 
 
916
  
856
917
  for(plugin *p = plugin_list; p != NULL; p = p->next){
857
918
    if(p->pid != 0){
858
919
      break;
863
924
      free_plugin_list();
864
925
    }
865
926
  }
866
 
 
 
927
  
867
928
  /* Main loop while running plugins exist */
868
929
  while(plugin_list){
869
930
    fd_set rfds = rfds_all;
875
936
    }
876
937
    /* OK, now either a process completed, or something can be read
877
938
       from one of them */
878
 
    for(plugin *proc = plugin_list; proc != NULL; proc = proc->next){
 
939
    for(plugin *proc = plugin_list; proc != NULL;){
879
940
      /* Is this process completely done? */
880
941
      if(proc->eof and proc->completed){
881
942
        /* Only accept the plugin output if it exited cleanly */
908
969
            exitstatus = EXIT_FAILURE;
909
970
            goto fallback;
910
971
          }
 
972
          
 
973
          plugin *next_plugin = proc->next;
911
974
          free_plugin(proc);
 
975
          proc = next_plugin;
 
976
          
912
977
          /* We are done modifying process list, so unblock signal */
913
978
          ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
914
979
                             NULL);
921
986
          if(plugin_list == NULL){
922
987
            break;
923
988
          }
 
989
          
924
990
          continue;
925
991
        }
926
992
        
927
993
        /* This process exited nicely, so print its buffer */
928
 
 
 
994
        
929
995
        bool bret = print_out_password(proc->buffer,
930
996
                                       proc->buffer_length);
931
997
        if(not bret){
938
1004
      /* This process has not completed.  Does it have any output? */
939
1005
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
940
1006
        /* This process had nothing to say at this time */
 
1007
        proc = proc->next;
941
1008
        continue;
942
1009
      }
943
1010
      /* Before reading, make the process' data buffer large enough */
956
1023
                 BUFFER_SIZE);
957
1024
      if(ret < 0){
958
1025
        /* Read error from this process; ignore the error */
 
1026
        proc = proc->next;
959
1027
        continue;
960
1028
      }
961
1029
      if(ret == 0){
976
1044
    bool bret;
977
1045
    fprintf(stderr, "Going to fallback mode using getpass(3)\n");
978
1046
    char *passwordbuffer = getpass("Password: ");
979
 
    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);
980
1054
    if(not bret){
981
1055
      perror("print_out_password");
982
1056
      exitstatus = EXIT_FAILURE;
989
1063
    perror("sigaction");
990
1064
    exitstatus = EXIT_FAILURE;
991
1065
  }
992
 
 
 
1066
  
993
1067
  if(custom_argv != NULL){
994
1068
    for(char **arg = custom_argv+1; *arg != NULL; arg++){
995
1069
      free(*arg);
1001
1075
    closedir(dir);
1002
1076
  }
1003
1077
  
1004
 
  /* Free the process list and kill the processes */
 
1078
  /* Kill the processes */
1005
1079
  for(plugin *p = plugin_list; p != NULL; p = p->next){
1006
1080
    if(p->pid != 0){
1007
1081
      close(p->fd);
1020
1094
  if(errno != ECHILD){
1021
1095
    perror("wait");
1022
1096
  }
1023
 
 
 
1097
  
1024
1098
  free_plugin_list();
1025
1099
  
1026
1100
  free(plugindir);