blob: 8d9ce3aab48d2ca207d83cfaf2ec4c58bc80a9b9 [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>
William Lallemand9a1ee7a2019-04-01 11:30:02 +020029
William Lallemand9a1ee7a2019-04-01 11:30:02 +020030
31static int use_program = 0; /* do we use the program section ? */
32
33/*
34 * Launch every programs
35 */
36int mworker_ext_launch_all()
37{
38 int ret;
39 struct mworker_proc *child;
William Lallemandbd3de3e2019-04-12 16:09:22 +020040 struct mworker_proc *tmp;
41 int reexec = 0;
William Lallemand9a1ee7a2019-04-01 11:30:02 +020042
43 if (!use_program)
44 return 0;
45
William Lallemandbd3de3e2019-04-12 16:09:22 +020046 reexec = getenv("HAPROXY_MWORKER_REEXEC") ? 1 : 0;
47
William Lallemand9a1ee7a2019-04-01 11:30:02 +020048 /* find the right mworker_proc */
William Lallemandbd3de3e2019-04-12 16:09:22 +020049 list_for_each_entry_safe(child, tmp, &proc_list, list) {
William Lallemand8f7069a2019-04-12 16:09:23 +020050 if (child->reloads == 0 && (child->options & PROC_O_TYPE_PROG)) {
William Lallemandbd3de3e2019-04-12 16:09:22 +020051
52 if (reexec && (!(child->options & PROC_O_START_RELOAD))) {
53 struct mworker_proc *old_child;
54
55 /*
56 * This is a reload and we don't want to fork a
57 * new program so have to remove the entry in
58 * the list.
59 *
60 * But before that, we need to mark the
61 * previous program as not leaving, if we find one.
62 */
63
64 list_for_each_entry(old_child, &proc_list, list) {
William Lallemand8f7069a2019-04-12 16:09:23 +020065 if (!(old_child->options & PROC_O_TYPE_PROG) || (!(old_child->options & PROC_O_LEAVING)))
William Lallemandbd3de3e2019-04-12 16:09:22 +020066 continue;
67
Tim Duesterhuse5ff1412021-01-02 22:31:53 +010068 if (strcmp(old_child->id, child->id) == 0)
William Lallemandbd3de3e2019-04-12 16:09:22 +020069 old_child->options &= ~PROC_O_LEAVING;
70 }
71
72
Willy Tarreau2b718102021-04-21 07:32:39 +020073 LIST_DELETE(&child->list);
Tim Duesterhus9b7a9762019-05-16 20:23:22 +020074 mworker_free_child(child);
William Lallemandbd3de3e2019-04-12 16:09:22 +020075 child = NULL;
76
77 continue;
78 }
79
William Lallemand9a1ee7a2019-04-01 11:30:02 +020080 child->timestamp = now.tv_sec;
81
82 ret = fork();
83 if (ret < 0) {
84 ha_alert("Cannot fork program '%s'.\n", child->id);
85 exit(EXIT_FAILURE); /* there has been an error */
86 } else if (ret > 0) { /* parent */
87 child->pid = ret;
88 ha_notice("New program '%s' (%d) forked\n", child->id, ret);
89 continue;
90 } else if (ret == 0) {
91 /* In child */
92 mworker_unblock_signals();
93 mworker_cleanlisteners();
94 mworker_cleantasks();
95
Andrew Heberle97236962019-07-12 11:50:26 +080096 /* setgid / setuid */
97 if (child->gid != -1) {
98 if (getgroups(0, NULL) > 0 && setgroups(0, NULL) == -1)
99 ha_warning("[%s.main()] Failed to drop supplementary groups. Using 'gid'/'group'"
100 " without 'uid'/'user' is generally useless.\n", child->command[0]);
101
102 if (setgid(child->gid) == -1) {
103 ha_alert("[%s.main()] Cannot set gid %d.\n", child->command[0], child->gid);
104 exit(1);
105 }
106 }
107
108 if (child->uid != -1 && setuid(child->uid) == -1) {
109 ha_alert("[%s.main()] Cannot set uid %d.\n", child->command[0], child->gid);
110 exit(1);
111 }
112
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200113 execvp(child->command[0], child->command);
114
115 ha_alert("Cannot execute %s: %s\n", child->command[0], strerror(errno));
116 exit(EXIT_FAILURE);
117 }
118 }
119 }
120
121 return 0;
122
123}
124
125
126/* Configuration */
127
128int cfg_parse_program(const char *file, int linenum, char **args, int kwm)
129{
130 static struct mworker_proc *ext_child = NULL;
131 struct mworker_proc *child;
132 int err_code = 0;
133
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100134 if (strcmp(args[0], "program") == 0) {
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200135 if (alertif_too_many_args(1, file, linenum, args, &err_code)) {
136 err_code |= ERR_ABORT;
137 goto error;
138 }
139
140 if (!*args[1]) {
141 ha_alert("parsing [%s:%d] : '%s' expects an <id> argument\n",
142 file, linenum, args[0]);
143 err_code |= ERR_ALERT | ERR_ABORT;
144 goto error;
145 }
146
147 ext_child = calloc(1, sizeof(*ext_child));
148 if (!ext_child) {
149 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
150 err_code |= ERR_ALERT | ERR_ABORT;
151 goto error;
152 }
153
William Lallemand8f7069a2019-04-12 16:09:23 +0200154 ext_child->options |= PROC_O_TYPE_PROG; /* external process */
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200155 ext_child->command = NULL;
156 ext_child->path = NULL;
157 ext_child->id = NULL;
158 ext_child->pid = -1;
159 ext_child->relative_pid = -1;
160 ext_child->reloads = 0;
161 ext_child->timestamp = -1;
162 ext_child->ipc_fd[0] = -1;
163 ext_child->ipc_fd[1] = -1;
William Lallemandbd3de3e2019-04-12 16:09:22 +0200164 ext_child->options |= PROC_O_START_RELOAD; /* restart the programs by default */
Andrew Heberle97236962019-07-12 11:50:26 +0800165 ext_child->uid = -1;
166 ext_child->gid = -1;
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200167 LIST_INIT(&ext_child->list);
168
169 list_for_each_entry(child, &proc_list, list) {
William Lallemand8f7069a2019-04-12 16:09:23 +0200170 if (child->reloads == 0 && (child->options & PROC_O_TYPE_PROG)) {
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100171 if (strcmp(args[1], child->id) == 0) {
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200172 ha_alert("parsing [%s:%d]: '%s' program section already exists in the configuration.\n", file, linenum, args[1]);
173 err_code |= ERR_ALERT | ERR_ABORT;
174 goto error;
175 }
176 }
177 }
178
179 ext_child->id = strdup(args[1]);
180 if (!ext_child->id) {
181 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
182 err_code |= ERR_ALERT | ERR_ABORT;
183 goto error;
184 }
185
Willy Tarreau2b718102021-04-21 07:32:39 +0200186 LIST_APPEND(&proc_list, &ext_child->list);
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200187
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100188 } else if (strcmp(args[0], "command") == 0) {
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200189 int arg_nb = 0;
190 int i = 0;
191
192 if (*(args[1]) == 0) {
193 ha_alert("parsing [%s:%d]: '%s' expects a command with optional arguments separated in words.\n", file, linenum, args[0]);
194 err_code |= ERR_ALERT | ERR_FATAL;
195 goto error;
196 }
197
198 while (*args[arg_nb+1])
199 arg_nb++;
200
201 ext_child->command = calloc(arg_nb+1, sizeof(*ext_child->command));
202
203 if (!ext_child->command) {
204 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
205 err_code |= ERR_ALERT | ERR_ABORT;
206 goto error;
207 }
208
209 while (i < arg_nb) {
210 ext_child->command[i] = strdup(args[i+1]);
211 if (!ext_child->command[i]) {
212 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
213 err_code |= ERR_ALERT | ERR_ABORT;
214 goto error;
215 }
216 i++;
217 }
218 ext_child->command[i] = NULL;
219
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100220 } else if (strcmp(args[0], "option") == 0) {
William Lallemandbd3de3e2019-04-12 16:09:22 +0200221
222 if (*(args[1]) == '\0') {
223 ha_alert("parsing [%s:%d]: '%s' expects an option name.\n",
224 file, linenum, args[0]);
225 err_code |= ERR_ALERT | ERR_FATAL;
226 goto error;
227 }
228
229 if (strcmp(args[1], "start-on-reload") == 0) {
230 if (alertif_too_many_args_idx(0, 1, file, linenum, args, &err_code))
231 goto error;
232 if (kwm == KWM_STD)
233 ext_child->options |= PROC_O_START_RELOAD;
234 else if (kwm == KWM_NO)
235 ext_child->options &= ~PROC_O_START_RELOAD;
236 goto out;
237
238 } else {
239 ha_alert("parsing [%s:%d] : unknown option '%s'.\n", file, linenum, args[1]);
240 err_code |= ERR_ALERT | ERR_FATAL;
241 goto error;
242 }
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100243 } else if (strcmp(args[0], "user") == 0) {
Andrew Heberle97236962019-07-12 11:50:26 +0800244 struct passwd *ext_child_user;
245 if (*(args[1]) == '\0') {
246 ha_alert("parsing [%s:%d]: '%s' expects a user name.\n",
247 file, linenum, args[0]);
248 err_code |= ERR_ALERT | ERR_FATAL;
249 goto error;
250 }
251
252 if (alertif_too_many_args(1, file, linenum, args, &err_code))
253 goto error;
254
255 if (ext_child->uid != -1) {
256 ha_alert("parsing [%s:%d] : user/uid already specified. Continuing.\n", file, linenum);
257 err_code |= ERR_ALERT;
258 goto out;
259 }
260
261 ext_child_user = getpwnam(args[1]);
262 if (ext_child_user != NULL) {
263 ext_child->uid = (int)ext_child_user->pw_uid;
264 } else {
265 ha_alert("parsing [%s:%d] : cannot find user id for '%s' (%d:%s)\n", file, linenum, args[1], errno, strerror(errno));
266 err_code |= ERR_ALERT | ERR_FATAL;
267 }
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100268 } else if (strcmp(args[0], "group") == 0) {
Andrew Heberle97236962019-07-12 11:50:26 +0800269 struct group *ext_child_group;
270 if (*(args[1]) == '\0') {
271 ha_alert("parsing [%s:%d]: '%s' expects a group name.\n",
272 file, linenum, args[0]);
273 err_code |= ERR_ALERT | ERR_FATAL;
274 goto error;
275 }
276
277 if (alertif_too_many_args(1, file, linenum, args, &err_code))
278 goto error;
279
280 if (ext_child->gid != -1) {
281 ha_alert("parsing [%s:%d] : group/gid already specified. Continuing.\n", file, linenum);
282 err_code |= ERR_ALERT;
283 goto out;
284 }
285
286 ext_child_group = getgrnam(args[1]);
287 if (ext_child_group != NULL) {
288 ext_child->gid = (int)ext_child_group->gr_gid;
289 } else {
290 ha_alert("parsing [%s:%d] : cannot find group id for '%s' (%d:%s)\n", file, linenum, args[1], errno, strerror(errno));
291 err_code |= ERR_ALERT | ERR_FATAL;
292 }
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200293 } else {
294 ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section\n", file, linenum, args[0], "program");
295 err_code |= ERR_ALERT | ERR_FATAL;
296 goto error;
297 }
298
299 use_program = 1;
300
301 return err_code;
302
303error:
Tim Duesterhus2c9e2742019-06-23 22:10:12 +0200304 if (ext_child) {
Willy Tarreau2b718102021-04-21 07:32:39 +0200305 LIST_DELETE(&ext_child->list);
Tim Duesterhus2c9e2742019-06-23 22:10:12 +0200306 if (ext_child->command) {
307 int i;
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200308
Tim Duesterhus2c9e2742019-06-23 22:10:12 +0200309 for (i = 0; ext_child->command[i]; i++) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100310 ha_free(&ext_child->command[i]);
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200311 }
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100312 ha_free(&ext_child->command);
Tim Duesterhus2c9e2742019-06-23 22:10:12 +0200313 }
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100314 ha_free(&ext_child->id);
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200315 }
316
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100317 ha_free(&ext_child);
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200318
William Lallemandbd3de3e2019-04-12 16:09:22 +0200319out:
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200320 return err_code;
321
322}
323
324int cfg_program_postparser()
325{
326 int err_code = 0;
327 struct mworker_proc *child;
328
329 list_for_each_entry(child, &proc_list, list) {
William Lallemand8f7069a2019-04-12 16:09:23 +0200330 if (child->reloads == 0 && (child->options & PROC_O_TYPE_PROG)) {
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200331 if (child->command == NULL) {
332 ha_alert("The program section '%s' lacks a command to launch.\n", child->id);
333 err_code |= ERR_ALERT | ERR_FATAL;
334 }
335 }
336 }
337
338 if (use_program && !(global.mode & MODE_MWORKER)) {
339 ha_alert("Can't use a 'program' section without master worker mode.\n");
340 err_code |= ERR_ALERT | ERR_FATAL;
341 }
342
343 return err_code;
344}
345
346
347REGISTER_CONFIG_SECTION("program", cfg_parse_program, NULL);
348REGISTER_CONFIG_POSTPARSER("program", cfg_program_postparser);