/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: 2021-02-04 17:59:45 UTC
  • Revision ID: teddy@recompile.se-20210204175945-8druo6d88ipc1z58
Fix issue with french translation

Initial white space was missing in both msgid and msgstr of the french
translation, leading to checking tools reporing an incomplete
translation.  The string is a raw key id, and therefore did not need
translation, so this was never a user-visible issue.

* debian/po/fr.po: Add missing whitespace to the id and translation
  for msgid " ${key_id}".

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 © 2008-2014 Teddy Hogeborn
6
 
 * Copyright © 2008-2014 Björn Påhlsson
7
 
 * 
8
 
 * This program is free software: you can redistribute it and/or
9
 
 * modify it under the terms of the GNU General Public License as
10
 
 * published by the Free Software Foundation, either version 3 of the
11
 
 * License, or (at your option) any later version.
12
 
 * 
13
 
 * This program is distributed in the hope that it will be useful, but
 
5
 * Copyright © 2008-2021 Teddy Hogeborn
 
6
 * Copyright © 2008-2021 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
14
16
 * WITHOUT ANY WARRANTY; without even the implied warranty of
15
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
18
 * General Public License for more details.
17
19
 * 
18
20
 * You should have received a copy of the GNU General Public License
19
 
 * along with this program.  If not, see
20
 
 * <http://www.gnu.org/licenses/>.
 
21
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
21
22
 * 
22
23
 * Contact the authors at <mandos@recompile.se>.
23
24
 */
24
25
 
25
 
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY(), getline(),
26
 
                                   O_CLOEXEC, pipe2() */
 
26
#define _GNU_SOURCE             /* strchrnul(), TEMP_FAILURE_RETRY(),
 
27
                                   getline(), asprintf(), O_CLOEXEC,
 
28
                                   scandirat(), pipe2() */
 
29
#include <argp.h>               /* argp_program_version,
 
30
                                   argp_program_bug_address,
 
31
                                   struct argp_option,
 
32
                                   struct argp_state, argp_error(),
 
33
                                   ARGP_NO_EXIT, argp_state_help,
 
34
                                   ARGP_HELP_STD_HELP,
 
35
                                   ARGP_HELP_USAGE, ARGP_HELP_EXIT_OK,
 
36
                                   ARGP_KEY_ARG, ARGP_ERR_UNKNOWN,
 
37
                                   struct argp, argp_parse(),
 
38
                                   ARGP_IN_ORDER, ARGP_NO_HELP */
 
39
#include <stdbool.h>            /* bool, false, true */
 
40
#include <sys/types.h>          /* pid_t, sig_atomic_t, uid_t, gid_t,
 
41
                                   getuid(), setgid(), setuid() */
27
42
#include <stddef.h>             /* size_t, NULL */
28
 
#include <stdlib.h>             /* malloc(), exit(), EXIT_SUCCESS,
29
 
                                   realloc() */
30
 
#include <stdbool.h>            /* bool, true, false */
31
 
#include <stdio.h>              /* fileno(), fprintf(),
32
 
                                   stderr, STDOUT_FILENO, fclose() */
33
 
#include <sys/types.h>          /* fstat(), struct stat, waitpid(),
34
 
                                   WIFEXITED(), WEXITSTATUS(), wait(),
35
 
                                   pid_t, uid_t, gid_t, getuid(),
36
 
                                   getgid() */
37
 
#include <sys/select.h>         /* fd_set, select(), FD_ZERO(),
38
 
                                   FD_SET(), FD_ISSET(), FD_CLR */
39
 
#include <sys/wait.h>           /* wait(), waitpid(), WIFEXITED(),
40
 
                                   WEXITSTATUS(), WTERMSIG(),
41
 
                                   WCOREDUMP() */
42
 
#include <sys/stat.h>           /* struct stat, fstat(), S_ISREG() */
43
 
#include <iso646.h>             /* and, or, not */
44
 
#include <dirent.h>             /* struct dirent, scandirat() */
45
 
#include <unistd.h>             /* fcntl(), F_GETFD, F_SETFD,
46
 
                                   FD_CLOEXEC, write(), STDOUT_FILENO,
47
 
                                   struct stat, fstat(), close(),
48
 
                                   setgid(), setuid(), S_ISREG(),
49
 
                                   faccessat() pipe2(), fork(),
50
 
                                   _exit(), dup2(), fexecve(), read()
51
 
                                */
 
43
#include <iso646.h>             /* or, and, not */
 
44
#include <string.h>             /* strcmp(), strdup(), strchrnul(),
 
45
                                   strncmp(), strlen(), strcpy(),
 
