blob: d0b9446e6b0dd8a2233d4e2993724e6c6a6f7c47 [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 Tarreauaeed4a82020-06-04 22:01:04 +020027#include <haproxy/log.h>
Willy Tarreaub5abe5b2020-06-04 14:07:37 +020028#include <haproxy/mworker.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020029#include <haproxy/task.h>
William Lallemand9a1ee7a2019-04-01 11:30:02 +020030
William Lallemand9a1ee7a2019-04-01 11:30:02 +020031
32static int use_program = 0; /* do we use the program section ? */
33
34/*
35 * Launch every programs
36 */
37int mworker_ext_launch_all()
38{
39 int ret;
40 struct mworker_proc *child;
William Lallemandbd3de3e2019-04-12 16:09:22 +020041 struct mworker_proc *tmp;
42 int reexec = 0;
William Lallemand9a1ee7a2019-04-01 11:30:02 +020043
44 if (!use_program)
45 return 0;
46
William Lallemandbd3de3e2019-04-12 16:09:22 +020047 reexec = getenv("HAPROXY_MWORKER_REEXEC") ? 1 : 0;
48
William Lallemand9a1ee7a2019-04-01 11:30:02 +020049 /* find the right mworker_proc */
William Lallemandbd3de3e2019-04-12 16:09:22 +020050 list_for_each_entry_safe(child, tmp, &proc_list, list) {
William Lallemand8f7069a2019-04-12 16:09:23 +020051 if (child->reloads == 0 && (child->options & PROC_O_TYPE_PROG)) {
William Lallemandbd3de3e2019-04-12 16:09:22 +020052
53 if (reexec && (!(child->options & PROC_O_START_RELOAD))) {
54 struct mworker_proc *old_child;
55
56 /*
57 * This is a reload and we don't want to fork a
58 * new program so have to remove the entry in
59 * the list.
60 *
61 * But before that, we need to mark the
62 * previous program as not leaving, if we find one.
63 */
64
65 list_for_each_entry(old_child, &proc_list, list) {
William Lallemand8f7069a2019-04-12 16:09:23 +020066 if (!(old_child->options & PROC_O_TYPE_PROG) || (!(old_child->options & PROC_O_LEAVING)))
William Lallemandbd3de3e2019-04-12 16:09:22 +020067 continue;
68
69 if (!strcmp(old_child->id, child->id))
70 old_child->options &= ~PROC_O_LEAVING;
71 }
72
73
74 LIST_DEL(&child->list);
Tim Duesterhus9b7a9762019-05-16 20:23:22 +020075 mworker_free_child(child);
William Lallemandbd3de3e2019-04-12 16:09:22 +020076 child = NULL;
77
78 continue;
79 }
80
William Lallemand9a1ee7a2019-04-01 11:30:02 +020081 child->timestamp = now.tv_sec;
82
83 ret = fork();
84 if (ret < 0) {
85 ha_alert("Cannot fork program '%s'.\n", child->id);
86 exit(EXIT_FAILURE); /* there has been an error */
87 } else if (ret > 0) { /* parent */
88 child->pid = ret;
89 ha_notice("New program '%s' (%d) forked\n", child->id, ret);
90 continue;
91 } else if (ret == 0) {
92 /* In child */
93 mworker_unblock_signals();
94 mworker_cleanlisteners();
95 mworker_cleantasks();
96
Andrew Heberle97236962019-07-12 11:50:26 +080097 /* setgid / setuid */
98 if (child->gid != -1) {
99 if (getgroups(0, NULL) > 0 && setgroups(0, NULL) == -1)
100 ha_warning("[%s.main()] Failed to drop supplementary groups. Using 'gid'/'group'"
101 " without 'uid'/'user' is generally useless.\n", child->command[0]);
102
103 if (setgid(child->gid) == -1) {
104 ha_alert("[%s.main()] Cannot set gid %d.\n", child->command[0], child->gid);
105 exit(1);
106 }
107 }
108
109 if (child->uid != -1 && setuid(child->uid) == -1) {
110 ha_alert("[%s.main()] Cannot set uid %d.\n", child->command[0], child->gid);
111 exit(1);
112 }
113
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200114 execvp(child->command[0], child->command);
115
116 ha_alert("Cannot execute %s: %s\n", child->command[0], strerror(errno));
117 exit(EXIT_FAILURE);
118 }
119 }
120 }
121
122 return 0;
123
124}
125
126
127/* Configuration */
128
129int cfg_parse_program(const char *file, int linenum, char **args, int kwm)
130{
131 static struct mworker_proc *ext_child = NULL;
132 struct mworker_proc *child;
133 int err_code = 0;
134
135 if (!strcmp(args[0], "program")) {
136 if (alertif_too_many_args(1, file, linenum, args, &err_code)) {
137 err_code |= ERR_ABORT;
138 goto error;
139 }
140
141 if (!*args[1]) {
142 ha_alert("parsing [%s:%d] : '%s' expects an <id> argument\n",
143 file, linenum, args[0]);
144 err_code |= ERR_ALERT | ERR_ABORT;
145 goto error;
146 }
147
148 ext_child = calloc(1, sizeof(*ext_child));
149 if (!ext_child) {
150 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
151 err_code |= ERR_ALERT | ERR_ABORT;
152 goto error;
153 }
154
William Lallemand8f7069a2019-04-12 16:09:23 +0200155 ext_child->options |= PROC_O_TYPE_PROG; /* external process */
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200156 ext_child->command = NULL;
157 ext_child->path = NULL;
158 ext_child->id = NULL;
159 ext_child->pid = -1;
160 ext_child->relative_pid = -1;
161 ext_child->reloads = 0;
162 ext_child->timestamp = -1;
163 ext_child->ipc_fd[0] = -1;
164 ext_child->ipc_fd[1] = -1;
William Lallemandbd3de3e2019-04-12 16:09:22 +0200165 ext_child->options |= PROC_O_START_RELOAD; /* restart the programs by default */
Andrew Heberle97236962019-07-12 11:50:26 +0800166 ext_child->uid = -1;
167 ext_child->gid = -1;
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200168 LIST_INIT(&ext_child->list);
169
170 list_for_each_entry(child, &proc_list, list) {
William Lallemand8f7069a2019-04-12 16:09:23 +0200171 if (child->reloads == 0 && (child->options & PROC_O_TYPE_PROG)) {
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200172 if (!strcmp(args[1], child->id)) {
173 ha_alert("parsing [%s:%d]: '%s' program section already exists in the configuration.\n", file, linenum, args[1]);
174 err_code |= ERR_ALERT | ERR_ABORT;
175 goto error;
176 }
177 }
178 }
179
180 ext_child->id = strdup(args[1]);
181 if (!ext_child->id) {
182 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
183 err_code |= ERR_ALERT | ERR_ABORT;
184 goto error;
185 }
186
187 LIST_ADDQ(&proc_list, &ext_child->list);
188
189 } else if (!strcmp(args[0], "command")) {
190 int arg_nb = 0;
191 int i = 0;
192
193 if (*(args[1]) == 0) {
194 ha_alert("parsing [%s:%d]: '%s' expects a command with optional arguments separated in words.\n", file, linenum, args[0]);
195 err_code |= ERR_ALERT | ERR_FATAL;
196 goto error;
197 }
198
199 while (*args[arg_nb+1])
200 arg_nb++;
201
202 ext_child->command = calloc(arg_nb+1, sizeof(*ext_child->command));
203
204 if (!ext_child->command) {
205 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
206 err_code |= ERR_ALERT | ERR_ABORT;
207 goto error;
208 }
209
210 while (i < arg_nb) {
211 ext_child->command[i] = strdup(args[i+1]);
212 if (!ext_child->command[i]) {
213 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
214 err_code |= ERR_ALERT | ERR_ABORT;
215 goto error;
216 }
217 i++;
218 }
219 ext_child->command[i] = NULL;
220
William Lallemandbd3de3e2019-04-12 16:09:22 +0200221 } else if (!strcmp(args[0], "option")) {
222
223 if (*(args[1]) == '\0') {
224 ha_alert("parsing [%s:%d]: '%s' expects an option name.\n",
225 file, linenum, args[0]);
226 err_code |= ERR_ALERT | ERR_FATAL;
227 goto error;
228 }
229
230 if (strcmp(args[1], "start-on-reload") == 0) {
231 if (alertif_too_many_args_idx(0, 1, file, linenum, args, &err_code))
232 goto error;
233 if (kwm == KWM_STD)
234 ext_child->options |= PROC_O_START_RELOAD;
235 else if (kwm == KWM_NO)
236 ext_child->options &= ~PROC_O_START_RELOAD;
237 goto out;
238
239 } else {
240 ha_alert("parsing [%s:%d] : unknown option '%s'.\n", file, linenum, args[1]);
241 err_code |= ERR_ALERT | ERR_FATAL;
242 goto error;
243 }
Andrew Heberle97236962019-07-12 11:50:26 +0800244 } else if (!strcmp(args[0], "user")) {
245 struct passwd *ext_child_user;
246 if (*(args[1]) == '\0') {
247 ha_alert("parsing [%s:%d]: '%s' expects a user name.\n",
248 file, linenum, args[0]);
249 err_code |= ERR_ALERT | ERR_FATAL;
250 goto error;
251 }
252
253 if (alertif_too_many_args(1, file, linenum, args, &err_code))
254 goto error;
255
256 if (ext_child->uid != -1) {
257 ha_alert("parsing [%s:%d] : user/uid already specified. Continuing.\n", file, linenum);
258 err_code |= ERR_ALERT;
259 goto out;
260 }
261
262 ext_child_user = getpwnam(args[1]);
263 if (ext_child_user != NULL) {
264 ext_child->uid = (int)ext_child_user->pw_uid;
265 } else {
266 ha_alert("parsing [%s:%d] : cannot find user id for '%s' (%d:%s)\n", file, linenum, args[1], errno, strerror(errno));
267 err_code |= ERR_ALERT | ERR_FATAL;
268 }
269 } else if (!strcmp(args[0], "group")) {
270 struct group *ext_child_group;
271 if (*(args[1]) == '\0') {
272 ha_alert("parsing [%s:%d]: '%s' expects a group name.\n",
273 file, linenum, args[0]);
274 err_code |= ERR_ALERT | ERR_FATAL;
275 goto error;
276 }
277
278 if (alertif_too_many_args(1, file, linenum, args, &err_code))
279 goto error;
280
281 if (ext_child->gid != -1) {
282 ha_alert("parsing [%s:%d] : group/gid already specified. Continuing.\n", file, linenum);
283 err_code |= ERR_ALERT;
284 goto out;
285 }
286
287 ext_child_group = getgrnam(args[1]);
288 if (ext_child_group != NULL) {
289 ext_child->gid = (int)ext_child_group->gr_gid;
290 } else {
291 ha_alert("parsing [%s:%d] : cannot find group id for '%s' (%d:%s)\n", file, linenum, args[1], errno, strerror(errno));
292 err_code |= ERR_ALERT | ERR_FATAL;
293 }
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200294 } else {
295 ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section\n", file, linenum, args[0], "program");
296 err_code |= ERR_ALERT | ERR_FATAL;
297 goto error;
298 }
299
300 use_program = 1;
301
302 return err_code;
303
304error:
Tim Duesterhus2c9e2742019-06-23 22:10:12 +0200305 if (ext_child) {
306 LIST_DEL(&ext_child->list);
307 if (ext_child->command) {
308 int i;
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200309
Tim Duesterhus2c9e2742019-06-23 22:10:12 +0200310 for (i = 0; ext_child->command[i]; i++) {
311 if (ext_child->command[i]) {
312 free(ext_child->command[i]);
313 ext_child->command[i] = NULL;
314 }
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200315 }
Tim Duesterhus2c9e2742019-06-23 22:10:12 +0200316 free(ext_child->command);
317 ext_child->command = NULL;
318 }
319 if (ext_child->id) {
320 free(ext_child->id);
321 ext_child->id = NULL;
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200322 }
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200323 }
324
325 free(ext_child);
326 ext_child = NULL;
327
William Lallemandbd3de3e2019-04-12 16:09:22 +0200328out:
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200329 return err_code;
330
331}
332
333int cfg_program_postparser()
334{
335 int err_code = 0;
336 struct mworker_proc *child;
337
338 list_for_each_entry(child, &proc_list, list) {
William Lallemand8f7069a2019-04-12 16:09:23 +0200339 if (child->reloads == 0 && (child->options & PROC_O_TYPE_PROG)) {
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200340 if (child->command == NULL) {
341 ha_alert("The program section '%s' lacks a command to launch.\n", child->id);
342 err_code |= ERR_ALERT | ERR_FATAL;
343 }
344 }
345 }
346
347 if (use_program && !(global.mode & MODE_MWORKER)) {
348 ha_alert("Can't use a 'program' section without master worker mode.\n");
349 err_code |= ERR_ALERT | ERR_FATAL;
350 }
351
352 return err_code;
353}
354
355
356REGISTER_CONFIG_SECTION("program", cfg_parse_program, NULL);
357REGISTER_CONFIG_POSTPARSER("program", cfg_program_postparser);