1 /*
2  * Master Worker - program
3  *
4  * Copyright HAProxy Technologies - William Lallemand <wlallemand@haproxy.com>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version
9  * 2 of the License, or (at your option) any later version.
10  *
11  */
12 
13 #define _GNU_SOURCE
14 
15 #include <sys/types.h>
16 #include <errno.h>
17 #include <grp.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <unistd.h>
21 
22 #include <common/cfgparse.h>
23 #include <common/errors.h>
24 #include <common/initcall.h>
25 
26 #include <proto/log.h>
27 #include <proto/mworker.h>
28 
29 static int use_program = 0; /* do we use the program section ? */
30 
31 /*
32  * Launch every programs
33  */
mworker_ext_launch_all()34 int mworker_ext_launch_all()
35 {
36 	int ret;
37 	struct mworker_proc *child;
38 	struct mworker_proc *tmp;
39 	int reexec = 0;
40 
41 	if (!use_program)
42 		return 0;
43 
44 	reexec = getenv("HAPROXY_MWORKER_REEXEC") ? 1 : 0;
45 
46 	/* find the right mworker_proc */
47 	list_for_each_entry_safe(child, tmp, &proc_list, list) {
48 		if (child->reloads == 0 && (child->options & PROC_O_TYPE_PROG)) {
49 
50 			if (reexec && (!(child->options & PROC_O_START_RELOAD))) {
51 				struct mworker_proc *old_child;
52 
53 				/*
54 				 * This is a reload and we don't want to fork a
55 				 * new program so have to remove the entry in
56 				 * the list.
57 				 *
58 				 * But before that, we need to mark the
59 				 * previous program as not leaving, if we find one.
60 				 */
61 
62 				list_for_each_entry(old_child, &proc_list, list) {
63 					if (!(old_child->options & PROC_O_TYPE_PROG) || (!(old_child->options & PROC_O_LEAVING)))
64 						continue;
65 
66 					if (!strcmp(old_child->id, child->id))
67 						old_child->options &= ~PROC_O_LEAVING;
68 				}
69 
70 
71 				LIST_DEL(&child->list);
72 				mworker_free_child(child);
73 				child = NULL;
74 
75 				continue;
76 			}
77 
78 			child->timestamp = now.tv_sec;
79 
80 			ret = fork();
81 			if (ret < 0) {
82 				ha_alert("Cannot fork program '%s'.\n", child->id);
83 				exit(EXIT_FAILURE); /* there has been an error */
84 			} else if (ret > 0) { /* parent */
85 				child->pid = ret;
86 				ha_notice("New program '%s' (%d) forked\n", child->id, ret);
87 				continue;
88 			} else if (ret == 0) {
89 				/* In child */
90 				mworker_unblock_signals();
91 				mworker_cleanlisteners();
92 				mworker_cleantasks();
93 
94 				/* This one must not be exported, it's internal! */
95 				unsetenv("HAPROXY_MWORKER_REEXEC");
96 				execvp(child->command[0], child->command);
97 
98 				ha_alert("Cannot execute %s: %s\n", child->command[0], strerror(errno));
99 				exit(EXIT_FAILURE);
100 			}
101 		}
102 	}
103 
104 	return 0;
105 
106 }
107 
108 
109 /* Configuration */
110 
cfg_parse_program(const char * file,int linenum,char ** args,int kwm)111 int cfg_parse_program(const char *file, int linenum, char **args, int kwm)
112 {
113 	static struct mworker_proc *ext_child = NULL;
114 	struct mworker_proc *child;
115 	int err_code = 0;
116 
117 	if (!strcmp(args[0], "program")) {
118 		if (alertif_too_many_args(1, file, linenum, args, &err_code)) {
119 			err_code |= ERR_ABORT;
120 			goto error;
121 		}
122 
123 		if (!*args[1]) {
124 			ha_alert("parsing [%s:%d] : '%s' expects an <id> argument\n",
125 				 file, linenum, args[0]);
126 			err_code |= ERR_ALERT | ERR_ABORT;
127 			goto error;
128 		}
129 
130 		ext_child = calloc(1, sizeof(*ext_child));
131 		if (!ext_child) {
132 			ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
133 			err_code |= ERR_ALERT | ERR_ABORT;
134 			goto error;
135 		}
136 
137 		ext_child->options |= PROC_O_TYPE_PROG; /* external process */
138 		ext_child->command = NULL;
139 		ext_child->path = NULL;
140 		ext_child->id = NULL;
141 		ext_child->pid = -1;
142 		ext_child->relative_pid = -1;
143 		ext_child->reloads = 0;
144 		ext_child->timestamp = -1;
145 		ext_child->ipc_fd[0] = -1;
146 		ext_child->ipc_fd[1] = -1;
147 		ext_child->options |= PROC_O_START_RELOAD; /* restart the programs by default */
148 		LIST_INIT(&ext_child->list);
149 
150 		list_for_each_entry(child, &proc_list, list) {
151 			if (child->reloads == 0 && (child->options & PROC_O_TYPE_PROG)) {
152 				if (!strcmp(args[1], child->id)) {
153 					ha_alert("parsing [%s:%d]: '%s' program section already exists in the configuration.\n", file, linenum, args[1]);
154 					err_code |= ERR_ALERT | ERR_ABORT;
155 					goto error;
156 				}
157 			}
158 		}
159 
160 		ext_child->id = strdup(args[1]);
161 		if (!ext_child->id) {
162 			ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
163 			err_code |= ERR_ALERT | ERR_ABORT;
164 			goto error;
165 		}
166 
167 		LIST_ADDQ(&proc_list, &ext_child->list);
168 
169 	} else if (!strcmp(args[0], "command")) {
170 		int arg_nb = 0;
171 		int i = 0;
172 
173 		if (*(args[1]) == 0) {
174 			ha_alert("parsing [%s:%d]: '%s' expects a command with optional arguments separated in words.\n", file, linenum, args[0]);
175 			err_code |= ERR_ALERT | ERR_FATAL;
176 			goto error;
177 		}
178 
179 		while (*args[arg_nb+1])
180 			arg_nb++;
181 
182 		ext_child->command = calloc(arg_nb+1, sizeof(*ext_child->command));
183 
184 		if (!ext_child->command) {
185 			ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
186 			err_code |= ERR_ALERT | ERR_ABORT;
187 			goto error;
188 		}
189 
190 		while (i < arg_nb) {
191 			ext_child->command[i] = strdup(args[i+1]);
192 			if (!ext_child->command[i]) {
193 				ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
194 				err_code |= ERR_ALERT | ERR_ABORT;
195 				goto error;
196 			}
197 			i++;
198 		}
199 		ext_child->command[i] = NULL;
200 
201 	} else if (!strcmp(args[0], "option")) {
202 
203 		if (*(args[1]) == '\0') {
204 			ha_alert("parsing [%s:%d]: '%s' expects an option name.\n",
205 				 file, linenum, args[0]);
206 			err_code |= ERR_ALERT | ERR_FATAL;
207 			goto error;
208 		}
209 
210 		if (strcmp(args[1], "start-on-reload") == 0) {
211 			if (alertif_too_many_args_idx(0, 1, file, linenum, args, &err_code))
212 				goto error;
213 			if (kwm == KWM_STD)
214 				ext_child->options |= PROC_O_START_RELOAD;
215 			else if (kwm == KWM_NO)
216 				ext_child->options &= ~PROC_O_START_RELOAD;
217 			goto out;
218 
219 		} else {
220 			ha_alert("parsing [%s:%d] : unknown option '%s'.\n", file, linenum, args[1]);
221 			err_code |= ERR_ALERT | ERR_FATAL;
222 			goto error;
223 		}
224 	} else {
225 		ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section\n", file, linenum, args[0], "program");
226 		err_code |= ERR_ALERT | ERR_FATAL;
227 		goto error;
228 	}
229 
230 	use_program = 1;
231 
232 	return err_code;
233 
234 error:
235 	if (ext_child) {
236 		LIST_DEL(&ext_child->list);
237 		if (ext_child->command) {
238 			int i;
239 
240 			for (i = 0; ext_child->command[i]; i++) {
241 				if (ext_child->command[i]) {
242 					free(ext_child->command[i]);
243 					ext_child->command[i] = NULL;
244 				}
245 			}
246 			free(ext_child->command);
247 			ext_child->command = NULL;
248 		}
249 		if (ext_child->id) {
250 			free(ext_child->id);
251 			ext_child->id = NULL;
252 		}
253 	}
254 
255 	free(ext_child);
256 	ext_child = NULL;
257 
258 out:
259 	return err_code;
260 
261 }
262 
cfg_program_postparser()263 int cfg_program_postparser()
264 {
265 	int err_code = 0;
266 	struct mworker_proc *child;
267 
268 	/* we only need to check this during configuration parsing,
269 	 * wait mode doesn't have the complete description of a program */
270 	if (global.mode & MODE_MWORKER_WAIT)
271 		return err_code;
272 
273 	list_for_each_entry(child, &proc_list, list) {
274 		if (child->reloads == 0 && (child->options & PROC_O_TYPE_PROG)) {
275 			if (child->command == NULL) {
276 				ha_alert("The program section '%s' lacks a command to launch.\n", child->id);
277 				err_code |= ERR_ALERT | ERR_FATAL;
278 			}
279 		}
280 	}
281 
282 	if (use_program && !(global.mode & MODE_MWORKER)) {
283 		ha_alert("Can't use a 'program' section without master worker mode.\n");
284 		err_code |= ERR_ALERT | ERR_FATAL;
285 	}
286 
287 	return err_code;
288 }
289 
290 
291 REGISTER_CONFIG_SECTION("program", cfg_parse_program, NULL);
292 REGISTER_CONFIG_POSTPARSER("program", cfg_program_postparser);
293