/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 plugbasedclient.c

  • Committer: Björn Påhlsson
  • Date: 2008-07-20 02:52:20 UTC
  • Revision ID: belorn@braxen-20080720025220-r5u0388uy9iu23h6
Added following support:
Pluginbased client handler
rewritten Mandos client
       Avahi instead of udp server discovery
       openpgp encrypted key support
Passprompt stand alone application for direct console input
Added logging for Mandos server

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*  -*- coding: utf-8 -*- */
2
 
/*
3
 
 * Mandos plugin runner - Run Mandos plugins
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
13
 
 * WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 
 * General Public License for more details.
16
 
 * 
17
 
 * You should have received a copy of the GNU General Public License
18
 
 * along with this program.  If not, see
19
 
 * <http://www.gnu.org/licenses/>.
20
 
 * 
21
 
 * Contact the authors at <mandos@fukt.bsnet.se>.
22
 
 */
23
 
 
24
 
#define _GNU_SOURCE             /* TEMP_FAILURE_RETRY() */
25
 
 
26
 
#include <stdio.h>              /* popen(), fileno(), fprintf(),
27
 
                                   stderr, STDOUT_FILENO */
28
 
#include <iso646.h>             /* and, or, not */
29
 
#include <sys/types.h>          /* DIR, opendir(), stat(),
30
 
                                   struct stat, waitpid(),
31
 
                                   WIFEXITED(), WEXITSTATUS(),
32
 
                                   wait() */
33
 
#include <sys/wait.h>           /* wait() */
34
 
#include <dirent.h>             /* DIR, struct dirent, opendir(),
35
 
                                   readdir(), closedir() */
36
 
#include <sys/stat.h>           /* struct stat, stat(), S_ISREG() */
37
 
#include <unistd.h>             /* struct stat, stat(), S_ISREG(),
38
 
                                   fcntl() */
39
 
#include <fcntl.h>              /* fcntl() */
40
 
#include <stddef.h>             /* NULL */
41
 
#include <stdlib.h>             /* EXIT_FAILURE */
42
 
#include <sys/select.h>         /* fd_set, select(), FD_ZERO(),
43
 
                                   FD_SET(), FD_ISSET() */
44
 
#include <string.h>             /* strlen(), strcpy(), strcat() */
45
 
#include <stdbool.h>            /* true */
46
 
#include <sys/wait.h>           /* waitpid(), WIFEXITED(),
47
 
                                   WEXITSTATUS() */
48
 
#include <errno.h>              /* errno */
49
 
#include <argp.h>               /* struct argp_option,
50
 
                                   struct argp_state, struct argp,
51
 
                                   argp_parse() */
 
1
#include <stdio.h>      /* popen, fileno */
 
2
#include <iso646.h>     /* and, or, not */
 
3
#include <sys/types.h>  /* DIR, opendir, stat, struct stat, waitpid,
 
4
                           WIFEXITED, WEXITSTATUS, wait */
 
5
#include <sys/wait.h>   /* wait */
 
6
#include <dirent.h>     /* DIR, opendir */
 
7
#include <sys/stat.h>   /* stat, struct stat */
 
8
#include <unistd.h>     /* stat, struct stat, chdir */
 
9
#include <stdlib.h>     /* EXIT_FAILURE */
 
10
#include <sys/select.h> /* fd_set, select, FD_ZERO, FD_SET, FD_ISSET */
 
11
#include <string.h>     /* strlen, strcpy, strcat */
 
12
#include <stdbool.h>    /* true */
 
13
#include <sys/wait.h>   /* waitpid, WIFEXITED, WEXITSTATUS */
 
14
#include <errno.h>      /* errno */
52
15
 
53
16
struct process;
54
17
 
56
19
  pid_t pid;
57
20
  int fd;
58
21
  char *buffer;
59
 
  size_t buffer_size;
60
 
  size_t buffer_length;
 
22
  int buffer_size;
 
23
  int buffer_length;
61
24
  struct process *next;
62
25
} process;
63
26
 
64
 
