blob: 800624f940d811c1f3305d6ebcee7a248b34434a [file] [log] [blame]
William Lallemand9a1ee7a2019-04-01 11:30:02 +02001/*
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>
Andrew Heberle97236962019-07-12 11:50:26 +080018#include <pwd.h>
William Lallemand9a1ee7a2019-04-01 11:30:02 +020019#include <stdio.h>
20#include <string.h>
21#include <unistd.h>
22
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020023#include <haproxy/api.h>
William Lallemand9a1ee7a2019-04-01 11:30:02 +020024#include <common/cfgparse.h>
Willy Tarreau8d366972020-05-27 16:10:29 +020025#include <haproxy/errors.h>
Willy Tarreauaeed4a82020-06-04 22:01:04 +020026#include <haproxy/log.h>
Willy Tarreaub5abe5b2020-06-04 14:07:37 +020027#include <haproxy/mworker.h>
William Lallemand9a1ee7a2019-04-01 11:30:02 +020028
William Lallemand9a1ee7a2019-04-01 11:30:02 +020029
30static int use_program = 0; /* do we use the program section ? */
31
32/*
33 * Launch every programs
34 */
35int mworker_ext_launch_all()
36{
37 int ret;
38 struct mworker_proc *child;
William Lallemandbd3de3e2019-04-12 16:09:22 +020039 struct mworker_proc *tmp;
40 int reexec = 0;
William Lallemand9a1ee7a2019-04-01 11:30:02 +020041
42 if (!use_program)
43 return 0;
44
William Lallemandbd3de3e2019-04-12 16:09:22 +020045 reexec = getenv("HAPROXY_MWORKER_REEXEC") ? 1 : 0;
46
William Lallemand9a1ee7a2019-04-01 11:30:02 +020047 /* find the right mworker_proc */
William Lallemandbd3de3e2019-04-12 16:09:22 +020048 list_for_each_entry_safe(child, tmp, &proc_list, list) {
William Lallemand8f7069a2019-04-12 16:09:23 +020049 if (child->reloads == 0 && (child->options & PROC_O_TYPE_PROG)) {
William Lallemandbd3de3e2019-04-12 16:09:22 +020050
51 if (reexec && (!(child->options & PROC_O_START_RELOAD))) {
52 struct mworker_proc *old_child;
53
54 /*
55 * This is a reload and we don't want to fork a
56 * new program so have to remove the entry in
57 * the list.
58 *
59 * But before that, we need to mark the
60 * previous program as not leaving, if we find one.
61 */
62
63 list_for_each_entry(old_child, &proc_list, list) {
William Lallemand8f7069a2019-04-12 16:09:23 +020064 if (!(old_child->options & PROC_O_TYPE_PROG) || (!(old_child->options & PROC_O_LEAVING)))
William Lallemandbd3de3e2019-04-12 16:09:22 +020065 continue;
66
67 if (!strcmp(old_child->id, child->id))
68 old_child->options &= ~PROC_O_LEAVING;
69 }
70
71
72 LIST_DEL(&child->list);
Tim Duesterhus9b7a9762019-05-16 20:23:22 +020073 mworker_free_child(child);
William Lallemandbd3de3e2019-04-12 16:09:22 +020074 child = NULL;
75
76 continue;
77 }
78
William Lallemand9a1ee7a2019-04-01 11:30:02 +020079 child->timestamp = now.tv_sec;
80
81 ret = fork();
82 if (ret < 0) {
83 ha_alert("Cannot fork program '%s'.\n", child->id);
84 exit(EXIT_FAILURE); /* there has been an error */
85 } else if (ret > 0) { /* parent */
86 child->pid = ret;
87 ha_notice("New program '%s' (%d) forked\n", child->id, ret);
88 continue;
89 } else if (ret == 0) {
90 /* In child */
91 mworker_unblock_signals();
92 mworker_cleanlisteners();
93 mworker_cleantasks();
94
Andrew Heberle97236962019-07-12 11:50:26 +080095 /* setgid / setuid */
96 if (child->gid != -1) {
97 if (getgroups(0, NULL) > 0 && setgroups(0, NULL) == -1)
98 ha_warning("[%s.main()] Failed to drop supplementary groups. Using 'gid'/'group'"
99 " without 'uid'/'user' is generally useless.\n", child->command[0]);
100
101 if (setgid(child->gid) == -1) {
102 ha_alert("[%s.main()] Cannot set gid %d.\n", child->command[0], child->gid);
103 exit(1);
104 }
105 }
106
107 if (child->uid != -1 && setuid(child->uid) == -1) {
108 ha_alert("[%s.main()] Cannot set uid %d.\n", child->command[0], child->gid);
109 exit(1);
110 }
111
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200112 execvp(child->command[0], child->command);
113
114 ha_alert("Cannot execute %s: %s\n", child->command[0], strerror(errno));
115 exit(EXIT_FAILURE);
116 }
117 }
118 }
119
120 return 0;
121
122}
123
124
125/* Configuration */
126
127int cfg_parse_program(const char *file, int linenum, char **args, int kwm)
128{
129 static struct mworker_proc *ext_child = NULL;
130 struct mworker_proc *child;
131 int err_code = 0;
132
133 if (!strcmp(args[0], "program")) {
134 if (alertif_too_many_args(1, file, linenum, args, &err_code)) {
135 err_code |= ERR_ABORT;
136 goto error;
137 }
138
139 if (!*args[1]) {
140 ha_alert("parsing [%s:%d] : '%s' expects an <id> argument\n",
141 file, linenum, args[0]);
142 err_code |= ERR_ALERT | ERR_ABORT;
143 goto error;
144 }
145
146 ext_child = calloc(1, sizeof(*ext_child));
147 if (!ext_child) {
148 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
149 err_code |= ERR_ALERT | ERR_ABORT;
150 goto error;
151 }
152
William Lallemand8f7069a2019-04-12 16:09:23 +0200153 ext_child->options |= PROC_O_TYPE_PROG; /* external process */
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200154 ext_child->command = NULL;
155 ext_child->path = NULL;
156 ext_child->id = NULL;
157 ext_child->pid = -1;
158 ext_child->relative_pid = -1;
159 ext_child->reloads = 0;
160 ext_child->timestamp = -1;
161 ext_child->ipc_fd[0] = -1;
162 ext_child->ipc_fd[1] = -1;
William Lallemandbd3de3e2019-04-12 16:09:22 +0200163 ext_child->options |= PROC_O_START_RELOAD; /* restart the programs by default */
Andrew Heberle97236962019-07-12 11:50:26 +0800164 ext_child->uid = -1;
165 ext_child->gid = -1;
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200166 LIST_INIT(&ext_child->list);
167
168 list_for_each_entry(child, &proc_list, list) {
William Lallemand8f7069a2019-04-12 16:09:23 +0200169 if (child->reloads == 0 && (child->options & PROC_O_TYPE_PROG)) {
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200170 if (!strcmp(args[1], child->id)) {
171 ha_alert("parsing [%s:%d]: '%s' program section already exists in the configuration.\n", file, linenum, args[1]);
172 err_code |= ERR_ALERT | ERR_ABORT;
173 goto error;
174 }
175 }
176 }
177
178 ext_child->id = strdup(args[1]);
179 if (!ext_child->id) {
180 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
181 err_code |= ERR_ALERT | ERR_ABORT;
182 goto error;
183 }
184
185 LIST_ADDQ(&proc_list, &ext_child->list);
186
187 } else if (!strcmp(args[0], "command")) {
188 int arg_nb = 0;
189 int i = 0;
190
191 if (*(args[1]) == 0) {
192 ha_alert("parsing [%s:%d]: '%s' expects a command with optional arguments separated in words.\n", file, linenum, args[0]);
193 err_code |= ERR_ALERT | ERR_FATAL;
194 goto error;
195 }
196
197 while (*args[arg_nb+1])
198 arg_nb++;
199
200 ext_child->command = calloc(arg_nb+1, sizeof(*ext_child->command));
201
202 if (!ext_child->command) {
203 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
204 err_code |= ERR_ALERT | ERR_ABORT;
205 goto error;
206 }
207
208 while (i < arg_nb) {
209 ext_child->command[i] = strdup(args[i+1]);
210 if (!ext_child->command[i]) {
211 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
212 err_code |= ERR_ALERT | ERR_ABORT;
213 goto error;
214 }
215 i++;
216 }
217 ext_child->command[i] = NULL;
218
William Lallemandbd3de3e2019-04-12 16:09:22 +0200219 } else if (!strcmp(args[0], "option")) {
220
221 if (*(args[1]) == '\0') {
222 ha_alert("parsing [%s:%d]: '%s' expects an option name.\n",
223 file, linenum, args[0]);
224 err_code |= ERR_ALERT | ERR_FATAL;
225 goto error;
226 }
227
228 if (strcmp(args[1], "start-on-reload") == 0) {
229 if (alertif_too_many_args_idx(0, 1, file, linenum, args, &err_code))
230 goto error;
231 if (kwm == KWM_STD)
232 ext_child->options |= PROC_O_START_RELOAD;
233 else if (kwm == KWM_NO)
234 ext_child->options &= ~PROC_O_START_RELOAD;
235 goto out;
236
237 } else {
238 ha_alert("parsing [%s:%d] : unknown option '%s'.\n", file, linenum, args[1]);
239 err_code |= ERR_ALERT | ERR_FATAL;
240 goto error;
241 }
Andrew Heberle97236962019-07-12 11:50:26 +0800242 } else if (!strcmp(args[0], "user")) {
243 struct passwd *ext_child_user;
244 if (*(args[1]) == '\0') {
245 ha_alert("parsing [%s:%d]: '%s' expects a user name.\n",
246 file, linenum, args[0]);
247 err_code |= ERR_ALERT | ERR_FATAL;
248 goto error;
249 }
250
251 if (alertif_too_many_args(1, file, linenum, args, &err_code))
252 goto error;
253
254 if (ext_child->uid != -1) {
255 ha_alert("parsing [%s:%d] : user/uid already specified. Continuing.\n", file, linenum);
256 err_code |= ERR_ALERT;
257 goto out;
258 }
259
260 ext_child_user = getpwnam(args[1]);
261 if (ext_child_user != NULL) {
262 ext_child->uid = (int)ext_child_user->pw_uid;
263 } else {
264 ha_alert("parsing [%s:%d] : cannot find user id for '%s' (%d:%s)\n", file, linenum, args[1], errno, strerror(errno));
265 err_code |= ERR_ALERT | ERR_FATAL;
266 }
267 } else if (!strcmp(args[0], "group")) {
268 struct group *ext_child_group;
269 if (*(args[1]) == '\0') {
270 ha_alert("parsing [%s:%d]: '%s' expects a group name.\n",
271 file, linenum, args[0]);
272 err_code |= ERR_ALERT | ERR_FATAL;
273 goto error;
274 }
275
276 if (alertif_too_many_args(1, file, linenum, args, &err_code))
277 goto error;
278
279 if (ext_child->gid != -1) {
280 ha_alert("parsing [%s:%d] : group/gid already specified. Continuing.\n", file, linenum);
281 err_code |= ERR_ALERT;
282 goto out;
283 }
284
285 ext_child_group = getgrnam(args[1]);
286 if (ext_child_group != NULL) {
287 ext_child->gid = (int)ext_child_group->gr_gid;
288 } else {
289 ha_alert("parsing [%s:%d] : cannot find group id for '%s' (%d:%s)\n", file, linenum, args[1], errno, strerror(errno));
290 err_code |= ERR_ALERT | ERR_FATAL;
291 }
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200292 } else {
293 ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section\n", file, linenum, args[0], "program");
294 err_code |= ERR_ALERT | ERR_FATAL;
295 goto error;
296 }
297
298 use_program = 1;
299
300 return err_code;
301
302error:
Tim Duesterhus2c9e2742019-06-23 22:10:12 +0200303 if (ext_child) {
304 LIST_DEL(&ext_child->list);
305 if (ext_child->command) {
306 int i;
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200307
Tim Duesterhus2c9e2742019-06-23 22:10:12 +0200308 for (i = 0; ext_child->command[i]; i++) {
309 if (ext_child->command[i]) {
310 free(ext_child->command[i]);
311 ext_child->command[i] = NULL;
312 }
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200313 }
Tim Duesterhus2c9e2742019-06-23 22:10:12 +0200314 free(ext_child->command);
315 ext_child->command = NULL;
316 }
317 if (ext_child->id) {
318 free(ext_child->id);
319 ext_child->id = NULL;
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200320 }
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200321 }
322
323 free(ext_child);
324 ext_child = NULL;
325
William Lallemandbd3de3e2019-04-12 16:09:22 +0200326out:
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200327 return err_code;
328
329}
330
331int cfg_program_postparser()
332{
333 int err_code = 0;
334 struct mworker_proc *child;
335
336 list_for_each_entry(child, &proc_list, list) {
William Lallemand8f7069a2019-04-12 16:09:23 +0200337 if (child->reloads == 0 && (child->options & PROC_O_TYPE_PROG)) {
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200338 if (child->command == NULL) {
339 ha_alert("The program section '%s' lacks a command to launch.\n", child->id);
340 err_code |= ERR_ALERT | ERR_FATAL;
341 }
342 }
343 }
344
345 if (use_program && !(global.mode & MODE_MWORKER)) {
346 ha_alert("Can't use a 'program' section without master worker mode.\n");
347 err_code |= ERR_ALERT | ERR_FATAL;
348 }
349
350 return err_code;
351}
352
353
354REGISTER_CONFIG_SECTION("program", cfg_parse_program, NULL);
355REGISTER_CONFIG_POSTPARSER("program", cfg_program_postparser);