/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
1
/*  -*- coding: utf-8 -*- */
2
/*
3
 * Mandos plugin runner - Run Mandos plugins
4
 *
28 by Teddy Hogeborn
* server.conf: New file.
5
 * Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
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
 * 
28 by Teddy Hogeborn
* server.conf: New file.
21
 * Contact the authors at <mandos@fukt.bsnet.se>.
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
22
 */
23
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
24
#define _GNU_SOURCE		/* TEMP_FAILURE_RETRY() */
25
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
26
#include <stdio.h>		/* popen(), fileno(), fprintf(),
27
				   stderr, STDOUT_FILENO */
28
#include <iso646.h>		/* and, or, not */
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
29
#include <sys/types.h>	        /* DIR, opendir(), stat(),
30
				   struct stat, waitpid(),
31
				   WIFEXITED(), WEXITSTATUS(),
32
				   wait() */
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
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() */
13 by Björn Påhlsson
Added following support:
52
37 by Teddy Hogeborn
Non-tested commit for merge purposes.
53
#define BUFFER_SIZE 256
54
13 by Björn Påhlsson
Added following support:
55
struct process;
56
57
typedef struct process{
58
  pid_t pid;
59
  int fd;
60
  char *buffer;
21 by Teddy Hogeborn
* Makefile (CFLAGS): Changed to use $(WARN), $(DEBUG), $(COVERAGE) and
61
  size_t buffer_size;
62
  size_t buffer_length;
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
63
  bool eof;
64
  bool completed;
65
  int status;
13 by Björn Påhlsson
Added following support:
66
  struct process *next;
67
} process;
68
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
69
typedef struct plugin{
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
70
  char *name;			/* can be NULL or any plugin name */
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
71
  char **argv;
72
  int argc;
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
73
  bool disabled;
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
74
  struct plugin *next;
75
} plugin;
76
37 by Teddy Hogeborn
Non-tested commit for merge purposes.
77
static plugin *getplugin(char *name, plugin **plugin_list){
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
78
  for (plugin *p = *plugin_list; p != NULL; p = p->next){
79
    if ((p->name == name)
80
	or (p->name and name and (strcmp(p->name, name) == 0))){
81
      return p;
82
    }
83
  }
84
  /* Create a new plugin */
85
  plugin *new_plugin = malloc(sizeof(plugin));
86
  if (new_plugin == NULL){
87
    perror("malloc");
88
    exit(EXIT_FAILURE);
89
  }
90
  new_plugin->name = name;
91
  new_plugin->argv = malloc(sizeof(char *) * 2);
92
  if (new_plugin->argv == NULL){
93
    perror("malloc");
94
    exit(EXIT_FAILURE);
95
  }
96
  new_plugin->argv[0] = name;
97
  new_plugin->argv[1] = NULL;
98
  new_plugin->argc = 1;
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
99
  new_plugin->disabled = false;
24.1.5 by Björn Påhlsson
plugbasedclient:
100
  new_plugin->next = *plugin_list;
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
101
  /* Append the new plugin to the list */
102
  *plugin_list = new_plugin;
103
  return new_plugin;
104
}
105
37 by Teddy Hogeborn
Non-tested commit for merge purposes.
106
static void addargument(plugin *p, char *arg){
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
107
  p->argv[p->argc] = arg;
108
  p->argv = realloc(p->argv, sizeof(char *) * (size_t)(p->argc + 2));
109
  if (p->argv == NULL){
110
    perror("malloc");
111
    exit(EXIT_FAILURE);
112
  }
113
  p->argc++;
114
  p->argv[p->argc] = NULL;
115
}
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
116
32 by Teddy Hogeborn
* plugins.d/plugbasedclient.c (set_cloexec_flag): New function.
117
/*
118
 * Based on the example in the GNU LibC manual chapter 13.13 "File
119
 * Descriptor Flags".
120
 * *Note File Descriptor Flags:(libc)Descriptor Flags.
121
 */