46
                                   strsep(), strchr(), strsignal() */
 
47
#include <stdlib.h>             /* malloc(), free(), reallocarray(),
 
48
                                   realloc(), EXIT_SUCCESS */
 
49
#include <errno.h>              /* errno, EINTR, ENOMEM, ECHILD,
 
50
                                   error_t, EINVAL, EMFILE, ENFILE,
 
51
                                   ENOENT, ESRCH */
 
52
#include <stdint.h>             /* SIZE_MAX */
 
53
#define _GNU_SOURCE             /* strchrnul(), TEMP_FAILURE_RETRY(),
 
54
                                   getline(), asprintf(), O_CLOEXEC,
 
55
                                   scandirat(), pipe2() */
 
56
#include <unistd.h>             /* TEMP_FAILURE_RETRY(), ssize_t,
 
57
                                   write(), STDOUT_FILENO, uid_t,
 
58
                                   gid_t, getuid(), fchown(), close(),
 
59
                                   symlink(), setgid(), setuid(),
 
60
                                   faccessat(), X_OK, pipe(), pipe2(),
 
61
                                   fork(), _exit(), dup2(), fexecve(),
 
62
                                   read(), getpass() */
52
63
#include <fcntl.h>              /* fcntl(), F_GETFD, F_SETFD,
53
 
                                   FD_CLOEXEC, openat(), scandirat(),
54
 
                                   pipe2() */
55
 
#include <string.h>             /* strsep, strlen(), strsignal(),
56
 
                                   strcmp(), strncmp() */
57
 
#include <errno.h>              /* errno */
58
 
#include <argp.h>               /* struct argp_option, struct
59
 
                                   argp_state, struct argp,
60
 
                                   argp_parse(), ARGP_ERR_UNKNOWN,
61
 
                                   ARGP_KEY_END, ARGP_KEY_ARG,
62
 
                                   error_t */
63
 
#include <signal.h>             /* struct sigaction, sigemptyset(),
64
 
                                   sigaddset(), sigaction(),
65
 
                                   sigprocmask(), SIG_BLOCK, SIGCHLD,
66
 
                                   SIG_UNBLOCK, kill(), sig_atomic_t
67
 
                                */
68
 
#include <errno.h>              /* errno, EBADF */
69
 
#include <inttypes.h>           /* intmax_t, PRIdMAX, strtoimax() */
 
64
                                   FD_CLOEXEC, open(), O_RDONLY,
 
65
                                   O_CLOEXEC, openat() */
 
66
#include <sys/wait.h>           /* waitpid(), WNOHANG, WIFEXITED(),
 
67
                                   WEXITSTATUS(), WIFSIGNALED(),
 
68
                                   WTERMSIG(), wait() */
 
69
#include <error.h>              /* error() */
 
70
#include <stdio.h>              /* FILE, fprintf(), fopen(),
 
71
                                   getline(), fclose(), EOF,
 
72
                                   asprintf(), stderr */
 
73
#include <dirent.h>             /* struct dirent, scandirat(),
 
74
                                   alphasort() */
 
75
#include <sys/stat.h>           /* struct stat, fstat(), S_ISDIR(),
 
76
                                   lstat(), S_ISREG() */
 
77
#include <sys/select.h>         /* fd_set, FD_ZERO(), FD_SETSIZE,
 
78
                                   FD_SET(), select(), FD_CLR(),
 
79
                                   FD_ISSET() */
 
80
#include <signal.h>             /* struct sigaction, SA_NOCLDSTOP,
 
81
                                   sigemptyset(), sigaddset(),
 
82
                                   SIGCHLD, sigprocmask(), SIG_BLOCK,
 
83
                                   SIG_UNBLOCK, kill(), SIGTERM */
70
84
#include <sysexits.h>           /* EX_OSERR, EX_USAGE, EX_IOERR,
71
85
                                   EX_CONFIG, EX_UNAVAILABLE, EX_OK */
72
 
#include <errno.h>              /* errno */
73
 
#include <error.h>              /* error() */
74
 
#include <fnmatch.h>            /* fnmatch() */
 
86
#include <inttypes.h>           /* intmax_t, strtoimax(), PRIdMAX */
 
87
#include <fnmatch.h>            /* fnmatch(), FNM_FILE_NAME,
 
88
                                   FNM_PERIOD, FNM_NOMATCH */
75
89
 
76
90
#define BUFFER_SIZE 256
77
91
 
179
193
  /* Resize the pointed-to array to hold one more pointer */
180
194
  char **new_array = NULL;