typedef struct plugin{
65
 
  char *name;                   /* can be NULL or any plugin name */
66
 
  char **argv;
67
 
  int argc;
68
 
  bool disabled;
69
 
  struct plugin *next;
70
 
} plugin;
71
 
 
72
 
plugin *getplugin(char *name, plugin **plugin_list){
73
 
  for (plugin *p = *plugin_list; p != NULL; p = p->next){
74
 
    if ((p->name == name)
75
 
        or (p->name and name and (strcmp(p->name, name) == 0))){
76
 
      return p;
77
 
    }
78
 
  }
79
 
  /* Create a new plugin */
80
 
  plugin *new_plugin = malloc(sizeof(plugin));
81
 
  if (new_plugin == NULL){
82
 
    perror("malloc");
83
 
    exit(EXIT_FAILURE);
84
 
  }
85
 
  new_plugin->name = name;
86
 
  new_plugin->argv = malloc(sizeof(char *) * 2);
87
 
  if (new_plugin->argv == NULL){
88
 
    perror("malloc");
89
 
    exit(EXIT_FAILURE);
90
 
  }
91
 
  new_plugin->argv[0] = name;
92
 
  new_plugin->argv[1] = NULL;
93
 
  new_plugin->argc = 1;
94
 
  new_plugin->disabled = false;
95
 
  new_plugin->next = *plugin_list;
96
 
  /* Append the new plugin to the list */
97
 
  *plugin_list = new_plugin;
98
 
  return new_plugin;
99
 
}
100
 
 
101
 
void addargument(plugin *p, char *arg){
102
 
  p->argv[p->argc] = arg;
103
 
  p->argv = realloc(p->argv, sizeof(char *) * (size_t)(p->argc + 2));
104
 
  if (p->argv == NULL){
105
 
    perror("malloc");
106
 
    exit(EXIT_FAILURE);
107
 
  }
108
 
  p->argc++;
109
 
  p->argv[p->argc] = NULL;
110
 
}
111
 
 
112
 
/*
113
 
 * Based on the example in the GNU LibC manual chapter 13.13 "File
114
 
 * Descriptor Flags".
115
 
 * *Note File Descriptor Flags:(libc)Descriptor Flags.
116
 
 */
117
 
int set_cloexec_flag(int fd)
118
 
{
119
 
  int ret = fcntl(fd, F_GETFD, 0);
120
 
  /* If reading the flags failed, return error indication now. */
121
 
  if(ret < 0){
122
 
    return ret;
123
 
  }
124
 
  /* Store modified flag word in the descriptor. */
125
 
  return fcntl(fd, F_SETFD, ret | FD_CLOEXEC);
126
 
}
127
 
 
128
 
 
129
27
#define BUFFER_SIZE 256
130
28
 
131
 
const char *argp_program_version =
132
 
  "plugbasedclient 0.9";
133
 
const char *argp_program_bug_address =
134
 
  "<mandos@fukt.bsnet.se>";