37 by Teddy Hogeborn
Non-tested commit for merge purposes.
122
static int set_cloexec_flag(int fd)
32 by Teddy Hogeborn
* plugins.d/plugbasedclient.c (set_cloexec_flag): New function.
123
{
124
  int ret = fcntl(fd, F_GETFD, 0);
125
  /* If reading the flags failed, return error indication now. */
126
  if(ret < 0){
127
    return ret;
128
  }
129
  /* Store modified flag word in the descriptor. */
130
  return fcntl(fd, F_SETFD, ret | FD_CLOEXEC);
131
}
132
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
133
const char *argp_program_version = "plugbasedclient 0.9";
134
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
135
136
process *process_list = NULL;
137
138
/* Mark a process as completed when it exits, and save its exit
139
   status. */
140
void handle_sigchld(__attribute__((unused)) int sig){
141
  process *proc = process_list;
142
  int status;
143
  pid_t pid = wait(&status);
144
  while(proc != NULL and proc->pid != pid){
24.1.8 by Björn Påhlsson
plugbasedclient
145
    proc = proc->next;
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
146
  }
147
  if(proc == NULL){
148
    /* Process not found in process list */
149
    return;
150
  }
151
  proc->status = status;
152
  proc->completed = true;
153
}
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
154
13 by Björn Påhlsson
Added following support:
155
int main(int argc, char *argv[]){
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
156
  const char *plugindir = "/conf/conf.d/mandos/plugins.d";
24.1.5 by Björn Påhlsson
plugbasedclient:
157
  size_t d_name_len;
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
158
  DIR *dir = NULL;
13 by Björn Påhlsson
Added following support:
159
  struct dirent *dirst;
160
  struct stat st;
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
161
  fd_set rfds_all;
13 by Björn Påhlsson
Added following support:
162
  int ret, maxfd = 0;
24.1.6 by Björn Påhlsson
plugbasedclient
163
  uid_t uid = 65534;
164
  gid_t gid = 65534;
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
165
  bool debug = false;
166
  int exitstatus = EXIT_SUCCESS;
24.1.7 by Björn Påhlsson
merge
167
  struct sigaction old_sigchld_action;
168
  struct sigaction sigchld_action = { .sa_handler = handle_sigchld,
169
				      .sa_flags = SA_NOCLDSTOP };
24.1.8 by Björn Påhlsson
plugbasedclient
170
  char *plus_options = NULL;
171
  char **plus_argv = NULL;
172
  
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
173
  /* Establish a signal handler */
174
  sigemptyset(&sigchld_action.sa_mask);
175
  ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
176
  if(ret < 0){
177
    perror("sigaddset");
178
    exit(EXIT_FAILURE);
179
  }
180
  ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
181
  if(ret < 0){
182
    perror("sigaction");
183
    exit(EXIT_FAILURE);
184
  }
185
  
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
186
  /* The options we understand. */
187
  struct argp_option options[] = {
188
    { .name = "global-options", .key = 'g',
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
189
      .arg = "OPTION[,OPTION[,...]]",
190
      .doc = "Options passed to all plugins" },
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
191
    { .name = "options-for", .key = 'o',
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
192
      .arg = "PLUGIN:OPTION[,OPTION[,...]]",
193
      .doc = "Options passed only to specified plugin" },
194
    { .name = "disable", .key = 'd',
195
      .arg = "PLUGIN",
196
      .doc = "Disable a specific plugin", .group = 1 },
24.1.5 by Björn Påhlsson
plugbasedclient:
197
    { .name = "plugin-dir", .key = 128,
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
198
      .arg = "DIRECTORY",
199
      .doc = "Specify a different plugin directory", .group = 2 },
24.1.6 by Björn Påhlsson
plugbasedclient
200
    { .name = "userid", .key = 129,
24.1.7 by Björn Påhlsson
merge
201
      .arg = "ID", .flags = 0,
202
      .doc = "User ID the plugins will run as", .group = 2 },
24.1.6 by Björn Påhlsson
plugbasedclient
203
    { .name = "groupid", .key = 130,
24.1.7 by Björn Påhlsson
merge
204
      .arg = "ID", .flags = 0,
205
      .doc = "Group ID the plugins will run as", .group = 2 },
206
    { .name = "debug", .key = 131,
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
207
      .doc = "Debug mode", .group = 3 },
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
208
    { .name = NULL }
209
  };
210
  
211
  error_t parse_opt (int key, char *arg, struct argp_state *state) {
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
212
    /* Get the INPUT argument from `argp_parse', which we know is a
213
       pointer to our plugin list pointer. */
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
214
    plugin **plugins = state->input;
215
    switch (key) {
216
    case 'g':
217
      if (arg != NULL){
218
	char *p = strtok(arg, ",");
219
	do{
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
220
	  addargument(getplugin(NULL, plugins), p);
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
221
	  p = strtok(NULL, ",");
24.1.8 by Björn Påhlsson
plugbasedclient
222
	} while (p != NULL);
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
223
      }
224
      break;
225
    case 'o':
226
      if (arg != NULL){
227
	char *name = strtok(arg, ":");
228
	char *p = strtok(NULL, ":");
24.1.8 by Björn Påhlsson
plugbasedclient
229
	if(p != NULL){
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
230
	  p = strtok(p, ",");
231
	  do{
232
	    addargument(getplugin(name, plugins), p);
233
	    p = strtok(NULL, ",");
24.1.8 by Björn Påhlsson
plugbasedclient
234
	  } while (p != NULL);
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
235
	}
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
236
      }
237
      break;
24.1.5 by Björn Påhlsson
plugbasedclient:
238
    case 'd':
239
      if (arg != NULL){
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
240
	getplugin(arg, plugins)->disabled = true;
24.1.5 by Björn Påhlsson
plugbasedclient:
241
      }
242
      break;
243
    case 128:
244
      plugindir = arg;
245
      break;
24.1.6 by Björn Påhlsson
plugbasedclient
246
    case 129:
247
      uid = (uid_t)strtol(arg, NULL, 10);
248
      break;
249
    case 130:
250
      gid = (gid_t)strtol(arg, NULL, 10);
251
      break;
24.1.7 by Björn Påhlsson
merge
252
    case 131:
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
253
      debug = true;
254
      break;
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
255
    case ARGP_KEY_ARG:
24.1.8 by Björn Påhlsson
plugbasedclient
256
      if(plus_options != NULL or arg == NULL or arg[0] != '+'){
257
	argp_usage (state);
258
      }
259
      plus_options = arg;
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
260
      break;
261
    case ARGP_KEY_END:
262
      break;
263
    default:
264
      return ARGP_ERR_UNKNOWN;
265
    }
266
    return 0;
267
  }
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
268
  
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
269
  plugin *plugin_list = NULL;
270
  
271
  struct argp argp = { .options = options, .parser = parse_opt,
24.1.8 by Björn Påhlsson
plugbasedclient
272
		       .args_doc = "[+PLUS_SEPARATED_OPTIONS]",
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
273
		       .doc = "Mandos plugin runner -- Run plugins" };
274
  
24.1.7 by Björn Påhlsson
merge
275
  argp_parse (&argp, argc, argv, 0, 0, &plugin_list);  
24.1.8 by Björn Påhlsson
plugbasedclient
276
  
277
  if(plus_options){
278
    /* This is a mangled argument in the form of
279
     "+--option+--other-option=parameter+--yet-another-option", etc */
280
    /* Make new argc and argv vars, and call argp_parse() again. */
281
    plus_options++;		/* skip the first '+' character */
282
    const char delims[] = "+";
283
    char *arg;
284
    int new_argc = 1;
285
    plus_argv = malloc(sizeof(char*) * 2);
286
    if(plus_argv == NULL){
287
      perror("malloc");
288
      exitstatus = EXIT_FAILURE;
289
      goto end;
290
    }
291
    plus_argv[0] = argv[0];
292
    plus_argv[1] = NULL;
293
    arg = strtok(plus_options, delims); /* Get first argument */
294
    while(arg != NULL){
295
      new_argc++;
296
      plus_argv = realloc(plus_argv, sizeof(char *)
297
			 * ((unsigned int) new_argc + 1));
298
      if(plus_argv == NULL){
299
	perror("malloc");
300
	exitstatus = EXIT_FAILURE;
301
	goto end;
302
      }
303
      plus_argv[new_argc-1] = arg;
304
      plus_argv[new_argc] = NULL;
305
      arg = strtok(NULL, delims); /* Get next argument */
306
    }
307
    argp_parse (&argp, new_argc, plus_argv, 0, 0, &plugin_list);  
308
  }
309
  
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
310
  if(debug){
311
    for(plugin *p = plugin_list; p != NULL; p=p->next){
312
      fprintf(stderr, "Plugin: %s has %d arguments\n",
32 by Teddy Hogeborn
* plugins.d/plugbasedclient.c (set_cloexec_flag): New function.
313
	      p->name ? p->name : "Global", p->argc - 1);
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
314
      for(char **a = p->argv; *a != NULL; a++){
315
	fprintf(stderr, "\tArg: %s\n", *a);
316
      }
317
    }
318
  }
24.1.8 by Björn Påhlsson
plugbasedclient
319
  
24.1.6 by Björn Påhlsson
plugbasedclient
320
  ret = setuid(uid);
321
  if (ret == -1){
322
    perror("setuid");
323
  }
24.1.7 by Björn Påhlsson
merge
324
  
24.1.6 by Björn Påhlsson
plugbasedclient
325
  setgid(gid);
326
  if (ret == -1){
39 by Teddy Hogeborn
* plugins.d/mandosclient.c (pgp_packet_decrypt): Renamed variables.
327
    perror("setgid");
24.1.6 by Björn Påhlsson
plugbasedclient
328
  }
329
  
13 by Björn Påhlsson
Added following support:
330
  dir = opendir(plugindir);
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
331
  if(dir == NULL){
332
    perror("Could not open plugin dir");
333
    exitstatus = EXIT_FAILURE;
32 by Teddy Hogeborn
* plugins.d/plugbasedclient.c (set_cloexec_flag): New function.
334
    goto end;
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
335
  }
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
336
  
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
337
  /* Set the FD_CLOEXEC flag on the directory, if possible */
338
  {
339
    int dir_fd = dirfd(dir);
340
    if(dir_fd >= 0){
341
      ret = set_cloexec_flag(dir_fd);
342
      if(ret < 0){
343
	perror("set_cloexec_flag");
344
	exitstatus = EXIT_FAILURE;
345
	goto end;
346
      }
347
    }
348
  }
24.1.1 by Björn Påhlsson
Added syntax and support for plugbasedclient arguments and how they
349
  
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
350
  FD_ZERO(&rfds_all);
13 by Björn Påhlsson
Added following support:
351
  
352
  while(true){
353
    dirst = readdir(dir);
354
    
355
    // All directory entries have been processed
356
    if(dirst == NULL){
357
      break;
358
    }
359
    
360
    d_name_len = strlen(dirst->d_name);
361
    
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
362
    // Ignore dotfiles, backup files and other junk
363
    {
364
      bool bad_name = false;
365
      
366
      const char const *bad_prefixes[] = { ".", "#", NULL };
367
      
368
      const char const *bad_suffixes[] = { "~", "#", ".dpkg-new",
369
					   ".dpkg-old",
370
					   ".dpkg-divert", NULL };
371
      for(const char **pre = bad_prefixes; *pre != NULL; pre++){
372
	size_t pre_len = strlen(*pre);
373
	if((d_name_len >= pre_len)
374
	   and strncmp((dirst->d_name), *pre, pre_len) == 0){
375
	  if(debug){
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
376
	    fprintf(stderr, "Ignoring plugin dir entry \"%s\""
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
377
		    " with bad prefix %s\n", dirst->d_name, *pre);
378
	  }
379
	  bad_name = true;
380
	  break;
381
	}
382
      }
383
      
384
      if(bad_name){
385
	continue;
386
      }
387
      
388
      for(const char **suf = bad_suffixes; *suf != NULL; suf++){
389
	size_t suf_len = strlen(*suf);
390
	if((d_name_len >= suf_len)
391
	   and (strcmp((dirst->d_name)+d_name_len-suf_len, *suf)
392
		== 0)){
393
	  if(debug){
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
394
	    fprintf(stderr, "Ignoring plugin dir entry \"%s\""
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
395
		    " with bad suffix %s\n", dirst->d_name, *suf);
396
	  }
397
	  bad_name = true;
398
	  break;
399
	}
400
      }
401
      
402
      if(bad_name){
403
	continue;
404
      }
13 by Björn Påhlsson
Added following support:
405
    }
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
406
    
24.1.5 by Björn Påhlsson
plugbasedclient:
407
    char *filename = malloc(d_name_len + strlen(plugindir) + 2);
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
408
    if (filename == NULL){
409
      perror("malloc");
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
410
      exitstatus = EXIT_FAILURE;
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
411
      goto end;
412
    }
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
413
    strcpy(filename, plugindir); /* Spurious warning */
414
    strcat(filename, "/");	/* Spurious warning */
415
    strcat(filename, dirst->d_name); /* Spurious warning */
416
    
13 by Björn Påhlsson
Added following support:
417
    stat(filename, &st);
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
418
    
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
419
    if (not S_ISREG(st.st_mode)	or (access(filename, X_OK) != 0)){
420
      if(debug){
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
421
	fprintf(stderr, "Ignoring plugin dir entry \"%s\""
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
422
		" with bad type or mode\n", filename);
423
      }
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
424
      free(filename);
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
425
      continue;
426
    }
427
    if(getplugin(dirst->d_name, &plugin_list)->disabled){
428
      if(debug){
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
429
	fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
430
		dirst->d_name);
431
      }
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
432
      free(filename);
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
433
      continue;
434
    }
32 by Teddy Hogeborn
* plugins.d/plugbasedclient.c (set_cloexec_flag): New function.
435
    plugin *p = getplugin(dirst->d_name, &plugin_list);
436
    {
437
      /* Add global arguments to argument list for this plugin */
438
      plugin *g = getplugin(NULL, &plugin_list);
439
      for(char **a = g->argv + 1; *a != NULL; a++){
440
	addargument(p, *a);
441
      }
442
    }
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
443
    int pipefd[2]; 
444
    ret = pipe(pipefd);
445
    if (ret == -1){
446
      perror("pipe");
447
      exitstatus = EXIT_FAILURE;
448
      goto end;
449
    }
450
    ret = set_cloexec_flag(pipefd[0]);
451
    if(ret < 0){
452
      perror("set_cloexec_flag");
453
      exitstatus = EXIT_FAILURE;
454
      goto end;
455
    }
456
    ret = set_cloexec_flag(pipefd[1]);
457
    if(ret < 0){
458
      perror("set_cloexec_flag");
459
      exitstatus = EXIT_FAILURE;
460
      goto end;
461
    }
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
462
    /* Block SIGCHLD until process is safely in process list */
463
    ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
464
    if(ret < 0){
465
      perror("sigprocmask");
466
      exitstatus = EXIT_FAILURE;
467
      goto end;
468
    }
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
469
    // Starting a new process to be watched
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
470
    pid_t pid = fork();
471
    if(pid == 0){
472
      /* this is the child process */
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
473
      ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
474
      if(ret < 0){
475
	perror("sigaction");
476
	_exit(EXIT_FAILURE);
477
      }
478
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
479
      if(ret < 0){
480
	perror("sigprocmask");
481
	_exit(EXIT_FAILURE);
482
      }
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
483
      dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
484
      
485
      if(dirfd(dir) < 0){
486
	/* If dir has no file descriptor, we could not set FD_CLOEXEC
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
487
	   above and must now close it manually here. */
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
488
	closedir(dir);
33 by Teddy Hogeborn
* plugins.d/plugbasedclient.c (main): Close the pipe fd after dup2:ing
489
      }
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
490
      if(execv(filename, p->argv) < 0){
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
491
	perror("execv");
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
492
	_exit(EXIT_FAILURE);
493
      }
494
      /* no return */
495
    }
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
496
    /* parent process */
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
497
    free(filename);
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
498
    close(pipefd[1]);		/* close unused write end of pipe */
499
    process *new_process = malloc(sizeof(process));
500
    if (new_process == NULL){
501
      perror("malloc");
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
502
      ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
503
      if(ret < 0){
504
	perror("sigprocmask");
505
      }
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
506
      exitstatus = EXIT_FAILURE;
507
      goto end;
508
    }
509
    
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
510
    *new_process = (struct process){ .pid = pid,
511
				     .fd = pipefd[0],
512
				     .next = process_list };
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
513
    // List handling
514
    process_list = new_process;
515
    /* Unblock SIGCHLD so signal handler can be run if this process
516
       has already completed */
517
    ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
518
    if(ret < 0){
519
      perror("sigprocmask");
520
      exitstatus = EXIT_FAILURE;
521
      goto end;
522
    }
523
    
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
524
    FD_SET(new_process->fd, &rfds_all);
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
525
    
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
526
    if (maxfd < new_process->fd){
527
      maxfd = new_process->fd;
528
    }
529
    
13 by Björn Påhlsson
Added following support:
530
  }