181
195
  do {
182
 
    new_array = realloc(*array, sizeof(char *)
183
 
                        * (size_t) ((*len) + 2));
 
196
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 26)
 
197
    new_array = reallocarray(*array, (size_t)((*len) + 2),
 
198
                             sizeof(char *));
 
199
#else
 
200
    if(((size_t)((*len) + 2)) > (SIZE_MAX / sizeof(char *))){
 
201
      /* overflow */
 
202
      new_array = NULL;
 
203
      errno = ENOMEM;
 
204
    } else {
 
205
      new_array = realloc(*array, (size_t)((*len) + 2)
 
206
                          * sizeof(char *));
 
207
    }
 
208
#endif
184
209
  } while(new_array == NULL and errno == EINTR);
185
210
  /* Malloc check */
186
211
  if(new_array == NULL){
313
338
__attribute__((nonnull))
314
339
static void free_plugin(plugin *plugin_node){
315
340
  
316
 
  for(char **arg = plugin_node->argv; *arg != NULL; arg++){
 
341
  for(char **arg = (plugin_node->argv)+1; *arg != NULL; arg++){
317
342
    free(*arg);
318
343
  }
 
344
  free(plugin_node->name);
319
345
  free(plugin_node->argv);
320
346
  for(char **env = plugin_node->environ; *env != NULL; env++){
321
347
    free(*env);
564
590
    case '?':                   /* --help */
565
591
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
566
592
      argp_state_help(state, state->out_stream, ARGP_HELP_STD_HELP);
 
593
      __builtin_unreachable();
567
594
    case -3:                    /* --usage */
568
595
      state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
569
596
      argp_state_help(state, state->out_stream,
570
597
                      ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
 
598
      __builtin_unreachable();
571
599
    case 'V':                   /* --version */
572
600
      fprintf(state->out_stream, "%s\n", argp_program_version);
573
601
      exit(EXIT_SUCCESS);
583
611
      if(arg[0] == '\0'){
584
612
        break;
585
613
      }
 
614
#if __GNUC__ >= 7
 
615
      __attribute__((fallthrough));
 
616
#else
 
617
          /* FALLTHROUGH */
 
618
#endif
586
619
    default:
587
620
      return ARGP_ERR_UNKNOWN;
588
621
    }
700
733
        
701
734
        custom_argc += 1;
702
735
        {
703
 
          char **new_argv = realloc(custom_argv, sizeof(char *)
704
 
                                    * ((unsigned int)
705
 
                                       custom_argc + 1));
 
736
#if defined(__GLIBC_PREREQ) and __GLIBC_PREREQ(2, 26)
 
737
          char **new_argv = reallocarray(custom_argv, (size_t)custom_argc + 1,
 
738
                                         sizeof(char *));
 
739
#else
 
740
          char **new_argv = NULL;
 
741
          if(((size_t)custom_argc + 1) > (SIZE_MAX / sizeof(char *))){
 
742
            /* overflow */
 
743
            errno = ENOMEM;
 
744
          } else {
 
745
            new_argv = realloc(custom_argv, ((size_t)custom_argc + 1)
 
746
                               * sizeof(char *));
 
747
          }
 
748
#endif
706
749
          if(new_argv == NULL){
707
 
            error(0, errno, "realloc");
 
750
            error(0, errno, "reallocarray");
708
751
            exitstatus = EX_OSERR;
709
752
            free(new_arg);
710
753
            free(org_line);
794
837
  }
795
838
  
796
839
  if(debug){
797
 
    for(plugin *p = plugin_list; p != NULL; p=p->next){
 
840
    for(plugin *p = plugin_list; p != NULL; p = p->next){
798
841
      fprintf(stderr, "Plugin: %s has %d arguments\n",
799
842
              p->name ? p->name : "Global", p->argc - 1);
800
843
      for(char **a = p->argv; *a != NULL; a++){
809
852
  
810
853
  if(getuid() == 0){
811
854
    /* Work around Debian bug #633582:
812
 
       <http://bugs.debian.org/633582> */
 
855
       <https://bugs.debian.org/633582> */
813
856
    int plugindir_fd = open(/* plugindir or */ PDIR, O_RDONLY);
814
857
    if(plugindir_fd == -1){
815
858
      if(errno != ENOENT){
827
870
          }
828
871
        }
829
872
      }
830
 
      TEMP_FAILURE_RETRY(close(plugindir_fd));
 
873
      close(plugindir_fd);
 
874
    }
 
875
 
 
876
    /* Work around Debian bug #981302
 
877
       <https://bugs.debian.org/981302> */
 
878
    if(lstat("/dev/fd", &st) != 0 and errno == ENOENT){
 
879
      ret = symlink("/proc/self/fd", "/dev/fd");
 
880
      if(ret == -1){
 
881
        error(0, errno, "Failed to create /dev/fd symlink");
 
882
      }
831
883
    }
832
884
  }
833
885
  
892
944
    return 1;
893
945
  }
894
946
  
895
 
#ifdef __GLIBC__
896
 
#if __GLIBC_PREREQ(2, 15)
897
947
  int numplugins = scandirat(dir_fd, ".", &direntries, good_name,
898
948
                             alphasort);
899
 
#else  /* not __GLIBC_PREREQ(2, 15) */
900
 
  int numplugins = scandir(plugindir != NULL ? plugindir : PDIR,
901
 
                           &direntries, good_name, alphasort);
902
 
#endif  /* not __GLIBC_PREREQ(2, 15) */
903
 
#else   /* not __GLIBC__ */
904
 
  int numplugins = scandir(plugindir != NULL ? plugindir : PDIR,
905
 
                           &direntries, good_name, alphasort);
906
 
#endif  /* not __GLIBC__ */
907
949
  if(numplugins == -1){
908
950
    error(0, errno, "Could not scan plugin dir");
909
951
    direntries = NULL;
925
967
    ret = (int)TEMP_FAILURE_RETRY(fstat(plugin_fd, &st));
926
968
    if(ret == -1){
927
969
      error(0, errno, "stat");
928
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
970
      close(plugin_fd);
929
971
      free(direntries[i]);
930
972
      continue;
931
973
    }
940
982
                plugindir != NULL ? plugindir : PDIR,
941
983
                direntries[i]->d_name);
942
984
      }
943
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
985
      close(plugin_fd);
944
986
      free(direntries[i]);
945
987
      continue;
946
988
    }
948
990
    plugin *p = getplugin(direntries[i]->d_name);
949
991
    if(p == NULL){
950
992
      error(0, errno, "getplugin");
951
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
993
      close(plugin_fd);
952
994
      free(direntries[i]);
953
995
      continue;
954
996
    }
957
999
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
958
1000
                direntries[i]->d_name);
959
1001
      }
960
 
      TEMP_FAILURE_RETRY(close(plugin_fd));
 
1002
      close(plugin_fd);
961
1003
      free(direntries[i]);
962
1004
      continue;
963
1005
    }
1003
1045
    if(pipefd[0] >= FD_SETSIZE){
1004
1046
      fprintf(stderr, "pipe()[0] (%d) >= FD_SETSIZE (%d)", pipefd[0],
1005
1047
              FD_SETSIZE);
1006
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
1007
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
1048
      close(pipefd[0]);
 
1049
      close(pipefd[1]);
1008
1050
      exitstatus = EX_OSERR;
1009
1051
      free(direntries[i]);
1010
1052
      goto fallback;
1014
1056
    ret = set_cloexec_flag(pipefd[0]);
1015
1057
    if(ret < 0){
1016
1058
      error(0, errno, "set_cloexec_flag");
1017
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
1018
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
1059
      close(pipefd[0]);
 
1060
      close(pipefd[1]);
1019
1061
      exitstatus = EX_OSERR;
1020
1062
      free(direntries[i]);
1021
1063
      goto fallback;
1023
1065
    ret = set_cloexec_flag(pipefd[1]);
1024
1066
    if(ret < 0){
1025
1067
      error(0, errno, "set_cloexec_flag");
1026
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
1027
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
1068
      close(pipefd[0]);
 
1069
      close(pipefd[1]);
1028
1070
      exitstatus = EX_OSERR;
1029
1071
      free(direntries[i]);
1030
1072
      goto fallback;
1049
1091
      error(0, errno, "fork");
1050
1092
      TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1051
1093
                                     &sigchld_action.sa_mask, NULL));
1052
 
      TEMP_FAILURE_RETRY(close(pipefd[0]));
1053
 
      TEMP_FAILURE_RETRY(close(pipefd[1]));
 
1094
      close(pipefd[0]);
 
1095
      close(pipefd[1]);
1054
1096
      exitstatus = EX_OSERR;
1055
1097
      free(direntries[i]);
1056
1098
      goto fallback;
1084
1126
      /* no return */
1085
1127
    }
1086
1128
    /* Parent process */
1087
 
    TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
1088
 
                                             pipe */
1089
 
    TEMP_FAILURE_RETRY(close(plugin_fd));
 
1129
    close(pipefd[1]);           /* Close unused write end of pipe */
 
1130
    close(plugin_fd);
1090
1131
    plugin *new_plugin = getplugin(direntries[i]->d_name);
1091
1132
    if(new_plugin == NULL){
1092
1133
      error(0, errno, "getplugin");
1104
1145
    
1105
1146
    new_plugin->pid = pid;
1106
1147
    new_plugin->fd = pipefd[0];
1107
 
    
 
1148
 
 
1149
    if(debug){
 
1150
      fprintf(stderr, "Plugin %s started (PID %" PRIdMAX ")\n",
 
1151
              new_plugin->name, (intmax_t) (new_plugin->pid));
 
1152
    }
 
1153
 
1108
1154
    /* Unblock SIGCHLD so signal handler can be run if this process
1109
1155
       has already completed */
1110
1156
    ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
1116
1162
      goto fallback;
1117
1163
    }
1118
1164
    
1119
 
#if defined (__GNUC__) and defined (__GLIBC__)
1120
 
#if not __GLIBC_PREREQ(2, 16)
1121
 
#pragma GCC diagnostic push
1122
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1123
 
#endif
1124
 
#endif
1125
 
    FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from
1126
 
                                          -Wconversion in GNU libc
1127
 
                                          before 2.16 */
1128
 
#if defined (__GNUC__) and defined (__GLIBC__)
1129
 
#if not __GLIBC_PREREQ(2, 16)
1130
 
#pragma GCC diagnostic pop
1131
 
#endif
1132
 
#endif
 
1165
    FD_SET(new_plugin->fd, &rfds_all);
1133
1166
    
1134
1167
    if(maxfd < new_plugin->fd){
1135
1168
      maxfd = new_plugin->fd;
1138
1171
  
1139
1172
  free(direntries);
1140
1173
  direntries = NULL;
1141
 
  TEMP_FAILURE_RETRY(close(dir_fd));
 
1174
  close(dir_fd);
1142
1175
  dir_fd = -1;
1143
1176
  free_plugin(getplugin(NULL));
1144
1177
  
1184
1217
                      (intmax_t) (proc->pid),
1185
1218
                      WTERMSIG(proc->status),
1186
1219
                      strsignal(WTERMSIG(proc->status)));
1187
 
            } else if(WCOREDUMP(proc->status)){
1188
 
              fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
1189
 
                      " core\n", proc->name, (intmax_t) (proc->pid));
