blob: 502c54991e5ece8d240da2706ad9029590bf616c [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>
Willy Tarreau6be78492020-06-05 00:00:29 +020024#include <haproxy/cfgparse.h>
Willy Tarreau8d366972020-05-27 16:10:29 +020025#include <haproxy/errors.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020026#include <haproxy/global.h>
Willy Tarreaub5abe5b2020-06-04 14:07:37 +020027#include <haproxy/mworker.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020028#include <haproxy/task.h>
Willy Tarreau410e2592021-10-06 19:31:06 +020029#include <haproxy/time.h>
Willy Tarreau4c943fd2022-01-28 12:25:14 +010030#include <haproxy/tools.h>
William Lallemand9a1ee7a2019-04-01 11:30:02 +020031
William Lallemand9a1ee7a2019-04-01 11:30:02 +020032
33static int use_program = 0; /* do we use the program section ? */
34
35/*
36 * Launch every programs
37 */
38int mworker_ext_launch_all()
39{
40 int ret;
41 struct mworker_proc *child;
William Lallemandbd3de3e2019-04-12 16:09:22 +020042 struct mworker_proc *tmp;
43 int reexec = 0;
William Lallemand9a1ee7a2019-04-01 11:30:02 +020044
45 if (!use_program)
46 return 0;
47
William Lallemandbd3de3e2019-04-12 16:09:22 +020048 reexec = getenv("HAPROXY_MWORKER_REEXEC") ? 1 : 0;
49
William Lallemand9a1ee7a2019-04-01 11:30:02 +020050 /* find the right mworker_proc */
William Lallemandbd3de3e2019-04-12 16:09:22 +020051 list_for_each_entry_safe(child, tmp, &proc_list, list) {
William Lallemand8f7069a2019-04-12 16:09:23 +020052 if (child->reloads == 0 && (child->options & PROC_O_TYPE_PROG)) {
William Lallemandbd3de3e2019-04-12 16:09:22 +020053
54 if (reexec && (!(child->options & PROC_O_START_RELOAD))) {
55 struct mworker_proc *old_child;
56
57 /*
58 * This is a reload and we don't want to fork a
59 * new program so have to remove the entry in
60 * the list.
61 *
62 * But before that, we need to mark the
63 * previous program as not leaving, if we find one.
64 */
65
66 list_for_each_entry(old_child, &proc_list, list) {
William Lallemand8f7069a2019-04-12 16:09:23 +020067 if (!(old_child->options & PROC_O_TYPE_PROG) || (!(old_child->options & PROC_O_LEAVING)))
William Lallemandbd3de3e2019-04-12 16:09:22 +020068 continue;
69
Tim Duesterhuse5ff1412021-01-02 22:31:53 +010070 if (strcmp(old_child->id, child->id) == 0)
William Lallemandbd3de3e2019-04-12 16:09:22 +020071 old_child->options &= ~PROC_O_LEAVING;
72 }
73
74
Willy Tarreau2b718102021-04-21 07:32:39 +020075 LIST_DELETE(&child->list);
Tim Duesterhus9b7a9762019-05-16 20:23:22 +020076 mworker_free_child(child);
William Lallemandbd3de3e2019-04-12 16:09:22 +020077 child = NULL;
78
79 continue;
80 }
81
William Lallemand9a1ee7a2019-04-01 11:30:02 +020082 child->timestamp = now.tv_sec;
83
84 ret = fork();
85 if (ret < 0) {
86 ha_alert("Cannot fork program '%s'.\n", child->id);
87 exit(EXIT_FAILURE); /* there has been an error */
88 } else if (ret > 0) { /* parent */
89 child->pid = ret;
90 ha_notice("New program '%s' (%d) forked\n", child->id, ret);
91 continue;
92 } else if (ret == 0) {
93 /* In child */
94 mworker_unblock_signals();
95 mworker_cleanlisteners();
96 mworker_cleantasks();
97
Andrew Heberle97236962019-07-12 11:50:26 +080098 /* setgid / setuid */
99 if (child->gid != -1) {
100 if (getgroups(0, NULL) > 0 && setgroups(0, NULL) == -1)
101 ha_warning("[%s.main()] Failed to drop supplementary groups. Using 'gid'/'group'"
102 " without 'uid'/'user' is generally useless.\n", child->command[0]);
103
104 if (setgid(child->gid) == -1) {
105 ha_alert("[%s.main()] Cannot set gid %d.\n", child->command[0], child->gid);
106 exit(1);
107 }
108 }
109
110 if (child->uid != -1 && setuid(child->uid) == -1) {
111 ha_alert("[%s.main()] Cannot set uid %d.\n", child->command[0], child->gid);
112 exit(1);
113 }
114
Willy Tarreau3c032f22021-07-21 10:17:02 +0200115 /* This one must not be exported, it's internal! */
116 unsetenv("HAPROXY_MWORKER_REEXEC");
William Lallemandb4e651f2023-04-05 15:50:57 +0200117 unsetenv("HAPROXY_STARTUPLOGS_FD");
118 unsetenv("HAPROXY_MWORKER_WAIT_ONLY");
119 unsetenv("HAPROXY_PROCESSES");
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200120 execvp(child->command[0], child->command);
121
122 ha_alert("Cannot execute %s: %s\n", child->command[0], strerror(errno));
123 exit(EXIT_FAILURE);
124 }
125 }
126 }
127
128 return 0;
129
130}
131
132
133/* Configuration */
134
135int cfg_parse_program(const char *file, int linenum, char **args, int kwm)
136{
137 static struct mworker_proc *ext_child = NULL;
138 struct mworker_proc *child;
139 int err_code = 0;
140
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100141 if (strcmp(args[0], "program") == 0) {
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200142 if (alertif_too_many_args(1, file, linenum, args, &err_code)) {
143 err_code |= ERR_ABORT;
144 goto error;
145 }
146
147 if (!*args[1]) {
148 ha_alert("parsing [%s:%d] : '%s' expects an <id> argument\n",
149 file, linenum, args[0]);
150 err_code |= ERR_ALERT | ERR_ABORT;
151 goto error;
152 }
153
154 ext_child = calloc(1, sizeof(*ext_child));
155 if (!ext_child) {
156 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
157 err_code |= ERR_ALERT | ERR_ABORT;
158 goto error;
159 }
160
William Lallemand8f7069a2019-04-12 16:09:23 +0200161 ext_child->options |= PROC_O_TYPE_PROG; /* external process */
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200162 ext_child->command = NULL;
163 ext_child->path = NULL;
164 ext_child->id = NULL;
165 ext_child->pid = -1;
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200166 ext_child->reloads = 0;
167 ext_child->timestamp = -1;
168 ext_child->ipc_fd[0] = -1;
169 ext_child->ipc_fd[1] = -1;
William Lallemandbd3de3e2019-04-12 16:09:22 +0200170 ext_child->options |= PROC_O_START_RELOAD; /* restart the programs by default */
Andrew Heberle97236962019-07-12 11:50:26 +0800171 ext_child->uid = -1;
172 ext_child->gid = -1;
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200173 LIST_INIT(&ext_child->list);
174
175 list_for_each_entry(child, &proc_list, list) {
William Lallemand8f7069a2019-04-12 16:09:23 +0200176 if (child->reloads == 0 && (child->options & PROC_O_TYPE_PROG)) {
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100177 if (strcmp(args[1], child->id) == 0) {
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200178 ha_alert("parsing [%s:%d]: '%s' program section already exists in the configuration.\n", file, linenum, args[1]);
179 err_code |= ERR_ALERT | ERR_ABORT;
180 goto error;
181 }
182 }
183 }
184
185 ext_child->id = strdup(args[1]);
186 if (!ext_child->id) {
187 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
188 err_code |= ERR_ALERT | ERR_ABORT;
189 goto error;
190 }
191
Willy Tarreau2b718102021-04-21 07:32:39 +0200192 LIST_APPEND(&proc_list, &ext_child->list);
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200193
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100194 } else if (strcmp(args[0], "command") == 0) {
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200195 int arg_nb = 0;
196 int i = 0;
197
198 if (*(args[1]) == 0) {
199 ha_alert("parsing [%s:%d]: '%s' expects a command with optional arguments separated in words.\n", file, linenum, args[0]);
200 err_code |= ERR_ALERT | ERR_FATAL;
201 goto error;
202 }
203
204 while (*args[arg_nb+1])
205 arg_nb++;
206
207 ext_child->command = calloc(arg_nb+1, sizeof(*ext_child->command));
208
209 if (!ext_child->command) {
210 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
211 err_code |= ERR_ALERT | ERR_ABORT;
212 goto error;
213 }
214
215 while (i < arg_nb) {
216 ext_child->command[i] = strdup(args[i+1]);
217 if (!ext_child->command[i]) {
218 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
219 err_code |= ERR_ALERT | ERR_ABORT;
220 goto error;
221 }
222 i++;
223 }
224 ext_child->command[i] = NULL;
225
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100226 } else if (strcmp(args[0], "option") == 0) {
William Lallemandbd3de3e2019-04-12 16:09:22 +0200227
228 if (*(args[1]) == '\0') {
229 ha_alert("parsing [%s:%d]: '%s' expects an option name.\n",
230 file, linenum, args[0]);
231 err_code |= ERR_ALERT | ERR_FATAL;
232 goto error;
233 }
234
235 if (strcmp(args[1], "start-on-reload") == 0) {
236 if (alertif_too_many_args_idx(0, 1, file, linenum, args, &err_code))
237 goto error;
238 if (kwm == KWM_STD)
239 ext_child->options |= PROC_O_START_RELOAD;
240 else if (kwm == KWM_NO)
241 ext_child->options &= ~PROC_O_START_RELOAD;
242 goto out;
243
244 } else {
245 ha_alert("parsing [%s:%d] : unknown option '%s'.\n", file, linenum, args[1]);
246 err_code |= ERR_ALERT | ERR_FATAL;
247 goto error;
248 }
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100249 } else if (strcmp(args[0], "user") == 0) {
Andrew Heberle97236962019-07-12 11:50:26 +0800250 struct passwd *ext_child_user;
251 if (*(args[1]) == '\0') {
252 ha_alert("parsing [%s:%d]: '%s' expects a user name.\n",
253 file, linenum, args[0]);
254 err_code |= ERR_ALERT | ERR_FATAL;
255 goto error;
256 }
257
258 if (alertif_too_many_args(1, file, linenum, args, &err_code))
259 goto error;
260
261 if (ext_child->uid != -1) {
262 ha_alert("parsing [%s:%d] : user/uid already specified. Continuing.\n", file, linenum);
263 err_code |= ERR_ALERT;
264 goto out;
265 }
266
267 ext_child_user = getpwnam(args[1]);
268 if (ext_child_user != NULL) {
269 ext_child->uid = (int)ext_child_user->pw_uid;
270 } else {
271 ha_alert("parsing [%s:%d] : cannot find user id for '%s' (%d:%s)\n", file, linenum, args[1], errno, strerror(errno));
272 err_code |= ERR_ALERT | ERR_FATAL;
273 }
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100274 } else if (strcmp(args[0], "group") == 0) {
Andrew Heberle97236962019-07-12 11:50:26 +0800275 struct group *ext_child_group;
276 if (*(args[1]) == '\0') {
277 ha_alert("parsing [%s:%d]: '%s' expects a group name.\n",
278 file, linenum, args[0]);
279 err_code |= ERR_ALERT | ERR_FATAL;
280 goto error;
281 }
282
283 if (alertif_too_many_args(1, file, linenum, args, &err_code))
284 goto error;
285
286 if (ext_child->gid != -1) {
287 ha_alert("parsing [%s:%d] : group/gid already specified. Continuing.\n", file, linenum);
288 err_code |= ERR_ALERT;
289 goto out;
290 }
291
292 ext_child_group = getgrnam(args[1]);
293 if (ext_child_group != NULL) {
294 ext_child->gid = (int)ext_child_group->gr_gid;
295 } else {
296 ha_alert("parsing [%s:%d] : cannot find group id for '%s' (%d:%s)\n", file, linenum, args[1], errno, strerror(errno));
297 err_code |= ERR_ALERT | ERR_FATAL;
298 }
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200299 } else {
300 ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section\n", file, linenum, args[0], "program");
301 err_code |= ERR_ALERT | ERR_FATAL;
302 goto error;
303 }
304
305 use_program = 1;
306
307 return err_code;
308
309error:
Tim Duesterhus2c9e2742019-06-23 22:10:12 +0200310 if (ext_child) {
Willy Tarreau2b718102021-04-21 07:32:39 +0200311 LIST_DELETE(&ext_child->list);
Tim Duesterhus2c9e2742019-06-23 22:10:12 +0200312 if (ext_child->command) {
313 int i;
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200314
Tim Duesterhus2c9e2742019-06-23 22:10:12 +0200315 for (i = 0; ext_child->command[i]; i++) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100316 ha_free(&ext_child->command[i]);
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200317 }
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100318 ha_free(&ext_child->command);
Tim Duesterhus2c9e2742019-06-23 22:10:12 +0200319 }
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100320 ha_free(&ext_child->id);
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200321 }
322
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100323 ha_free(&ext_child);
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200324
William Lallemandbd3de3e2019-04-12 16:09:22 +0200325out:
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200326 return err_code;
327
328}
329
330int cfg_program_postparser()
331{
332 int err_code = 0;
333 struct mworker_proc *child;
334
William Lallemand5f47b2e2021-11-10 15:10:00 +0100335 /* we only need to check this during configuration parsing,
336 * wait mode doesn't have the complete description of a program */
337 if (global.mode & MODE_MWORKER_WAIT)
338 return err_code;
339
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200340 list_for_each_entry(child, &proc_list, list) {
William Lallemand8f7069a2019-04-12 16:09:23 +0200341 if (child->reloads == 0 && (child->options & PROC_O_TYPE_PROG)) {
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200342 if (child->command == NULL) {
343 ha_alert("The program section '%s' lacks a command to launch.\n", child->id);
344 err_code |= ERR_ALERT | ERR_FATAL;
345 }
346 }
347 }
348
349 if (use_program && !(global.mode & MODE_MWORKER)) {
350 ha_alert("Can't use a 'program' section without master worker mode.\n");
351 err_code |= ERR_ALERT | ERR_FATAL;
352 }
353
354 return err_code;
355}
356
357
358REGISTER_CONFIG_SECTION("program", cfg_parse_program, NULL);
359REGISTER_CONFIG_POSTPARSER("program", cfg_program_postparser);