531
  
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
532
  /* Free the plugin list */
533
  for(plugin *next; plugin_list != NULL; plugin_list = next){
534
    next = plugin_list->next;
535
    free(plugin_list->argv);
536
    free(plugin_list);
537
  }
538
  
13 by Björn Påhlsson
Added following support:
539
  closedir(dir);
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
540
  dir = NULL;
13 by Björn Påhlsson
Added following support:
541
  
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
542
  if (process_list == NULL){
543
    fprintf(stderr, "No plugin processes started, exiting\n");
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
544
    exitstatus = EXIT_FAILURE;
545
    goto end;
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
546
  }
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
547
  while(process_list){
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
548
    fd_set rfds = rfds_all;
549
    int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
550
    if (select_ret == -1){
551
      perror("select");
552
      exitstatus = EXIT_FAILURE;
553
      goto end;
554
    }
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
555
    /* OK, now either a process completed, or something can be read
556
       from one of them */
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
557
    for(process *proc = process_list; proc ; proc = proc->next){
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
558
      /* Is this process completely done? */
559
      if(proc->eof and proc->completed){
560
	/* Only accept the plugin output if it exited cleanly */
561
	if(not WIFEXITED(proc->status)
562
	   or WEXITSTATUS(proc->status) != 0){
563
	  /* Bad exit by plugin */
564
	  if(debug){
565
	    if(WIFEXITED(proc->status)){
566
	      fprintf(stderr, "Plugin %d exited with status %d\n",
567
		      proc->pid, WEXITSTATUS(proc->status));
568
	    } else if(WIFSIGNALED(proc->status)) {
569
	      fprintf(stderr, "Plugin %d killed by signal %d\n",
570
		      proc->pid, WTERMSIG(proc->status));
571
	    } else if(WCOREDUMP(proc->status)){
572
	      fprintf(stderr, "Plugin %d dumped core\n", proc->pid);
573
	    }
574
	  }
575
	  /* Remove the plugin */
576
	  FD_CLR(proc->fd, &rfds_all);
577
	  /* Block signal while modifying process_list */
578
	  ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
579
	  if(ret < 0){
580
	    perror("sigprocmask");
581
	    exitstatus = EXIT_FAILURE;
582
	    goto end;
583
	  }
584
	  /* Delete this process entry from the list */
585
	  if(process_list == proc){
586
	    /* First one - simple */
587
	    process_list = proc->next;
588
	  } else {
589
	    /* Second one or later */
590
	    for(process *p = process_list; p != NULL; p = p->next){
591
	      if(p->next == proc){
592
		p->next = proc->next;
593
		break;
594
	      }
595
	    }
596
	  }
597
	  /* We are done modifying process list, so unblock signal */
598
	  ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
599
			     NULL);
600
	  if(ret < 0){
601
	    perror("sigprocmask");
602
	  }
603
	  free(proc->buffer);
604
	  free(proc);
605
	  /* We deleted this process from the list, so we can't go
606
	     proc->next.  Therefore, start over from the beginning of
607
	     the process list */
608
	  break;
609
	}
610
	/* This process exited nicely, so print its buffer */
611
	for(size_t written = 0; written < proc->buffer_length;
612
	    written += (size_t)ret){
613
	  ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO,
614
					 proc->buffer + written,
615
					 proc->buffer_length
616
					 - written));
617
	  if(ret < 0){
618
	    perror("write");
619
	    exitstatus = EXIT_FAILURE;
620
	    goto end;
621
	  }
622
	}
