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