135
 
 
136
29
int main(int argc, char *argv[]){
137
 
  const char *plugindir = "/conf/conf.d/mandos/plugins.d";
138
 
  size_t d_name_len;
 
30
  char plugindir[] = "plugins.d";
 
31
  size_t d_name_len, plugindir_len = sizeof(plugindir)-1;
139
32
  DIR *dir;
140
33
  struct dirent *dirst;
141
34
  struct stat st;
142
 
  fd_set rfds_all;
 
35
  fd_set rfds_orig;
143
36
  int ret, maxfd = 0;
144
37
  process *process_list = NULL;
145
 
  bool debug = false;
146
 
  int exitstatus = EXIT_SUCCESS;
147
 
  
148
 
  /* The options we understand. */
149
 
  struct argp_option options[] = {
150
 
    { .name = "global-options", .key = 'g',
151
 
      .arg = "OPTION[,OPTION[,...]]",
152
 
      .doc = "Options passed to all plugins" },
153
 
    { .name = "options-for", .key = 'o',
154
 
      .arg = "PLUGIN:OPTION[,OPTION[,...]]",
155
 
      .doc = "Options passed only to specified plugin" },
156
 
    { .name = "disable", .key = 'd',
157
 
      .arg = "PLUGIN",
158
 
      .doc = "Disable a specific plugin", .group = 1 },
159
 
    { .name = "plugin-dir", .key = 128,
160
 
      .arg = "DIRECTORY",
161
 
      .doc = "Specify a different plugin directory", .group = 2 },
162
 
    { .name = "debug", .key = 129,
163
 
      .doc = "Debug mode", .group = 3 },
164
 
    { .name = NULL }
165
 
  };
166
 
  
167
 
  error_t parse_opt (int key, char *arg, struct argp_state *state) {
168
 
       /* Get the INPUT argument from `argp_parse', which we
169
 
          know is a pointer to our plugin list pointer. */
170
 
    plugin **plugins = state->input;
171
 
    switch (key) {
172
 
    case 'g':
173
 
      if (arg != NULL){
174
 
        char *p = strtok(arg, ",");
175
 
        do{
176
 
          addargument(getplugin(NULL, plugins), p);
177
 
          p = strtok(NULL, ",");
178
 
        } while (p);
179
 
      }
180
 
      break;
181
 
    case 'o':
182
 
      if (arg != NULL){
183
 
        char *name = strtok(arg, ":");
184
 
        char *p = strtok(NULL, ":");
185
 
        if(p){
186
 
          p = strtok(p, ",");
187
 
          do{
188
 
            addargument(getplugin(name, plugins), p);
189
 
            p = strtok(NULL, ",");
190
 
          } while (p);
191
 
        }
192
 
      }
193
 
      break;
194
 
    case 'd':
195
 
      if (arg != NULL){
196
 
        getplugin(arg, plugins)->disabled = true;
197
 
      }
198
 
      break;
199
 
    case 128:
200
 
      plugindir = arg;
201
 
      break;
202
 
    case 129:
203
 
      debug = true;
204
 
      break;
205
 
    case ARGP_KEY_ARG:
206
 
      argp_usage (state);
207
 
      break;
208
 
    case ARGP_KEY_END:
209
 
      break;
210
 
    default:
211
 
      return ARGP_ERR_UNKNOWN;
212
 
    }
213
 
    return 0;
214
 
  }
215
 
  
216
 
  plugin *plugin_list = NULL;
217
 
  
218
 
  struct argp argp = { .options = options, .parser = parse_opt,
219
 
                       .args_doc = "",
220
 
                       .doc = "Mandos plugin runner -- Run plugins" };
221
 
  
222
 
  argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
223
 
  
224
 
  if(debug){
225
 
    for(plugin *p = plugin_list; p != NULL; p=p->next){
226
 
      fprintf(stderr, "Plugin: %s has %d arguments\n",
227
 
              p->name ? p->name : "Global", p->argc - 1);
228
 
      for(char **a = p->argv; *a != NULL; a++){
229
 
        fprintf(stderr, "\tArg: %s\n", *a);
230
 
      }
231
 
    }
232
 
  }
233
38
  
234
39
  dir = opendir(plugindir);
235
 
  if(dir == NULL){
236
 
    perror("Could not open plugin dir");
237
 
    exitstatus = EXIT_FAILURE;
238
 
    goto end;
239
 
  }
240
 
  /* Set the FD_CLOEXEC flag on the directory, if possible */
241
 
  {
242
 
    int dir_fd = dirfd(dir);
243
 
    if(dir_fd >= 0){
244
 
      ret = set_cloexec_flag(dir_fd);
245
 
      if(ret < 0){
246
 
        perror("set_cloexec_flag");
247
 
        exitstatus = EXIT_FAILURE;
248
 
        goto end;
249
 
      }
250
 
    }
251
 
  }
252
 
  
 
40
 
253
41
  if(dir == NULL){
254
42
    fprintf(stderr, "Can not open directory\n");
255
43
    return EXIT_FAILURE;
256
44
  }
257
45
  
258
 
  FD_ZERO(&rfds_all);
 
46
  FD_ZERO(&rfds_orig);
259
47
  
260
48
  while(true){
261
49
    dirst = readdir(dir);
267
55
    
268
56
    d_name_len = strlen(dirst->d_name);
269
57
    
270
 
    // Ignore dotfiles, backup files and other junk
271
 
    {
272
 
      bool bad_name = false;
273
 
      
274
 
      const char const *bad_prefixes[] = { ".", "#", NULL };
275
 
      
276
 
      const char const *bad_suffixes[] = { "~", "#", ".dpkg-new",
277
 
                                           ".dpkg-old",
278
 
                                           ".dpkg-divert", NULL };
279
 
      for(const char **pre = bad_prefixes; *pre != NULL; pre++){
280
 
        size_t pre_len = strlen(*pre);
281
 
        if((d_name_len >= pre_len)
282
 
           and strncmp((dirst->d_name), *pre, pre_len) == 0){
283
 
          if(debug){
284
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
285
 
                    " with bad prefix %s\n", dirst->d_name, *pre);
286
 
          }
287
 
          bad_name = true;
288
 
          break;
289
 
        }
290
 
      }
291
 
      
292
 
      if(bad_name){
293
 
        continue;
294
 
      }
295
 
      
296
 
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
297
 
        size_t suf_len = strlen(*suf);
298
 
        if((d_name_len >= suf_len)
299
 
           and (strcmp((dirst->d_name)+d_name_len-suf_len, *suf)
300
 
                == 0)){
301
 
          if(debug){
302
 
            fprintf(stderr, "Ignoring plugin dir entry \"%s\""
303
 
                    " with bad suffix %s\n", dirst->d_name, *suf);
304
 
          }
305
 
          bad_name = true;
306
 
          break;
307
 
        }
308
 
      }
309
 
      
310
 
      if(bad_name){
311
 
        continue;
312
 
      }
 
58
    // Ignore dotfiles and backup files
 
59
    if (dirst->d_name[0] == '.'
 
60
        or dirst->d_name[d_name_len - 1] == '~'){
 
61
      continue;
313
62
    }
314
63
    
315
 
    char *filename = malloc(d_name_len + strlen(plugindir) + 2);
316
 
    if (filename == NULL){
317
 
      perror("malloc");
318
 
      exitstatus = EXIT_FAILURE;
319
 
      goto end;
320
 
    }
 
64
    char *filename = malloc(d_name_len + plugindir_len + 1);
321
65
    strcpy(filename, plugindir);
322
66
    strcat(filename, "/");
323
67
    strcat(filename, dirst->d_name);    
324
 
 
 
68
    
325
69
    stat(filename, &st);
326
70
 
327
 
    if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
328
 
      if(debug){
329
 
        fprintf(stderr, "Ignoring plugin dir entry \"%s\""
330
 
                " with bad type or mode\n", filename);
331
 
      }
332
 
      continue;
333
 
    }
