blob: fd8e66384847ee13adbf7cb4bf7b7ff1e5c71f80 [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>
18#include <stdio.h>
19#include <string.h>
20#include <unistd.h>
21
22#include <common/cfgparse.h>
23#include <common/errors.h>
24#include <common/initcall.h>
25
26#include <proto/log.h>
27#include <proto/mworker.h>
28
29static int use_program = 0; /* do we use the program section ? */
30
31/*
32 * Launch every programs
33 */
34int mworker_ext_launch_all()
35{
36 int ret;
37 struct mworker_proc *child;
William Lallemandbd3de3e2019-04-12 16:09:22 +020038 struct mworker_proc *tmp;
39 int reexec = 0;
William Lallemand9a1ee7a2019-04-01 11:30:02 +020040
41 if (!use_program)
42 return 0;
43
William Lallemandbd3de3e2019-04-12 16:09:22 +020044 reexec = getenv("HAPROXY_MWORKER_REEXEC") ? 1 : 0;
45
William Lallemand9a1ee7a2019-04-01 11:30:02 +020046 /* find the right mworker_proc */
William Lallemandbd3de3e2019-04-12 16:09:22 +020047 list_for_each_entry_safe(child, tmp, &proc_list, list) {
William Lallemand8f7069a2019-04-12 16:09:23 +020048 if (child->reloads == 0 && (child->options & PROC_O_TYPE_PROG)) {
William Lallemandbd3de3e2019-04-12 16:09:22 +020049
50 if (reexec && (!(child->options & PROC_O_START_RELOAD))) {
51 struct mworker_proc *old_child;
52
53 /*
54 * This is a reload and we don't want to fork a
55 * new program so have to remove the entry in
56 * the list.
57 *
58 * But before that, we need to mark the
59 * previous program as not leaving, if we find one.
60 */
61
62 list_for_each_entry(old_child, &proc_list, list) {
William Lallemand8f7069a2019-04-12 16:09:23 +020063 if (!(old_child->options & PROC_O_TYPE_PROG) || (!(old_child->options & PROC_O_LEAVING)))
William Lallemandbd3de3e2019-04-12 16:09:22 +020064 continue;
65
66 if (!strcmp(old_child->id, child->id))
67 old_child->options &= ~PROC_O_LEAVING;
68 }
69
70
71 LIST_DEL(&child->list);
72 if (child->command) {
73 int i;
74
75 for (i = 0; child->command[i]; i++) {
76 if (child->command[i]) {
77 free(child->command[i]);
78 child->command[i] = NULL;
79 }
80 }
81 free(child->command);
82 child->command = NULL;
83 }
84 if (child->id) {
85 free(child->id);
86 child->id = NULL;
87 }
88
89 free(child);
90 child = NULL;
91
92 continue;
93 }
94
William Lallemand9a1ee7a2019-04-01 11:30:02 +020095 child->timestamp = now.tv_sec;
96
97 ret = fork();
98 if (ret < 0) {
99 ha_alert("Cannot fork program '%s'.\n", child->id);
100 exit(EXIT_FAILURE); /* there has been an error */
101 } else if (ret > 0) { /* parent */
102 child->pid = ret;
103 ha_notice("New program '%s' (%d) forked\n", child->id, ret);
104 continue;
105 } else if (ret == 0) {
106 /* In child */
107 mworker_unblock_signals();
108 mworker_cleanlisteners();
109 mworker_cleantasks();
110
111 execvp(child->command[0], child->command);
112
113 ha_alert("Cannot execute %s: %s\n", child->command[0], strerror(errno));
114 exit(EXIT_FAILURE);
115 }
116 }
117 }
118
119 return 0;
120
121}
122
123
124/* Configuration */
125
126int cfg_parse_program(const char *file, int linenum, char **args, int kwm)
127{
128 static struct mworker_proc *ext_child = NULL;
129 struct mworker_proc *child;
130 int err_code = 0;
131
132 if (!strcmp(args[0], "program")) {
133 if (alertif_too_many_args(1, file, linenum, args, &err_code)) {
134 err_code |= ERR_ABORT;
135 goto error;
136 }
137
138 if (!*args[1]) {
139 ha_alert("parsing [%s:%d] : '%s' expects an <id> argument\n",
140 file, linenum, args[0]);
141 err_code |= ERR_ALERT | ERR_ABORT;
142 goto error;
143 }
144
145 ext_child = calloc(1, sizeof(*ext_child));
146 if (!ext_child) {
147 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
148 err_code |= ERR_ALERT | ERR_ABORT;
149 goto error;
150 }
151
William Lallemand8f7069a2019-04-12 16:09:23 +0200152 ext_child->options |= PROC_O_TYPE_PROG; /* external process */
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200153 ext_child->command = NULL;
154 ext_child->path = NULL;
155 ext_child->id = NULL;
156 ext_child->pid = -1;
157 ext_child->relative_pid = -1;
158 ext_child->reloads = 0;
159 ext_child->timestamp = -1;
160 ext_child->ipc_fd[0] = -1;
161 ext_child->ipc_fd[1] = -1;
William Lallemandbd3de3e2019-04-12 16:09:22 +0200162 ext_child->options |= PROC_O_START_RELOAD; /* restart the programs by default */
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200163 LIST_INIT(&ext_child->list);
164
165 list_for_each_entry(child, &proc_list, list) {
William Lallemand8f7069a2019-04-12 16:09:23 +0200166 if (child->reloads == 0 && (child->options & PROC_O_TYPE_PROG)) {
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200167 if (!strcmp(args[1], child->id)) {
168 ha_alert("parsing [%s:%d]: '%s' program section already exists in the configuration.\n", file, linenum, args[1]);
169 err_code |= ERR_ALERT | ERR_ABORT;
170 goto error;
171 }
172 }
173 }
174
175 ext_child->id = strdup(args[1]);
176 if (!ext_child->id) {
177 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
178 err_code |= ERR_ALERT | ERR_ABORT;
179 goto error;
180 }
181
182 LIST_ADDQ(&proc_list, &ext_child->list);
183
184 } else if (!strcmp(args[0], "command")) {
185 int arg_nb = 0;
186 int i = 0;
187
188 if (*(args[1]) == 0) {
189 ha_alert("parsing [%s:%d]: '%s' expects a command with optional arguments separated in words.\n", file, linenum, args[0]);
190 err_code |= ERR_ALERT | ERR_FATAL;
191 goto error;
192 }
193
194 while (*args[arg_nb+1])
195 arg_nb++;
196
197 ext_child->command = calloc(arg_nb+1, sizeof(*ext_child->command));
198
199 if (!ext_child->command) {
200 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
201 err_code |= ERR_ALERT | ERR_ABORT;
202 goto error;
203 }
204
205 while (i < arg_nb) {
206 ext_child->command[i] = strdup(args[i+1]);
207 if (!ext_child->command[i]) {
208 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
209 err_code |= ERR_ALERT | ERR_ABORT;
210 goto error;
211 }
212 i++;
213 }
214 ext_child->command[i] = NULL;
215
William Lallemandbd3de3e2019-04-12 16:09:22 +0200216 } else if (!strcmp(args[0], "option")) {
217
218 if (*(args[1]) == '\0') {
219 ha_alert("parsing [%s:%d]: '%s' expects an option name.\n",
220 file, linenum, args[0]);
221 err_code |= ERR_ALERT | ERR_FATAL;
222 goto error;
223 }
224
225 if (strcmp(args[1], "start-on-reload") == 0) {
226 if (alertif_too_many_args_idx(0, 1, file, linenum, args, &err_code))
227 goto error;
228 if (kwm == KWM_STD)
229 ext_child->options |= PROC_O_START_RELOAD;
230 else if (kwm == KWM_NO)
231 ext_child->options &= ~PROC_O_START_RELOAD;
232 goto out;
233
234 } else {
235 ha_alert("parsing [%s:%d] : unknown option '%s'.\n", file, linenum, args[1]);
236 err_code |= ERR_ALERT | ERR_FATAL;
237 goto error;
238 }
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200239 } else {
240 ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section\n", file, linenum, args[0], "program");
241 err_code |= ERR_ALERT | ERR_FATAL;
242 goto error;
243 }
244
245 use_program = 1;
246
247 return err_code;
248
249error:
250 LIST_DEL(&ext_child->list);
251 if (ext_child->command) {
252 int i;
253
254 for (i = 0; ext_child->command[i]; i++) {
255 if (ext_child->command[i]) {
256 free(ext_child->command[i]);
257 ext_child->command[i] = NULL;
258 }
259 }
260 free(ext_child->command);
261 ext_child->command = NULL;
262 }
263 if (ext_child->id) {
264 free(ext_child->id);
265 ext_child->id = NULL;
266 }
267
268 free(ext_child);
269 ext_child = NULL;
270
William Lallemandbd3de3e2019-04-12 16:09:22 +0200271out:
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200272 return err_code;
273
274}
275
276int cfg_program_postparser()
277{
278 int err_code = 0;
279 struct mworker_proc *child;
280
281 list_for_each_entry(child, &proc_list, list) {
William Lallemand8f7069a2019-04-12 16:09:23 +0200282 if (child->reloads == 0 && (child->options & PROC_O_TYPE_PROG)) {
William Lallemand9a1ee7a2019-04-01 11:30:02 +0200283 if (child->command == NULL) {
284 ha_alert("The program section '%s' lacks a command to launch.\n", child->id);
285 err_code |= ERR_ALERT | ERR_FATAL;
286 }
287 }
288 }
289
290 if (use_program && !(global.mode & MODE_MWORKER)) {
291 ha_alert("Can't use a 'program' section without master worker mode.\n");
292 err_code |= ERR_ALERT | ERR_FATAL;
293 }
294
295 return err_code;
296}
297
298
299REGISTER_CONFIG_SECTION("program", cfg_parse_program, NULL);
300REGISTER_CONFIG_POSTPARSER("program", cfg_program_postparser);