blob: 75cfc4255c90404fc4a8aa48e51094ec42c05b31 [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 Lallemand9a1ee7a2019-04-01 11:30:02 +0200117 execvp(child->command[0], child->command);
118
119 ha_alert("Cannot execute %s: %s\n", child->command[0], strerror(errno));
120 exit(EXIT_FAILURE);
121 }
122 }
123 }
124
125 return 0;
126
127}
128
129
130/* Configuration */
131
132int cfg_parse_program(const char *file, int linenum, char **args, int kwm)
133{
134 static struct mworker_proc *ext_child = NULL;
135 struct mworker_proc *child;
136 int err_code = 0;
137
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100138 if (strcmp(args[0], "program") == 0) {
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200139 if (alertif_too_many_args(1, file, linenum, args, &err_code)) {
140 err_code |= ERR_ABORT;
141 goto error;
142 }
143
144 if (!*args[1]) {
145 ha_alert("parsing [%s:%d] : '%s' expects an <id> argument\n",
146 file, linenum, args[0]);
147 err_code |= ERR_ALERT | ERR_ABORT;
148 goto error;
149 }
150
151 ext_child = calloc(1, sizeof(*ext_child));
152 if (!ext_child) {
153 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
154 err_code |= ERR_ALERT | ERR_ABORT;
155 goto error;
156 }
157
William Lallemand8f7069a2019-04-12 16:09:23 +0200158 ext_child->options |= PROC_O_TYPE_PROG; /* external process */
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200159 ext_child->command = NULL;
160 ext_child->path = NULL;
161 ext_child->id = NULL;
162 ext_child->pid = -1;
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200163 ext_child->reloads = 0;
164 ext_child->timestamp = -1;
165 ext_child->ipc_fd[0] = -1;
166 ext_child->ipc_fd[1] = -1;
William Lallemandbd3de3e2019-04-12 16:09:22 +0200167 ext_child->options |= PROC_O_START_RELOAD; /* restart the programs by default */
Andrew Heberle97236962019-07-12 11:50:26 +0800168 ext_child->uid = -1;
169 ext_child->gid = -1;
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200170 LIST_INIT(&ext_child->list);
171
172 list_for_each_entry(child, &proc_list, list) {
William Lallemand8f7069a2019-04-12 16:09:23 +0200173 if (child->reloads == 0 && (child->options & PROC_O_TYPE_PROG)) {
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100174 if (strcmp(args[1], child->id) == 0) {
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200175 ha_alert("parsing [%s:%d]: '%s' program section already exists in the configuration.\n", file, linenum, args[1]);
176 err_code |= ERR_ALERT | ERR_ABORT;
177 goto error;
178 }
179 }
180 }
181
182 ext_child->id = strdup(args[1]);
183 if (!ext_child->id) {
184 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
185 err_code |= ERR_ALERT | ERR_ABORT;
186 goto error;
187 }
188
Willy Tarreau2b718102021-04-21 07:32:39 +0200189 LIST_APPEND(&proc_list, &ext_child->list);
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200190
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100191 } else if (strcmp(args[0], "command") == 0) {
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200192 int arg_nb = 0;
193 int i = 0;
194
195 if (*(args[1]) == 0) {
196 ha_alert("parsing [%s:%d]: '%s' expects a command with optional arguments separated in words.\n", file, linenum, args[0]);
197 err_code |= ERR_ALERT | ERR_FATAL;
198 goto error;
199 }
200
201 while (*args[arg_nb+1])
202 arg_nb++;
203
204 ext_child->command = calloc(arg_nb+1, sizeof(*ext_child->command));
205
206 if (!ext_child->command) {
207 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
208 err_code |= ERR_ALERT | ERR_ABORT;
209 goto error;
210 }
211
212 while (i < arg_nb) {
213 ext_child->command[i] = strdup(args[i+1]);
214 if (!ext_child->command[i]) {
215 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
216 err_code |= ERR_ALERT | ERR_ABORT;
217 goto error;
218 }
219 i++;
220 }
221 ext_child->command[i] = NULL;
222
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100223 } else if (strcmp(args[0], "option") == 0) {
William Lallemandbd3de3e2019-04-12 16:09:22 +0200224
225 if (*(args[1]) == '\0') {
226 ha_alert("parsing [%s:%d]: '%s' expects an option name.\n",
227 file, linenum, args[0]);
228 err_code |= ERR_ALERT | ERR_FATAL;
229 goto error;
230 }
231
232 if (strcmp(args[1], "start-on-reload") == 0) {
233 if (alertif_too_many_args_idx(0, 1, file, linenum, args, &err_code))
234 goto error;
235 if (kwm == KWM_STD)
236 ext_child->options |= PROC_O_START_RELOAD;
237 else if (kwm == KWM_NO)
238 ext_child->options &= ~PROC_O_START_RELOAD;
239 goto out;
240
241 } else {
242 ha_alert("parsing [%s:%d] : unknown option '%s'.\n", file, linenum, args[1]);
243 err_code |= ERR_ALERT | ERR_FATAL;
244 goto error;
245 }
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100246 } else if (strcmp(args[0], "user") == 0) {
Andrew Heberle97236962019-07-12 11:50:26 +0800247 struct passwd *ext_child_user;
248 if (*(args[1]) == '\0') {
249 ha_alert("parsing [%s:%d]: '%s' expects a user name.\n",
250 file, linenum, args[0]);
251 err_code |= ERR_ALERT | ERR_FATAL;
252 goto error;
253 }
254
255 if (alertif_too_many_args(1, file, linenum, args, &err_code))
256 goto error;
257
258 if (ext_child->uid != -1) {
259 ha_alert("parsing [%s:%d] : user/uid already specified. Continuing.\n", file, linenum);
260 err_code |= ERR_ALERT;
261 goto out;
262 }
263
264 ext_child_user = getpwnam(args[1]);
265 if (ext_child_user != NULL) {
266 ext_child->uid = (int)ext_child_user->pw_uid;
267 } else {
268 ha_alert("parsing [%s:%d] : cannot find user id for '%s' (%d:%s)\n", file, linenum, args[1], errno, strerror(errno));
269 err_code |= ERR_ALERT | ERR_FATAL;
270 }
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100271 } else if (strcmp(args[0], "group") == 0) {
Andrew Heberle97236962019-07-12 11:50:26 +0800272 struct group *ext_child_group;
273 if (*(args[1]) == '\0') {
274 ha_alert("parsing [%s:%d]: '%s' expects a group name.\n",
275 file, linenum, args[0]);
276 err_code |= ERR_ALERT | ERR_FATAL;
277 goto error;
278 }
279
280 if (alertif_too_many_args(1, file, linenum, args, &err_code))
281 goto error;
282
283 if (ext_child->gid != -1) {
284 ha_alert("parsing [%s:%d] : group/gid already specified. Continuing.\n", file, linenum);
285 err_code |= ERR_ALERT;
286 goto out;
287 }
288
289 ext_child_group = getgrnam(args[1]);
290 if (ext_child_group != NULL) {
291 ext_child->gid = (int)ext_child_group->gr_gid;
292 } else {
293 ha_alert("parsing [%s:%d] : cannot find group id for '%s' (%d:%s)\n", file, linenum, args[1], errno, strerror(errno));
294 err_code |= ERR_ALERT | ERR_FATAL;
295 }
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200296 } else {
297 ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section\n", file, linenum, args[0], "program");
298 err_code |= ERR_ALERT | ERR_FATAL;
299 goto error;
300 }
301
302 use_program = 1;
303
304 return err_code;
305
306error:
Tim Duesterhus2c9e2742019-06-23 22:10:12 +0200307 if (ext_child) {
Willy Tarreau2b718102021-04-21 07:32:39 +0200308 LIST_DELETE(&ext_child->list);
Tim Duesterhus2c9e2742019-06-23 22:10:12 +0200309 if (ext_child->command) {
310 int i;
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200311
Tim Duesterhus2c9e2742019-06-23 22:10:12 +0200312 for (i = 0; ext_child->command[i]; i++) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100313 ha_free(&ext_child->command[i]);
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200314 }
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100315 ha_free(&ext_child->command);
Tim Duesterhus2c9e2742019-06-23 22:10:12 +0200316 }
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100317 ha_free(&ext_child->id);
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200318 }
319
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100320 ha_free(&ext_child);
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200321
William Lallemandbd3de3e2019-04-12 16:09:22 +0200322out:
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200323 return err_code;
324
325}
326
327int cfg_program_postparser()
328{
329 int err_code = 0;
330 struct mworker_proc *child;
331
William Lallemand5f47b2e2021-11-10 15:10:00 +0100332 /* we only need to check this during configuration parsing,
333 * wait mode doesn't have the complete description of a program */
334 if (global.mode & MODE_MWORKER_WAIT)
335 return err_code;
336
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200337 list_for_each_entry(child, &proc_list, list) {
William Lallemand8f7069a2019-04-12 16:09:23 +0200338 if (child->reloads == 0 && (child->options & PROC_O_TYPE_PROG)) {
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200339 if (child->command == NULL) {
340 ha_alert("The program section '%s' lacks a command to launch.\n", child->id);
341 err_code |= ERR_ALERT | ERR_FATAL;
342 }
343 }
344 }
345
346 if (use_program && !(global.mode & MODE_MWORKER)) {
347 ha_alert("Can't use a 'program' section without master worker mode.\n");
348 err_code |= ERR_ALERT | ERR_FATAL;
349 }
350
351 return err_code;
352}
353
354
355REGISTER_CONFIG_SECTION("program", cfg_parse_program, NULL);
356REGISTER_CONFIG_POSTPARSER("program", cfg_program_postparser);