334
 
    if(getplugin(dirst->d_name, &plugin_list)->disabled){
335
 
      if(debug){
336
 
        fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
337
 
                dirst->d_name);
338
 
      }
339
 
      continue;
340
 
    }
341
 
    plugin *p = getplugin(dirst->d_name, &plugin_list);
342
 
    {
343
 
      /* Add global arguments to argument list for this plugin */
344
 
      plugin *g = getplugin(NULL, &plugin_list);
345
 
      for(char **a = g->argv + 1; *a != NULL; a++){
346
 
        addargument(p, *a);
347
 
      }
348
 
    }
349
 
    int pipefd[2]; 
350
 
    ret = pipe(pipefd);
351
 
    if (ret == -1){
352
 
      perror("pipe");
353
 
      exitstatus = EXIT_FAILURE;
354
 
      goto end;
355
 
    }
356
 
    ret = set_cloexec_flag(pipefd[0]);
357
 
    if(ret < 0){
358
 
      perror("set_cloexec_flag");
359
 
      exitstatus = EXIT_FAILURE;
360
 
      goto end;
361
 
    }
362
 
    ret = set_cloexec_flag(pipefd[1]);
363
 
    if(ret < 0){
364
 
      perror("set_cloexec_flag");
365
 
      exitstatus = EXIT_FAILURE;
366
 
      goto end;
367
 
    }
368
 
    // Starting a new process to be watched