623
	goto end;
624
      }
625
      /* This process has not completed.  Does it have any output? */
626
      if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
627
	/* This process had nothing to say at this time */
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
628
	continue;
629
      }
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
630
      /* Before reading, make the process' data buffer large enough */
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
631
      if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
632
	proc->buffer = realloc(proc->buffer, proc->buffer_size
633
			       + (size_t) BUFFER_SIZE);
634
	if (proc->buffer == NULL){
635
	  perror("malloc");
636
	  exitstatus = EXIT_FAILURE;
637
	  goto end;
638
	}
639
	proc->buffer_size += BUFFER_SIZE;
640
      }
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
641
      /* Read from the process */
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
642
      ret = read(proc->fd, proc->buffer + proc->buffer_length,
643
		 BUFFER_SIZE);
644
      if(ret < 0){
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
645
	/* Read error from this process; ignore the error */
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
646
	continue;
647
      }
648
      if(ret == 0){
649
	/* got EOF */
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
650
	proc->eof = true;
651
      } else {
652
	proc->buffer_length += (size_t) ret;
13 by Björn Påhlsson
Added following support:
653
      }
654
    }
655
  }
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
656
  if(process_list == NULL){
657
    fprintf(stderr, "All plugin processes failed, exiting\n");
658
    exitstatus = EXIT_FAILURE;
659
  }