1190
1220
            }
1191
1221
          }
1192
1222
          
1193
1223
          /* Remove the plugin */
1194
 
#if defined (__GNUC__) and defined (__GLIBC__)
1195
 
#if not __GLIBC_PREREQ(2, 16)
1196
 
#pragma GCC diagnostic push
1197
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1198
 
#endif
1199
 
#endif
1200
 
          FD_CLR(proc->fd, &rfds_all); /* Spurious warning from
1201
 
                                          -Wconversion in GNU libc
1202
 
                                          before 2.16 */
1203
 
#if defined (__GNUC__) and defined (__GLIBC__)
1204
 
#if not __GLIBC_PREREQ(2, 16)
1205
 
#pragma GCC diagnostic pop
1206
 
#endif
1207
 
#endif
 
1224
          FD_CLR(proc->fd, &rfds_all);
1208
1225
          
1209
1226
          /* Block signal while modifying process_list */
1210
1227
          ret = (int)TEMP_FAILURE_RETRY(sigprocmask
1250
1267
      }
1251
1268
      
1252
1269
      /* This process has not completed.  Does it have any output? */
1253
 
#if defined (__GNUC__) and defined (__GLIBC__)
1254
 
#if not __GLIBC_PREREQ(2, 16)
1255
 
#pragma GCC diagnostic push
1256
 
#pragma GCC diagnostic ignored "-Wsign-conversion"
1257
 
#endif
1258
 
#endif
1259
 
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* Spurious
1260
 
                                                         warning from
1261
 
                                                         -Wconversion
1262
 
                                                         in GNU libc
1263
 
                                                         before
1264
 
                                                         2.16 */
1265
 
#if defined (__GNUC__) and defined (__GLIBC__)
1266
 
#if not __GLIBC_PREREQ(2, 16)
1267
 
#pragma GCC diagnostic pop
1268
 
#endif
1269
 
#endif
 
1270
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
1270
1271
        /* This process had nothing to say at this time */
1271
1272
        proc = proc->next;
1272
1273
        continue;
1342
1343
  free(direntries);
1343
1344
  
1344
1345
  if(dir_fd != -1){
1345
 
    TEMP_FAILURE_RETRY(close(dir_fd));
 
1346
    close(dir_fd);
1346
1347
  }
1347
1348
  
1348
1349
  /* Kill the processes */