369
 
    pid_t pid = fork();
370
 
    if(pid == 0){
371
 
      /* this is the child process */
372
 
      dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
373
 
      
374
 
      if(dirfd(dir) < 0){
375
 
        /* If dir has no file descriptor, we could not set FD_CLOEXEC
376
 
           and must close it manually  */
 
71
    if (S_ISREG(st.st_mode) and (access(filename, X_OK) == 0)){
 
72
      // Starting a new process to be watched
 
73
      process *new_process = malloc(sizeof(process));
 
74
      int pipefd[2];
 
75
      pipe(pipefd);
 
76
      new_process->pid = fork();
 
77
      if(new_process->pid == 0){
 
78
        /* this is the child process */
377
79
        closedir(dir);
378
 
      }
379
 
      if(execv(filename, p->argv) < 0){
380
 
        perror("execv");
381
 
        _exit(EXIT_FAILURE);
382
 
      }
383
 
      /* no return */
384
 
    }
385
 
    /* parent process */
386
 
    close(pipefd[1]);           /* close unused write end of pipe */
387
 
    process *new_process = malloc(sizeof(process));
388
 
    if (new_process == NULL){
389
 
      perror("malloc");
390
 
      exitstatus = EXIT_FAILURE;
391
 
      goto end;
392
 
    }
393
 
    
394
 
    *new_process = (struct process){ .pid = pid,
395
 
                                     .fd = pipefd[0],
396
 
                                     .next = process_list };
397
 
    FD_SET(new_process->fd, &rfds_all);
398
 
    
399
 
    if (maxfd < new_process->fd){
400
 
      maxfd = new_process->fd;
401
 
    }
402
 
    
403
 
    //List handling
404
 
    process_list = new_process;
405
 
  }
406
 
  
407
 
  /* Free the plugin list */
408
 
  for(plugin *next; plugin_list != NULL; plugin_list = next){
409
 
    next = plugin_list->next;
410
 
    free(plugin_list->argv);
411
 
    free(plugin_list);
 
80
        close(pipefd[0]);       /* close unused read end of pipe */
 
81
        dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
 
82
        /* create a new modified argument list */
 
83
        char **new_argv = malloc(sizeof(char *) * argc + 1);
 
84
        new_argv[0] = filename;
 
85
        for(int i = 1; i < argc; i++){
 
86
          new_argv[i] = argv[i];
 
87
        }
 
88
        new_argv[argc] = NULL;
 
89
        if(execv(filename, new_argv) < 0){
 
90
          perror(argv[0]);
 
91
          close(pipefd[1]);
 
92
          exit(EXIT_FAILURE);
 
93
        }
 
94
        /* no return */
 
95
      }
 
96
      close(pipefd[1]);         /* close unused write end of pipe */
 
97
      new_process->fd = pipefd[0];
 
98
      new_process->buffer = malloc(BUFFER_SIZE);
 
99
      if (new_process->buffer == NULL){
 
100
        perror(argv[0]);
 
101
        goto end;
 
102
      }
 
103
      new_process->buffer_size = BUFFER_SIZE;
 
104
      new_process->buffer_length = 0;
 
105
      FD_SET(new_process->fd, &rfds_orig);
 
106
      
 
107
      if (maxfd < new_process->fd){
 
108
        maxfd = new_process->fd;
 
109
      }
 
110
      
 
111
      //List handling
 
112
      new_process->next = process_list;
 
113
      process_list = new_process;
 
114
    }
412
115
  }
413
116
  
414
117
  closedir(dir);
415
118
  
416
 
  if (process_list == NULL){
417
 
    fprintf(stderr, "No plugin processes started, exiting\n");
418
 
    return EXIT_FAILURE;
419
 
  }