13 by Björn Påhlsson
Added following support:
660
  
661
 end:
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
662
  /* Restore old signal handler */
663
  sigaction(SIGCHLD, &old_sigchld_action, NULL);
664
  
24.1.8 by Björn Påhlsson
plugbasedclient
665
  free(plus_argv);
666
  
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
667
  /* Free the plugin list */
668
  for(plugin *next; plugin_list != NULL; plugin_list = next){
669
    next = plugin_list->next;
670
    free(plugin_list->argv);
671
    free(plugin_list);
672
  }
673
  
674
  if(dir != NULL){
675
    closedir(dir);
676
  }
677
  
678
  /* Free the process list and kill the processes */
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
679
  for(process *next; process_list != NULL; process_list = next){
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
680
    next = process_list->next;
34 by Teddy Hogeborn
* plugbasedclient.c (main): Check if plugin dir could be opened. Set
681
    close(process_list->fd);
682
    kill(process_list->pid, SIGTERM);
683
    free(process_list->buffer);
684
    free(process_list);
13 by Björn Påhlsson
Added following support:
685
  }
686
  
35 by Teddy Hogeborn
* plugbasedclient.c (struct process): New fields "eof", "completed",
687
  /* Wait for any remaining child processes to terminate */
688
  do{
689
    ret = wait(NULL);
690
  } while(ret >= 0);
691
  if(errno != ECHILD){
692
    perror("wait");
693
  }
694
  
31 by Teddy Hogeborn
* plugins.d/plugbasedclient.c: Update include file comments.
695
  return exitstatus;
13 by Björn Påhlsson
Added following support:
696
}