420
 
  while(true){
421
 
    fd_set rfds = rfds_all;
422
 
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
423
 
    if (select_ret == -1){
424
 
      perror("select");
425
 
      exitstatus = EXIT_FAILURE;
426
 
      goto end;
427
 
    }
428
 
    for(process *proc = process_list; proc ; proc = proc->next){
429
 
      if(not FD_ISSET(proc->fd, &rfds)){
430
 
        continue;
431
 
      }
432
 
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
433
 
        proc->buffer = realloc(proc->buffer, proc->buffer_size
434
 
                               + (size_t) BUFFER_SIZE);
435
 
        if (proc->buffer == NULL){
436
 
          perror("malloc");
437
 
          exitstatus = EXIT_FAILURE;
438
 
          goto end;
439
 
        }
440
 
        proc->buffer_size += BUFFER_SIZE;
441
 
      }
442
 
      ret = read(proc->fd, proc->buffer + proc->buffer_length,
443
 
                 BUFFER_SIZE);
444
 
      if(ret < 0){
445
 
        /* Read error from this process; ignore it */
446
 
        continue;
447
 
      }
448
 
      proc->buffer_length += (size_t) ret;
449
 
      if(ret == 0){
450
 
        /* got EOF */
451
 
        /* wait for process exit */
452
 
        int status;
453
 
        waitpid(proc->pid, &status, 0);
454
 
        if(not WIFEXITED(status) or WEXITSTATUS(status) != 0){
455
 
          FD_CLR(proc->fd, &rfds_all);
456
 
          continue;
457
 
        }
458
 
        for(size_t written = 0;
459
 
            written < proc->buffer_length; written += (size_t)ret){
460
 
          ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO,
461
 
                                         proc->buffer + written,
462
 
                                         proc->buffer_length
463
 
                                         - written));
464
 
          if(ret < 0){
465
 
            perror("write");
466
 
            exitstatus = EXIT_FAILURE;
467
 
            goto end;
 
119
  if (process_list != NULL){
 
120
    while(true){
 
121
      fd_set rfds = rfds_orig;
 
122
      int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
 
123
      if (select_ret == -1){
 
124
        perror(argv[0]);
 
125
        goto end;
 
126
      }else{    
 
127
        for(process *process_itr = process_list; process_itr != NULL;
 
128
            process_itr = process_itr->next){
 
129
          if(FD_ISSET(process_itr->fd, &rfds)){
 
130
            if(process_itr->buffer_length + BUFFER_SIZE
 
131
               > process_itr->buffer_size){
 
132
                process_itr->buffer = realloc(process_itr->buffer,
 
133
                                              process_itr->buffer_size
 
134
                                              + BUFFER_SIZE);
 
135
                if (process_itr->buffer == NULL){
 
136
                  perror(argv[0]);
 
137
                  goto end;
 
138
                }
 
139
                process_itr->buffer_size += BUFFER_SIZE;
 
140
            }
 
141
            ret = read(process_itr->fd, process_itr->buffer
 
142
                       + process_itr->buffer_length, BUFFER_SIZE);
 
143
            process_itr->buffer_length+=ret;
 
144
            if(ret == 0){
 
145
              /* got EOF */
 
146
              /* wait for process exit */
 
147
              int status;
 
148
              waitpid(process_itr->pid, &status, 0);
 
149
              if(WIFEXITED(status) and WEXITSTATUS(status) == 0){
 
150
                write(STDOUT_FILENO, process_itr->buffer,
 
151
                      process_itr->buffer_length);
 
152
                goto end;
 
153
              } else {
 
154
                FD_CLR(process_itr->fd, &rfds_orig);
 
155
              }
 
156
            }
468
157
          }
469
158
        }
470
 
        goto end;
471
159
      }
472
160
    }
473
161
  }
474
162
  
475
163
 end:
476
 
  for(process *next; process_list != NULL; process_list = next){
477
 
    close(process_list->fd);
478
 
    kill(process_list->pid, SIGTERM);
479
 
    free(process_list->buffer);
480
 
    free(process_list);
 
164
  for(process *process_itr = process_list; process_itr != NULL;
 
165
      process_itr = process_itr->next){
 
166
    close(process_itr->fd);
 
167
    kill(process_itr->pid, SIGTERM);
 
168
    free(process_itr->buffer);
481
169
  }
482
170
  
483
171
  while(true){
490
178
      break;
491
179
    }
492
180
  }  
493
 
  return exitstatus;
 
181
  return EXIT_SUCCESS;
494
182
}