/src/haproxy/src/haproxy.c
Line | Count | Source |
1 | | /* |
2 | | * HAProxy : High Availability-enabled HTTP/TCP proxy |
3 | | * Copyright 2000-2026 Willy Tarreau <willy@haproxy.org>. |
4 | | * |
5 | | * This program is free software; you can redistribute it and/or |
6 | | * modify it under the terms of the GNU General Public License |
7 | | * as published by the Free Software Foundation; either version |
8 | | * 2 of the License, or (at your option) any later version. |
9 | | */ |
10 | | |
11 | | #define _GNU_SOURCE |
12 | | #include <stdio.h> |
13 | | #include <stdlib.h> |
14 | | #include <unistd.h> |
15 | | #include <string.h> |
16 | | #include <ctype.h> |
17 | | #include <dirent.h> |
18 | | #include <sys/stat.h> |
19 | | #include <sys/time.h> |
20 | | #include <sys/types.h> |
21 | | #include <sys/socket.h> |
22 | | #include <netinet/tcp.h> |
23 | | #include <netinet/in.h> |
24 | | #include <arpa/inet.h> |
25 | | #include <netdb.h> |
26 | | #include <signal.h> |
27 | | #include <stdarg.h> |
28 | | #include <sys/resource.h> |
29 | | #include <sys/utsname.h> |
30 | | #include <sys/wait.h> |
31 | | #include <time.h> |
32 | | #include <syslog.h> |
33 | | #include <grp.h> |
34 | | |
35 | | #ifdef USE_THREAD |
36 | | #include <pthread.h> |
37 | | #endif |
38 | | |
39 | | #ifdef USE_CPU_AFFINITY |
40 | | #include <sched.h> |
41 | | #if defined(__FreeBSD__) || defined(__DragonFly__) |
42 | | #include <sys/param.h> |
43 | | #ifdef __FreeBSD__ |
44 | | #include <sys/cpuset.h> |
45 | | #endif |
46 | | #endif |
47 | | #endif |
48 | | |
49 | | #if defined(USE_PRCTL) |
50 | | #include <sys/prctl.h> |
51 | | #endif |
52 | | |
53 | | #if defined(USE_PROCCTL) |
54 | | #include <sys/procctl.h> |
55 | | #endif |
56 | | |
57 | | #ifdef DEBUG_FULL |
58 | | #include <assert.h> |
59 | | #endif |
60 | | |
61 | | #include <import/sha1.h> |
62 | | |
63 | | #include <haproxy/acl.h> |
64 | | #include <haproxy/action.h> |
65 | | #include <haproxy/activity.h> |
66 | | #include <haproxy/api.h> |
67 | | #include <haproxy/arg.h> |
68 | | #include <haproxy/auth.h> |
69 | | #include <haproxy/base64.h> |
70 | | #include <haproxy/capture-t.h> |
71 | | #include <haproxy/cfgcond.h> |
72 | | #include <haproxy/cfgdiag.h> |
73 | | #include <haproxy/cfgparse.h> |
74 | | #include <haproxy/chunk.h> |
75 | | #include <haproxy/cli.h> |
76 | | #include <haproxy/clock.h> |
77 | | #include <haproxy/connection.h> |
78 | | #include <haproxy/counters.h> |
79 | | #ifdef USE_CPU_AFFINITY |
80 | | #include <haproxy/cpuset.h> |
81 | | #include <haproxy/cpu_topo.h> |
82 | | #endif |
83 | | #include <haproxy/debug.h> |
84 | | #include <haproxy/dns.h> |
85 | | #include <haproxy/dynbuf.h> |
86 | | #include <haproxy/errors.h> |
87 | | #include <haproxy/fd.h> |
88 | | #include <haproxy/filters.h> |
89 | | #include <haproxy/global.h> |
90 | | #include <haproxy/hlua.h> |
91 | | #include <haproxy/http_rules.h> |
92 | | #include <haproxy/limits.h> |
93 | | #if defined(USE_LINUX_CAP) |
94 | | #include <haproxy/linuxcap.h> |
95 | | #endif |
96 | | #include <haproxy/list.h> |
97 | | #include <haproxy/listener.h> |
98 | | #include <haproxy/log.h> |
99 | | #include <haproxy/mworker.h> |
100 | | #include <haproxy/namespace.h> |
101 | | #include <haproxy/net_helper.h> |
102 | | #include <haproxy/openssl-compat.h> |
103 | | #include <haproxy/pattern.h> |
104 | | #include <haproxy/peers.h> |
105 | | #include <haproxy/pool.h> |
106 | | #include <haproxy/protocol.h> |
107 | | #include <haproxy/proto_sockpair.h> |
108 | | #include <haproxy/proto_tcp.h> |
109 | | #include <haproxy/proxy.h> |
110 | | #include <haproxy/regex.h> |
111 | | #include <haproxy/resolvers.h> |
112 | | #include <haproxy/sample.h> |
113 | | #include <haproxy/server.h> |
114 | | #include <haproxy/session.h> |
115 | | #include <haproxy/signal.h> |
116 | | #include <haproxy/sock.h> |
117 | | #include <haproxy/sock_inet.h> |
118 | | #include <haproxy/ssl_sock.h> |
119 | | #include <haproxy/stats-file.h> |
120 | | #include <haproxy/stats-t.h> |
121 | | #include <haproxy/stream.h> |
122 | | #include <haproxy/systemd.h> |
123 | | #include <haproxy/task.h> |
124 | | #include <haproxy/thread.h> |
125 | | #include <haproxy/time.h> |
126 | | #include <haproxy/tools.h> |
127 | | #include <haproxy/trace.h> |
128 | | #include <haproxy/uri_auth-t.h> |
129 | | #include <haproxy/vars.h> |
130 | | #include <haproxy/version.h> |
131 | | |
132 | | |
133 | | /* array of init calls for older platforms */ |
134 | | DECLARE_INIT_STAGES; |
135 | | |
136 | | /* create a read_mostly section to hold variables which are accessed a lot |
137 | | * but which almost never change. The purpose is to isolate them in their |
138 | | * own cache lines where they don't risk to be perturbated by write accesses |
139 | | * to neighbor variables. We need to create an empty aligned variable for |
140 | | * this. The fact that the variable is of size zero means that it will be |
141 | | * eliminated at link time if no other variable uses it, but alignment will |
142 | | * be respected. |
143 | | */ |
144 | | empty_t __read_mostly_align HA_SECTION("read_mostly") ALIGNED(64); |
145 | | |
146 | | /* list of config files */ |
147 | | static struct list cfg_cfgfiles = LIST_HEAD_INIT(cfg_cfgfiles); |
148 | | int pid; /* current process id */ |
149 | | char **init_env; /* to keep current process env variables backup */ |
150 | | int pidfd = -1; /* FD to keep PID */ |
151 | | int daemon_fd[2] = {-1, -1}; /* pipe to communicate with parent process */ |
152 | | int devnullfd = -1; |
153 | | int fileless_mode; |
154 | | struct cfgfile fileless_cfg; |
155 | | extern __attribute__((weak)) void haproxy_init_args(int argc, char **argv); |
156 | | extern __attribute__((weak)) char **copy_argv(int argc, char **argv); |
157 | | |
158 | | static int stopped_tgroups; |
159 | | static int stop_detected; |
160 | | |
161 | | /* global options */ |
162 | | struct global global = { |
163 | | .uid = -1, // not set |
164 | | .gid = -1, // not set |
165 | | .hard_stop_after = TICK_ETERNITY, |
166 | | .close_spread_time = TICK_ETERNITY, |
167 | | .close_spread_end = TICK_ETERNITY, |
168 | | .numa_cpu_mapping = 1, |
169 | | .nbthread = 0, |
170 | | .req_count = 0, |
171 | | .loggers = LIST_HEAD_INIT(global.loggers), |
172 | | .maxzlibmem = DEFAULT_MAXZLIBMEM * 1024U * 1024U, |
173 | | .comp_rate_lim = 0, |
174 | | .ssl_server_verify = SSL_SERVER_VERIFY_REQUIRED, |
175 | | .unix_bind = { |
176 | | .ux = { |
177 | | .uid = -1, |
178 | | .gid = -1, |
179 | | .mode = 0, |
180 | | } |
181 | | }, |
182 | | .tune = { |
183 | | .options = GTUNE_LISTENER_MQ_OPT, |
184 | | .bufsize = (BUFSIZE + 2*sizeof(void *) - 1) & -(2*sizeof(void *)), |
185 | | .bufsize_small = BUFSIZE_SMALL, |
186 | | .bufsize_large = 0, |
187 | | .maxrewrite = MAXREWRITE, |
188 | | .reserved_bufs = RESERVED_BUFS, |
189 | | .pattern_cache = DEFAULT_PAT_LRU_SIZE, |
190 | | .pool_low_ratio = 20, |
191 | | .pool_high_ratio = 25, |
192 | | .max_http_hdr = MAX_HTTP_HDR, |
193 | | #ifdef USE_OPENSSL |
194 | | .sslcachesize = SSLCACHESIZE, |
195 | | #endif |
196 | | .comp_maxlevel = 1, |
197 | | .glitch_kill_maxidle = 100, |
198 | | #ifdef DEFAULT_IDLE_TIMER |
199 | | .idle_timer = DEFAULT_IDLE_TIMER, |
200 | | #else |
201 | | .idle_timer = 1000, /* 1 second */ |
202 | | #endif |
203 | | .nb_stk_ctr = MAX_SESS_STKCTR, |
204 | | .default_shards = -2, /* by-group */ |
205 | | }, |
206 | | #ifdef USE_OPENSSL |
207 | | #ifdef DEFAULT_MAXSSLCONN |
208 | | .maxsslconn = DEFAULT_MAXSSLCONN, |
209 | | #endif |
210 | | #endif |
211 | | /* by default allow clients which use a privileged port for TCP only */ |
212 | | .clt_privileged_ports = HA_PROTO_TCP, |
213 | | .maxthrpertgroup = MAX_THREADS_PER_GROUP, |
214 | | /* others NULL OK */ |
215 | | }; |
216 | | |
217 | | /*********************************************************************/ |
218 | | |
219 | | int stopping; /* non zero means stopping in progress */ |
220 | | int killed; /* non zero means a hard-stop is triggered */ |
221 | | int jobs = 0; /* number of active jobs (conns, listeners, active tasks, ...) */ |
222 | | int unstoppable_jobs = 0; /* number of active jobs that can't be stopped during a soft stop */ |
223 | | int active_peers = 0; /* number of active peers (connection attempts and connected) */ |
224 | | int connected_peers = 0; /* number of connected peers (verified ones) */ |
225 | | int arg_mode = 0; /* MODE_DEBUG etc as passed on command line ... */ |
226 | | char *change_dir = NULL; /* set when -C is passed */ |
227 | | char *check_condition = NULL; /* check condition passed to -cc */ |
228 | | char *progname = NULL; /* HAProxy binary's name */ |
229 | | |
230 | | /* Here we store information about the pids of the processes we may pause |
231 | | * or kill. We will send them a signal every 10 ms until we can bind to all |
232 | | * our ports. With 200 retries, that's about 2 seconds. |
233 | | */ |
234 | 0 | #define MAX_START_RETRIES 200 |
235 | | static int *oldpids = NULL; |
236 | | int oldpids_sig; /* use USR1 or TERM */ |
237 | | |
238 | | /* Path to the unix socket we use to retrieve listener sockets from the old process */ |
239 | | const char *old_unixsocket; |
240 | | |
241 | | int atexit_flag = 0; |
242 | | |
243 | | int nb_oldpids = 0; |
244 | | const int zero = 0; |
245 | | const int one = 1; |
246 | | const struct linger nolinger = { .l_onoff = 1, .l_linger = 0 }; |
247 | | |
248 | | char hostname[MAX_HOSTNAME_LEN]; |
249 | | char *localpeer = NULL; |
250 | | static char *kwd_dump = NULL; // list of keyword dumps to produce |
251 | | |
252 | | char **old_argv = NULL; /* previous argv but cleaned up */ |
253 | | |
254 | | struct list proc_list = LIST_HEAD_INIT(proc_list); |
255 | | |
256 | | #ifdef DEBUG_UNIT |
257 | | struct list unittest_list = LIST_HEAD_INIT(unittest_list); |
258 | | static int unittest_argc = -1; |
259 | | #endif |
260 | | |
261 | | int master = 0; /* 1 if in master, 0 if in child */ |
262 | | |
263 | | /* per-boot randomness */ |
264 | | unsigned char boot_seed[20]; /* per-boot random seed (160 bits initially) */ |
265 | | |
266 | | /* bitfield of a few warnings to emit just once (WARN_*) */ |
267 | | unsigned int warned = 0; |
268 | | |
269 | | /* set if experimental features have been used for the current process */ |
270 | | unsigned int tainted = 0; |
271 | | |
272 | | unsigned int experimental_directives_allowed = 0; |
273 | | unsigned int deprecated_directives_allowed = 0; |
274 | | |
275 | | int check_kw_experimental(struct cfg_keyword *kw, const char *file, int linenum, |
276 | | char **errmsg) |
277 | 0 | { |
278 | 0 | if (kw->flags & KWF_EXPERIMENTAL) { |
279 | 0 | if (!experimental_directives_allowed) { |
280 | 0 | memprintf(errmsg, "parsing [%s:%d] : '%s' directive is experimental, must be allowed via a global 'expose-experimental-directives'", |
281 | 0 | file, linenum, kw->kw); |
282 | 0 | return 1; |
283 | 0 | } |
284 | 0 | mark_tainted(TAINTED_CONFIG_EXP_KW_DECLARED); |
285 | 0 | } |
286 | | |
287 | 0 | return 0; |
288 | 0 | } |
289 | | |
290 | | /* These are strings to be reported in the output of "haproxy -vv". They may |
291 | | * either be constants (in which case must_free must be zero) or dynamically |
292 | | * allocated strings to pass to free() on exit, and in this case must_free |
293 | | * must be non-zero. |
294 | | */ |
295 | | struct list build_opts_list = LIST_HEAD_INIT(build_opts_list); |
296 | | struct build_opts_str { |
297 | | struct list list; |
298 | | const char *str; |
299 | | int must_free; |
300 | | }; |
301 | | |
302 | | int mode_stress_level = 0; |
303 | | |
304 | | /*********************************************************************/ |
305 | | /* general purpose functions ***************************************/ |
306 | | /*********************************************************************/ |
307 | | |
308 | | /* used to register some build option strings at boot. Set must_free to |
309 | | * non-zero if the string must be freed upon exit. |
310 | | */ |
311 | | void hap_register_build_opts(const char *str, int must_free) |
312 | 0 | { |
313 | 0 | struct build_opts_str *b; |
314 | |
|
315 | 0 | b = calloc(1, sizeof(*b)); |
316 | 0 | if (!b) { |
317 | 0 | fprintf(stderr, "out of memory\n"); |
318 | 0 | exit(1); |
319 | 0 | } |
320 | 0 | b->str = str; |
321 | 0 | b->must_free = must_free; |
322 | 0 | LIST_APPEND(&build_opts_list, &b->list); |
323 | 0 | } |
324 | | |
325 | | /* returns the first build option when <curr> is NULL, or the next one when |
326 | | * <curr> is passed the last returned value. NULL when there is no more entries |
327 | | * in the list. Otherwise the returned pointer is &opt->str so the caller can |
328 | | * print it as *ret. |
329 | | */ |
330 | | const char **hap_get_next_build_opt(const char **curr) |
331 | 0 | { |
332 | 0 | struct build_opts_str *head, *start; |
333 | |
|
334 | 0 | head = container_of(&build_opts_list, struct build_opts_str, list); |
335 | |
|
336 | 0 | if (curr) |
337 | 0 | start = container_of(curr, struct build_opts_str, str); |
338 | 0 | else |
339 | 0 | start = head; |
340 | |
|
341 | 0 | start = container_of(start->list.n, struct build_opts_str, list); |
342 | |
|
343 | 0 | if (start == head) |
344 | 0 | return NULL; |
345 | | |
346 | 0 | return &start->str; |
347 | 0 | } |
348 | | |
349 | | /* used to make a new feature appear in the build_features list at boot time. |
350 | | * The feature must be in the format "XXX" without the leading "+" which will |
351 | | * be automatically appended. |
352 | | */ |
353 | | void hap_register_feature(const char *name) |
354 | 0 | { |
355 | 0 | static int must_free = 0; |
356 | 0 | int new_len = strlen(build_features) + 2 + strlen(name); |
357 | 0 | char *new_features; |
358 | 0 | char *startp, *endp; |
359 | 0 | int found = 0; |
360 | |
|
361 | 0 | new_features = malloc(new_len + 1); |
362 | 0 | if (!new_features) |
363 | 0 | return; |
364 | | |
365 | 0 | strlcpy2(new_features, build_features, new_len); |
366 | |
|
367 | 0 | startp = new_features; |
368 | | |
369 | | /* look if the string already exists */ |
370 | 0 | while (startp) { |
371 | 0 | char *sign = startp; |
372 | | |
373 | | /* tokenize for simpler strcmp */ |
374 | 0 | endp = strchr(startp, ' '); |
375 | 0 | if (endp) |
376 | 0 | *endp = '\0'; |
377 | |
|
378 | 0 | startp++; /* skip sign */ |
379 | |
|
380 | 0 | if (strcmp(startp, name) == 0) { |
381 | 0 | *sign = '+'; |
382 | 0 | found = 1; |
383 | 0 | } |
384 | | |
385 | | /* couldn't find a space, that's the end of the string */ |
386 | 0 | if (!endp) |
387 | 0 | break; |
388 | | |
389 | 0 | *endp = ' '; |
390 | 0 | startp = endp + 1; |
391 | |
|
392 | 0 | if (found) |
393 | 0 | break; |
394 | 0 | } |
395 | | |
396 | | /* if we didn't find the feature add it to the string */ |
397 | 0 | if (!found) |
398 | 0 | snprintf(new_features, new_len + 1, "%s +%s", build_features, name); |
399 | |
|
400 | 0 | if (must_free) |
401 | 0 | ha_free(&build_features); |
402 | |
|
403 | 0 | build_features = new_features; |
404 | 0 | must_free = 1; |
405 | 0 | } |
406 | | |
407 | | #ifdef DEBUG_UNIT |
408 | | /* register a function that could be registered in "-U" argument */ |
409 | | void hap_register_unittest(const char *name, int (*fct)(int argc, char **argv)) |
410 | | { |
411 | | struct unittest_fct *unit; |
412 | | |
413 | | if (!name || !fct) |
414 | | return; |
415 | | |
416 | | unit = calloc(1, sizeof(*unit)); |
417 | | unit->fct = fct; |
418 | | unit->name = name; |
419 | | LIST_APPEND(&unittest_list, &unit->list); |
420 | | } |
421 | | #endif |
422 | | |
423 | 0 | #define VERSION_MAX_ELTS 7 |
424 | | |
425 | | /* This function splits an haproxy version string into an array of integers. |
426 | | * The syntax of the supported version string is the following: |
427 | | * |
428 | | * <a>[.<b>[.<c>[.<d>]]][-{dev,pre,rc}<f>][-*][-<g>] |
429 | | * |
430 | | * This validates for example: |
431 | | * 1.2.1-pre2, 1.2.1, 1.2.10.1, 1.3.16-rc1, 1.4-dev3, 1.5-dev18, 1.5-dev18-43 |
432 | | * 2.4-dev18-f6818d-20 |
433 | | * |
434 | | * The result is set in a array of <VERSION_MAX_ELTS> elements. Each letter has |
435 | | * one fixed place in the array. The tags take a numeric value called <e> which |
436 | | * defaults to 3. "dev" is 1, "rc" and "pre" are 2. Numbers not encountered are |
437 | | * considered as zero (henxe 1.5 and 1.5.0 are the same). |
438 | | * |
439 | | * The resulting values are: |
440 | | * 1.2.1-pre2 1, 2, 1, 0, 2, 2, 0 |
441 | | * 1.2.1 1, 2, 1, 0, 3, 0, 0 |
442 | | * 1.2.10.1 1, 2, 10, 1, 3, 0, 0 |
443 | | * 1.3.16-rc1 1, 3, 16, 0, 2, 1, 0 |
444 | | * 1.4-dev3 1, 4, 0, 0, 1, 3, 0 |
445 | | * 1.5-dev18 1, 5, 0, 0, 1, 18, 0 |
446 | | * 1.5-dev18-43 1, 5, 0, 0, 1, 18, 43 |
447 | | * 2.4-dev18-f6818d-20 2, 4, 0, 0, 1, 18, 20 |
448 | | * |
449 | | * The function returns non-zero if the conversion succeeded, or zero if it |
450 | | * failed. |
451 | | */ |
452 | | int split_version(const char *version, unsigned int *value) |
453 | 0 | { |
454 | 0 | const char *p, *s; |
455 | 0 | char *error; |
456 | 0 | int nelts; |
457 | | |
458 | | /* Initialize array with zeroes */ |
459 | 0 | for (nelts = 0; nelts < VERSION_MAX_ELTS; nelts++) |
460 | 0 | value[nelts] = 0; |
461 | 0 | value[4] = 3; |
462 | |
|
463 | 0 | p = version; |
464 | | |
465 | | /* If the version number is empty, return false */ |
466 | 0 | if (*p == '\0') |
467 | 0 | return 0; |
468 | | |
469 | | /* Convert first number <a> */ |
470 | 0 | value[0] = strtol(p, &error, 10); |
471 | 0 | p = error + 1; |
472 | 0 | if (*error == '\0') |
473 | 0 | return 1; |
474 | 0 | if (*error == '-') |
475 | 0 | goto split_version_tag; |
476 | 0 | if (*error != '.') |
477 | 0 | return 0; |
478 | | |
479 | | /* Convert first number <b> */ |
480 | 0 | value[1] = strtol(p, &error, 10); |
481 | 0 | p = error + 1; |
482 | 0 | if (*error == '\0') |
483 | 0 | return 1; |
484 | 0 | if (*error == '-') |
485 | 0 | goto split_version_tag; |
486 | 0 | if (*error != '.') |
487 | 0 | return 0; |
488 | | |
489 | | /* Convert first number <c> */ |
490 | 0 | value[2] = strtol(p, &error, 10); |
491 | 0 | p = error + 1; |
492 | 0 | if (*error == '\0') |
493 | 0 | return 1; |
494 | 0 | if (*error == '-') |
495 | 0 | goto split_version_tag; |
496 | 0 | if (*error != '.') |
497 | 0 | return 0; |
498 | | |
499 | | /* Convert first number <d> */ |
500 | 0 | value[3] = strtol(p, &error, 10); |
501 | 0 | p = error + 1; |
502 | 0 | if (*error == '\0') |
503 | 0 | return 1; |
504 | 0 | if (*error != '-') |
505 | 0 | return 0; |
506 | | |
507 | 0 | split_version_tag: |
508 | | /* Check for commit number */ |
509 | 0 | if (*p >= '0' && *p <= '9') |
510 | 0 | goto split_version_commit; |
511 | | |
512 | | /* Read tag */ |
513 | 0 | if (strncmp(p, "dev", 3) == 0) { value[4] = 1; p += 3; } |
514 | 0 | else if (strncmp(p, "rc", 2) == 0) { value[4] = 2; p += 2; } |
515 | 0 | else if (strncmp(p, "pre", 3) == 0) { value[4] = 2; p += 3; } |
516 | 0 | else |
517 | 0 | goto split_version_commit; |
518 | | |
519 | | /* Convert tag number */ |
520 | 0 | value[5] = strtol(p, &error, 10); |
521 | 0 | p = error + 1; |
522 | 0 | if (*error == '\0') |
523 | 0 | return 1; |
524 | 0 | if (*error != '-') |
525 | 0 | return 0; |
526 | | |
527 | 0 | split_version_commit: |
528 | | /* Search the last "-" */ |
529 | 0 | s = strrchr(p, '-'); |
530 | 0 | if (s) { |
531 | 0 | s++; |
532 | 0 | if (*s == '\0') |
533 | 0 | return 0; |
534 | 0 | value[6] = strtol(s, &error, 10); |
535 | 0 | if (*error != '\0') |
536 | 0 | value[6] = 0; |
537 | 0 | return 1; |
538 | 0 | } |
539 | | |
540 | | /* convert the version */ |
541 | 0 | value[6] = strtol(p, &error, 10); |
542 | 0 | if (*error != '\0') |
543 | 0 | value[6] = 0; |
544 | |
|
545 | 0 | return 1; |
546 | 0 | } |
547 | | |
548 | | /* This function compares the current haproxy version with an arbitrary version |
549 | | * string. It returns: |
550 | | * -1 : the version in argument is older than the current haproxy version |
551 | | * 0 : the version in argument is the same as the current haproxy version |
552 | | * 1 : the version in argument is newer than the current haproxy version |
553 | | * |
554 | | * Or some errors: |
555 | | * -2 : the current haproxy version is not parsable |
556 | | * -3 : the version in argument is not parsable |
557 | | */ |
558 | | int compare_current_version(const char *version) |
559 | 0 | { |
560 | 0 | unsigned int loc[VERSION_MAX_ELTS]; |
561 | 0 | unsigned int mod[VERSION_MAX_ELTS]; |
562 | 0 | int i; |
563 | | |
564 | | /* split versions */ |
565 | 0 | if (!split_version(haproxy_version, loc)) |
566 | 0 | return -2; |
567 | 0 | if (!split_version(version, mod)) |
568 | 0 | return -3; |
569 | | |
570 | | /* compare versions */ |
571 | 0 | for (i = 0; i < VERSION_MAX_ELTS; i++) { |
572 | 0 | if (mod[i] < loc[i]) |
573 | 0 | return -1; |
574 | 0 | else if (mod[i] > loc[i]) |
575 | 0 | return 1; |
576 | 0 | } |
577 | 0 | return 0; |
578 | 0 | } |
579 | | |
580 | | void display_version() |
581 | 0 | { |
582 | 0 | struct utsname utsname; |
583 | |
|
584 | 0 | printf("HAProxy version %s %s - https://haproxy.org/\n" |
585 | 0 | PRODUCT_STATUS "\n", haproxy_version, haproxy_date); |
586 | |
|
587 | 0 | if (strlen(PRODUCT_URL_BUGS) > 0) { |
588 | 0 | char base_version[20]; |
589 | 0 | int dots = 0; |
590 | 0 | char *del; |
591 | | |
592 | | /* only retrieve the base version without distro-specific extensions */ |
593 | 0 | for (del = haproxy_version; *del; del++) { |
594 | 0 | if (*del == '.') |
595 | 0 | dots++; |
596 | 0 | else if (*del < '0' || *del > '9') |
597 | 0 | break; |
598 | 0 | } |
599 | |
|
600 | 0 | strlcpy2(base_version, haproxy_version, del - haproxy_version + 1); |
601 | 0 | if (dots < 2) |
602 | 0 | printf("Known bugs: https://github.com/haproxy/haproxy/issues?q=is:issue+is:open\n"); |
603 | 0 | else |
604 | 0 | printf("Known bugs: " PRODUCT_URL_BUGS "\n", base_version); |
605 | 0 | } |
606 | |
|
607 | 0 | if (uname(&utsname) == 0) { |
608 | 0 | printf("Running on: %s %s %s %s\n", utsname.sysname, utsname.release, utsname.version, utsname.machine); |
609 | 0 | } |
610 | 0 | } |
611 | | |
612 | | /* compare a feature string, ignoring the first character (-/+) |
613 | | used for qsort */ |
614 | | static int feat_cmp(const void *a, const void *b) |
615 | 0 | { |
616 | 0 | const struct ist *ia = a; |
617 | 0 | const struct ist *ib = b; |
618 | |
|
619 | 0 | struct ist sa = istadv(*ia, 1); |
620 | 0 | struct ist sb = istadv(*ib, 1); |
621 | |
|
622 | 0 | return istdiff(sa, sb); |
623 | 0 | } |
624 | | |
625 | | /* split the feature list into an allocated sorted array of ist |
626 | | the return ptr must be freed by the caller */ |
627 | | static struct ist *split_feature_list() |
628 | 0 | { |
629 | 0 | struct ist *out; |
630 | 0 | struct ist tmp = ist(build_features); |
631 | |
|
632 | 0 | int n = 1; /* last element don't have a ' ' */ |
633 | 0 | int i = 0; |
634 | |
|
635 | 0 | for (i = 0; build_features[i] != '\0'; i++) { |
636 | 0 | if (build_features[i] == ' ') |
637 | 0 | n++; |
638 | 0 | } |
639 | 0 | out = calloc(n + 1, sizeof(*out)); // last elem is NULL |
640 | 0 | if (!out) |
641 | 0 | goto end; |
642 | | |
643 | 0 | i = 0; |
644 | 0 | while (tmp.len) |
645 | 0 | out[i++] = istsplit(&tmp, ' '); |
646 | |
|
647 | 0 | qsort(out, n, sizeof(struct ist), feat_cmp); |
648 | |
|
649 | 0 | end: |
650 | |
|
651 | 0 | return out; |
652 | 0 | } |
653 | | |
654 | | /* display_mode: |
655 | | * 0 = short version (e.g., "3.3.1") |
656 | | * 1 = full version (e.g., "3.3.1-dev5-1bb975-71") |
657 | | * 2 = branch version (e.g., "3.3") |
658 | | */ |
659 | | void display_version_plain(int display_mode) |
660 | 0 | { |
661 | 0 | char out[30] = ""; |
662 | 0 | int dots = 0; |
663 | 0 | int i; |
664 | |
|
665 | 0 | if (display_mode == 1) { |
666 | 0 | printf("%s\n", haproxy_version); |
667 | 0 | return; |
668 | 0 | } |
669 | | |
670 | 0 | for (i = 0; i < sizeof(out) - 1 && haproxy_version[i]; i++) { |
671 | 0 | if (display_mode == 2) { |
672 | 0 | if (haproxy_version[i] == '.') dots++; |
673 | 0 | if (dots == 2 || haproxy_version[i] == '-') { |
674 | 0 | out[i] = '\0'; |
675 | 0 | break; |
676 | 0 | } |
677 | 0 | } else { |
678 | 0 | if ((haproxy_version[i] < '0' || haproxy_version[i] > '9') && haproxy_version[i] != '.') { |
679 | 0 | out[i] = '\0'; |
680 | 0 | break; |
681 | 0 | } |
682 | 0 | } |
683 | 0 | out[i] = haproxy_version[i]; |
684 | 0 | out[i+1] = '\0'; |
685 | 0 | } |
686 | |
|
687 | 0 | printf("%s\n", out); |
688 | 0 | } |
689 | | |
690 | | static void display_build_opts() |
691 | 0 | { |
692 | 0 | const char **opt; |
693 | 0 | struct ist *feat_list = NULL, *tmp; |
694 | |
|
695 | 0 | feat_list = split_feature_list(); |
696 | |
|
697 | 0 | printf("Build options : %s", build_opts_string); |
698 | 0 | printf("\n\nFeature list :"); |
699 | 0 | for (tmp = feat_list;tmp->ptr;tmp++) |
700 | 0 | if (!isttest(istist(*tmp, ist("HAVE_WORKING_")))) |
701 | 0 | printf(" %.*s", (int)tmp->len, tmp->ptr); |
702 | 0 | printf("\nDetected feature list :"); |
703 | 0 | for (tmp = feat_list;tmp->ptr;tmp++) |
704 | 0 | if (isttest(istist(*tmp, ist("HAVE_WORKING_")))) |
705 | 0 | printf(" %.*s", (int)tmp->len, tmp->ptr); |
706 | 0 | printf("\n\nDefault settings :" |
707 | 0 | "\n bufsize = %d, maxrewrite = %d, maxpollevents = %d" |
708 | 0 | "\n\n", |
709 | 0 | BUFSIZE, MAXREWRITE, MAX_POLL_EVENTS); |
710 | |
|
711 | 0 | for (opt = NULL; (opt = hap_get_next_build_opt(opt)); puts(*opt)) |
712 | 0 | ; |
713 | |
|
714 | 0 | putchar('\n'); |
715 | |
|
716 | | #ifdef DEBUG_UNIT |
717 | | list_unittests(); |
718 | | putchar('\n'); |
719 | | #endif |
720 | 0 | list_pollers(stdout); |
721 | 0 | putchar('\n'); |
722 | 0 | list_mux_proto(stdout); |
723 | 0 | putchar('\n'); |
724 | 0 | list_services(stdout); |
725 | 0 | putchar('\n'); |
726 | 0 | list_filters(stdout); |
727 | 0 | putchar('\n'); |
728 | 0 | ha_free(&feat_list); |
729 | 0 | } |
730 | | |
731 | | /* |
732 | | * This function prints the command line usage and exits |
733 | | */ |
734 | | static void usage(char *name) |
735 | 0 | { |
736 | 0 | display_version(); |
737 | 0 | fprintf(stderr, |
738 | 0 | "Usage : %s [-f <cfgfile|cfgdir>]* [ -vdV" |
739 | 0 | "D ] [ -n <maxconn> ] [ -N <maxpconn> ]\n" |
740 | 0 | " [ -p <pidfile> ] [ -m <max megs> ] [ -C <dir> ] [-- <cfgfile>*]\n" |
741 | 0 | " -v displays version ; -vv shows known build options.\n" |
742 | 0 | " -vq/-vqs/-vqb only displays version, short version, branch.\n" |
743 | 0 | " -d enters debug mode ; -db only disables background mode.\n" |
744 | 0 | " -dM[<byte>,help,...] debug memory (default: poison with <byte>/0x50)\n" |
745 | 0 | " -dt activate traces on stderr; see '-dt help'\n" |
746 | 0 | " -V enters verbose mode (disables quiet mode)\n" |
747 | 0 | " -D goes daemon ; -C changes to <dir> before loading files.\n" |
748 | 0 | " -W master-worker mode.\n" |
749 | 0 | " -Ws master-worker mode with systemd notify support.\n" |
750 | 0 | " -q quiet mode : don't display messages\n" |
751 | 0 | " -c check mode : only check config files and exit\n" |
752 | 0 | " -cc check condition : evaluate a condition and exit\n" |
753 | 0 | " -4 force resolvers to consider IPv4 responses only\n" |
754 | 0 | " -n sets the maximum total # of connections (uses ulimit -n)\n" |
755 | 0 | " -m limits the usable amount of memory (in MB)\n" |
756 | 0 | " -N sets the default, per-proxy maximum # of connections (%d)\n" |
757 | 0 | " -L set local peer name (default to hostname)\n" |
758 | 0 | " -p writes pids of all children to this file\n" |
759 | 0 | " -dC[[key],line] display the configuration file, if there is a key, the file will be anonymised\n" |
760 | | #if defined(USE_EPOLL) |
761 | | " -de disables epoll() usage even when available\n" |
762 | | #endif |
763 | | #if defined(USE_KQUEUE) |
764 | | " -dk disables kqueue() usage even when available\n" |
765 | | #endif |
766 | | #if defined(USE_EVPORTS) |
767 | | " -dv disables event ports usage even when available\n" |
768 | | #endif |
769 | 0 | #if defined(USE_POLL) |
770 | 0 | " -dp disables poll() usage even when available\n" |
771 | 0 | #endif |
772 | | #if defined(USE_LINUX_SPLICE) |
773 | | " -dS disables splice usage (broken on old kernels)\n" |
774 | | #endif |
775 | | #if defined(USE_GETADDRINFO) |
776 | | " -dG disables getaddrinfo() usage\n" |
777 | | #endif |
778 | 0 | #if defined(SO_REUSEPORT) |
779 | 0 | " -dR disables SO_REUSEPORT usage\n" |
780 | 0 | #endif |
781 | | #if defined(HA_HAVE_DUMP_LIBS) |
782 | | " -dL dumps loaded object files after config checks\n" |
783 | | #endif |
784 | | #if defined(USE_CPU_AFFINITY) |
785 | | " -dc dumps the list of selected and evicted CPUs\n" |
786 | | #endif |
787 | 0 | " -dK{class[,...]} dump registered keywords (use 'help' for list)\n" |
788 | 0 | " -dr ignores server address resolution failures\n" |
789 | 0 | " -dV disables SSL verify on servers side\n" |
790 | 0 | " -dW fails if any warning is emitted\n" |
791 | 0 | " -dD diagnostic mode : warn about suspicious configuration statements\n" |
792 | 0 | " -dF disable fast-forward\n" |
793 | 0 | " -dI enable insecure fork\n" |
794 | 0 | " -dZ disable zero-copy forwarding\n" |
795 | | #if defined(HA_USE_KTLS) |
796 | | " -dT disable kTLS\n" |
797 | | #endif |
798 | 0 | " -sf/-st [pid ]* finishes/terminates old pids.\n" |
799 | 0 | " -x <unix_socket> get listening sockets from a unix socket\n" |
800 | 0 | " -S <bind>[,<bind options>...] new master CLI\n" |
801 | 0 | "\n", |
802 | 0 | name, cfg_maxpconn); |
803 | 0 | exit(1); |
804 | 0 | } |
805 | | |
806 | | |
807 | | |
808 | | /*********************************************************************/ |
809 | | /* more specific functions ***************************************/ |
810 | | /*********************************************************************/ |
811 | | |
812 | | /* sends the signal <sig> to all pids found in <oldpids>. Returns the number of |
813 | | * pids the signal was correctly delivered to. |
814 | | */ |
815 | | int tell_old_pids(int sig) |
816 | 0 | { |
817 | 0 | int p; |
818 | 0 | int ret = 0; |
819 | 0 | for (p = 0; p < nb_oldpids; p++) |
820 | 0 | if (kill(oldpids[p], sig) == 0) |
821 | 0 | ret++; |
822 | 0 | return ret; |
823 | 0 | } |
824 | | |
825 | | /* |
826 | | * remove a pid forom the olpid array and decrease nb_oldpids |
827 | | * return 1 pid was found otherwise return 0 |
828 | | */ |
829 | | |
830 | | int delete_oldpid(int pid) |
831 | 0 | { |
832 | 0 | int i; |
833 | |
|
834 | 0 | for (i = 0; i < nb_oldpids; i++) { |
835 | 0 | if (oldpids[i] == pid) { |
836 | 0 | oldpids[i] = oldpids[nb_oldpids - 1]; |
837 | 0 | oldpids[nb_oldpids - 1] = 0; |
838 | 0 | nb_oldpids--; |
839 | 0 | return 1; |
840 | 0 | } |
841 | 0 | } |
842 | 0 | return 0; |
843 | 0 | } |
844 | | |
845 | | /* |
846 | | * Exit with an error message upon a master recovery mode failure. |
847 | | */ |
848 | | static void exit_on_failure() |
849 | 0 | { |
850 | 0 | ha_alert("Master encountered an error in recovery mode, exiting.\n"); |
851 | 0 | } |
852 | | |
853 | | |
854 | | /* |
855 | | * upon SIGUSR1, let's have a soft stop. Note that soft_stop() broadcasts |
856 | | * a signal zero to all subscribers. This means that it's as easy as |
857 | | * subscribing to signal 0 to get informed about an imminent shutdown. |
858 | | */ |
859 | | static void sig_soft_stop(struct sig_handler *sh) |
860 | 0 | { |
861 | 0 | soft_stop(); |
862 | 0 | signal_unregister_handler(sh); |
863 | 0 | pool_gc(NULL); |
864 | 0 | } |
865 | | |
866 | | /* |
867 | | * upon SIGTTOU, we pause everything |
868 | | */ |
869 | | static void sig_pause(struct sig_handler *sh) |
870 | 0 | { |
871 | 0 | if (protocol_pause_all() & ERR_FATAL) { |
872 | 0 | const char *msg = "Some proxies refused to pause, performing soft stop now.\n"; |
873 | 0 | ha_warning("%s", msg); |
874 | 0 | send_log(NULL, LOG_WARNING, "%s", msg); |
875 | 0 | soft_stop(); |
876 | 0 | } |
877 | 0 | pool_gc(NULL); |
878 | 0 | } |
879 | | |
880 | | /* |
881 | | * upon SIGTTIN, let's have a soft stop. |
882 | | */ |
883 | | static void sig_listen(struct sig_handler *sh) |
884 | 0 | { |
885 | 0 | if (protocol_resume_all() & ERR_FATAL) { |
886 | 0 | const char *msg = "Some proxies refused to resume, probably due to a conflict on a listening port. You may want to try again after the conflicting application is stopped, otherwise a restart might be needed to resume safe operations.\n"; |
887 | 0 | ha_warning("%s", msg); |
888 | 0 | send_log(NULL, LOG_WARNING, "%s", msg); |
889 | 0 | } |
890 | 0 | } |
891 | | |
892 | | /* |
893 | | * this function dumps every server's state when the process receives SIGHUP. |
894 | | */ |
895 | | static void sig_dump_state(struct sig_handler *sh) |
896 | 0 | { |
897 | 0 | struct proxy *p = proxies_list; |
898 | |
|
899 | 0 | ha_warning("SIGHUP received, dumping servers states.\n"); |
900 | 0 | while (p) { |
901 | 0 | struct server *s = p->srv; |
902 | |
|
903 | 0 | send_log(p, LOG_NOTICE, "SIGHUP received, dumping servers states for proxy %s.\n", p->id); |
904 | 0 | while (s) { |
905 | 0 | chunk_printf(&trash, |
906 | 0 | "SIGHUP: Server %s/%s is %s. Conn: %d act, %d pend, %llu tot.", |
907 | 0 | p->id, s->id, |
908 | 0 | (s->cur_state != SRV_ST_STOPPED) ? "UP" : "DOWN", |
909 | 0 | s->cur_sess, s->queueslength, (ullong)COUNTERS_SHARED_TOTAL(s->counters.shared.tg, cum_sess, HA_ATOMIC_LOAD)); |
910 | 0 | ha_warning("%s\n", trash.area); |
911 | 0 | send_log(p, LOG_NOTICE, "%s\n", trash.area); |
912 | 0 | s = s->next; |
913 | 0 | } |
914 | | |
915 | | /* FIXME: those info are a bit outdated. We should be able to distinguish between FE and BE. */ |
916 | 0 | if (!p->srv) { |
917 | 0 | chunk_printf(&trash, |
918 | 0 | "SIGHUP: Proxy %s has no servers. Conn: act(FE+BE): %d+%d, %d pend (%d unass), tot(FE+BE): %llu+%llu.", |
919 | 0 | p->id, |
920 | 0 | p->feconn, p->beconn, p->totpend, p->queueslength, (ullong)COUNTERS_SHARED_TOTAL(p->fe_counters.shared.tg, cum_conn, HA_ATOMIC_LOAD), (ullong)COUNTERS_SHARED_TOTAL(p->be_counters.shared.tg, cum_sess, HA_ATOMIC_LOAD)); |
921 | 0 | } else if (p->srv_act == 0) { |
922 | 0 | chunk_printf(&trash, |
923 | 0 | "SIGHUP: Proxy %s %s ! Conn: act(FE+BE): %d+%d, %d pend (%d unass), tot(FE+BE): %llu+%llu.", |
924 | 0 | p->id, |
925 | 0 | (p->srv_bck) ? "is running on backup servers" : "has no server available", |
926 | 0 | p->feconn, p->beconn, p->totpend, p->queueslength, (ullong)COUNTERS_SHARED_TOTAL(p->fe_counters.shared.tg, cum_conn, HA_ATOMIC_LOAD), (ullong)COUNTERS_SHARED_TOTAL(p->be_counters.shared.tg, cum_sess, HA_ATOMIC_LOAD)); |
927 | 0 | } else { |
928 | 0 | chunk_printf(&trash, |
929 | 0 | "SIGHUP: Proxy %s has %d active servers and %d backup servers available." |
930 | 0 | " Conn: act(FE+BE): %d+%d, %d pend (%d unass), tot(FE+BE): %llu+%llu.", |
931 | 0 | p->id, p->srv_act, p->srv_bck, |
932 | 0 | p->feconn, p->beconn, p->totpend, p->queueslength, (ullong)COUNTERS_SHARED_TOTAL(p->fe_counters.shared.tg, cum_conn, HA_ATOMIC_LOAD), (ullong)COUNTERS_SHARED_TOTAL(p->be_counters.shared.tg, cum_sess, HA_ATOMIC_LOAD)); |
933 | 0 | } |
934 | 0 | ha_warning("%s\n", trash.area); |
935 | 0 | send_log(p, LOG_NOTICE, "%s\n", trash.area); |
936 | |
|
937 | 0 | p = p->next; |
938 | 0 | } |
939 | 0 | } |
940 | | |
941 | | static void dump(struct sig_handler *sh) |
942 | 0 | { |
943 | | /* free everything possible */ |
944 | 0 | pool_gc(NULL); |
945 | 0 | } |
946 | | |
947 | | /* |
948 | | * This function dup2 the stdio FDs (0,1,2) with <fd>, then closes <fd> |
949 | | * If <fd> < 0, it opens /dev/null and use it to dup |
950 | | * |
951 | | * In the case of chrooting, you have to open /dev/null before the chroot, and |
952 | | * pass the <fd> to this function |
953 | | */ |
954 | | void stdio_quiet(int fd) |
955 | 0 | { |
956 | 0 | int close_fd = 0; |
957 | 0 | if (fd < 0) { |
958 | 0 | fd = open("/dev/null", O_RDWR, 0); |
959 | 0 | close_fd = 1; |
960 | 0 | } |
961 | |
|
962 | 0 | if (fd > -1) { |
963 | 0 | fclose(stdin); |
964 | 0 | fclose(stdout); |
965 | 0 | fclose(stderr); |
966 | |
|
967 | 0 | dup2(fd, 0); |
968 | 0 | dup2(fd, 1); |
969 | 0 | dup2(fd, 2); |
970 | 0 | if (fd > 2 && close_fd) |
971 | 0 | close(fd); |
972 | 0 | return; |
973 | 0 | } |
974 | | |
975 | 0 | ha_alert("Cannot open /dev/null\n"); |
976 | 0 | exit(EXIT_FAILURE); |
977 | 0 | } |
978 | | |
979 | | |
980 | | /* This function checks if cfg_cfgfiles contains directories. |
981 | | * If it finds one, it adds all the files (and only files) it contains |
982 | | * in cfg_cfgfiles in place of the directory (and removes the directory). |
983 | | * It adds the files in lexical order. |
984 | | * It adds only files with .cfg extension. |
985 | | * It doesn't add files with name starting with '.' |
986 | | */ |
987 | | static void cfgfiles_expand_directories(void) |
988 | 0 | { |
989 | 0 | struct cfgfile *cfg, *cfg_tmp; |
990 | 0 | char *err = NULL; |
991 | |
|
992 | 0 | list_for_each_entry_safe(cfg, cfg_tmp, &cfg_cfgfiles, list) { |
993 | 0 | struct stat file_stat; |
994 | 0 | struct dirent **dir_entries = NULL; |
995 | 0 | int dir_entries_nb; |
996 | 0 | int dir_entries_it; |
997 | |
|
998 | 0 | if (stat(cfg->filename, &file_stat)) { |
999 | 0 | ha_alert("Cannot open configuration file/directory %s : %s\n", |
1000 | 0 | cfg->filename, |
1001 | 0 | strerror(errno)); |
1002 | 0 | exit(1); |
1003 | 0 | } |
1004 | | |
1005 | 0 | if (!S_ISDIR(file_stat.st_mode)) |
1006 | 0 | continue; |
1007 | | |
1008 | | /* from this point cfg->name is a directory */ |
1009 | | |
1010 | 0 | dir_entries_nb = scandir(cfg->filename, &dir_entries, NULL, alphasort); |
1011 | 0 | if (dir_entries_nb < 0) { |
1012 | 0 | ha_alert("Cannot open configuration directory %s : %s\n", |
1013 | 0 | cfg->filename, |
1014 | 0 | strerror(errno)); |
1015 | 0 | exit(1); |
1016 | 0 | } |
1017 | | |
1018 | | /* for each element in the directory cfg->name */ |
1019 | 0 | for (dir_entries_it = 0; dir_entries_it < dir_entries_nb; dir_entries_it++) { |
1020 | 0 | struct dirent *dir_entry = dir_entries[dir_entries_it]; |
1021 | 0 | char *filename = NULL; |
1022 | 0 | char *d_name_cfgext = strstr(dir_entry->d_name, ".cfg"); |
1023 | | |
1024 | | /* don't add filename that begin with . |
1025 | | * only add filename with .cfg extension |
1026 | | */ |
1027 | 0 | if (dir_entry->d_name[0] == '.' || |
1028 | 0 | !(d_name_cfgext && d_name_cfgext[4] == '\0')) |
1029 | 0 | goto next_dir_entry; |
1030 | | |
1031 | 0 | if (!memprintf(&filename, "%s/%s", cfg->filename, dir_entry->d_name)) { |
1032 | 0 | ha_alert("Cannot load configuration files %s : out of memory.\n", |
1033 | 0 | filename); |
1034 | 0 | exit(1); |
1035 | 0 | } |
1036 | | |
1037 | 0 | if (stat(filename, &file_stat)) { |
1038 | 0 | ha_alert("Cannot open configuration file %s : %s\n", |
1039 | 0 | cfg->filename, |
1040 | 0 | strerror(errno)); |
1041 | 0 | exit(1); |
1042 | 0 | } |
1043 | | |
1044 | | /* don't add anything else than regular file in cfg_cfgfiles |
1045 | | * this way we avoid loops |
1046 | | */ |
1047 | 0 | if (!S_ISREG(file_stat.st_mode)) |
1048 | 0 | goto next_dir_entry; |
1049 | | |
1050 | 0 | if (!list_append_cfgfile(&cfg->list, filename, &err)) { |
1051 | 0 | ha_alert("Cannot load configuration files %s : %s\n", |
1052 | 0 | filename, |
1053 | 0 | err); |
1054 | 0 | exit(1); |
1055 | 0 | } |
1056 | | |
1057 | 0 | next_dir_entry: |
1058 | 0 | free(filename); |
1059 | 0 | free(dir_entry); |
1060 | 0 | } |
1061 | | |
1062 | 0 | free(dir_entries); |
1063 | | |
1064 | | /* remove the current directory (cfg) from cfgfiles */ |
1065 | 0 | free(cfg->filename); |
1066 | 0 | LIST_DELETE(&cfg->list); |
1067 | 0 | free(cfg); |
1068 | 0 | } |
1069 | | |
1070 | 0 | free(err); |
1071 | 0 | } |
1072 | | |
1073 | | /* Loads config files. Returns -1 and frees allocated memory in env_cfgfiles, if |
1074 | | * we are run out of memory or load_cfg_in_mem() has failed. load_cfg_in_mem() |
1075 | | * frees in its stack the memory allocated for config files content, if it has |
1076 | | * encountered an error. |
1077 | | */ |
1078 | | static int load_cfg() |
1079 | 0 | { |
1080 | 0 | struct cfgfile *cfg, *cfg_tmp; |
1081 | | |
1082 | | /* handle cfgfiles that are actually directories */ |
1083 | 0 | cfgfiles_expand_directories(); |
1084 | |
|
1085 | 0 | if (LIST_ISEMPTY(&cfg_cfgfiles)) |
1086 | 0 | usage(progname); |
1087 | |
|
1088 | 0 | list_for_each_entry_safe(cfg, cfg_tmp, &cfg_cfgfiles, list) { |
1089 | |
|
1090 | 0 | cfg->size = load_cfg_in_mem(cfg->filename, &cfg->content); |
1091 | 0 | if (cfg->size < 0) |
1092 | 0 | return -1; |
1093 | |
|
1094 | 0 | } |
1095 | | |
1096 | 0 | return 0; |
1097 | |
|
1098 | 0 | } |
1099 | | |
1100 | | /* Calls parser for each config file from cfg_cfgfiles list. Returns -1, if we |
1101 | | * are run out of memory, can't apply default path or when the parser function |
1102 | | * returns some fatal errors. |
1103 | | * Otherwise, it returns an err_code, which may contain 0 (OK) or ERR_WARN, |
1104 | | * ERR_ALERT. |
1105 | | */ |
1106 | | static int read_cfg() |
1107 | 0 | { |
1108 | 0 | char *env_cfgfiles = NULL; |
1109 | 0 | struct cfgfile *cfg; |
1110 | 0 | int err_code = 0; |
1111 | | |
1112 | | /* temporary create environment variables with default |
1113 | | * values to ease user configuration. Do not forget to |
1114 | | * unset them after the list_for_each_entry loop. |
1115 | | */ |
1116 | 0 | setenv("HAPROXY_HTTP_LOG_FMT", default_http_log_format, 1); |
1117 | 0 | setenv("HAPROXY_HTTP_CLF_LOG_FMT", clf_http_log_format, 1); |
1118 | 0 | setenv("HAPROXY_HTTPS_LOG_FMT", default_https_log_format, 1); |
1119 | 0 | setenv("HAPROXY_TCP_LOG_FMT", default_tcp_log_format, 1); |
1120 | 0 | setenv("HAPROXY_TCP_CLF_LOG_FMT", clf_tcp_log_format, 1); |
1121 | 0 | setenv("HAPROXY_BRANCH", PRODUCT_BRANCH, 1); |
1122 | 0 | list_for_each_entry(cfg, &cfg_cfgfiles, list) { |
1123 | 0 | int ret; |
1124 | | |
1125 | | /* save all successfully loaded conf files in HAPROXY_CFGFILES |
1126 | | * env var |
1127 | | */ |
1128 | 0 | if (!memprintf(&env_cfgfiles, "%s%s%s", |
1129 | 0 | (env_cfgfiles ? env_cfgfiles : ""), |
1130 | 0 | (env_cfgfiles ? ";" : ""), cfg->filename)) { |
1131 | | /* free what we've already allocated and free cfglist */ |
1132 | 0 | ha_alert("Could not allocate memory for HAPROXY_CFGFILES env variable\n"); |
1133 | 0 | goto err; |
1134 | 0 | } |
1135 | | |
1136 | 0 | ret = parse_cfg(cfg); |
1137 | 0 | if (ret == -1) |
1138 | 0 | goto err; |
1139 | | |
1140 | 0 | if (ret & (ERR_ABORT|ERR_FATAL)) |
1141 | 0 | ha_alert("Error(s) found in configuration file : %s\n", cfg->filename); |
1142 | 0 | err_code |= ret; |
1143 | 0 | if (err_code & ERR_ABORT) |
1144 | 0 | goto err; |
1145 | | |
1146 | |
|
1147 | 0 | } |
1148 | | /* remove temporary environment variables. */ |
1149 | 0 | unsetenv("HAPROXY_HTTP_LOG_FMT"); |
1150 | 0 | unsetenv("HAPROXY_HTTP_CLF_LOG_FMT"); |
1151 | 0 | unsetenv("HAPROXY_HTTPS_LOG_FMT"); |
1152 | 0 | unsetenv("HAPROXY_TCP_LOG_FMT"); |
1153 | 0 | unsetenv("HAPROXY_TCP_CLF_LOG_FMT"); |
1154 | | |
1155 | | /* do not try to resolve arguments nor to spot inconsistencies when |
1156 | | * the configuration contains fatal errors. |
1157 | | */ |
1158 | 0 | if (err_code & (ERR_ABORT|ERR_FATAL)) { |
1159 | 0 | ha_alert("Fatal errors found in configuration.\n"); |
1160 | 0 | goto err; |
1161 | 0 | } |
1162 | | |
1163 | 0 | setenv("HAPROXY_CFGFILES", env_cfgfiles, 1); |
1164 | 0 | free(env_cfgfiles); |
1165 | |
|
1166 | 0 | return err_code; |
1167 | | |
1168 | 0 | err: |
1169 | 0 | free(env_cfgfiles); |
1170 | 0 | return -1; |
1171 | 0 | } |
1172 | | |
1173 | | /* |
1174 | | * copy and cleanup the current argv |
1175 | | * Remove the -sf /-st / -x parameters |
1176 | | * Return an allocated copy of argv |
1177 | | */ |
1178 | | |
1179 | | char **copy_argv(int argc, char **argv) |
1180 | 0 | { |
1181 | 0 | char **newargv, **retargv; |
1182 | |
|
1183 | 0 | newargv = calloc(argc + 2, sizeof(*newargv)); |
1184 | 0 | if (newargv == NULL) { |
1185 | 0 | ha_warning("Cannot allocate memory\n"); |
1186 | 0 | return NULL; |
1187 | 0 | } |
1188 | 0 | retargv = newargv; |
1189 | | |
1190 | | /* first copy argv[0] */ |
1191 | 0 | *newargv++ = *argv++; |
1192 | 0 | argc--; |
1193 | |
|
1194 | 0 | while (argc > 0) { |
1195 | 0 | if (**argv != '-') { |
1196 | | /* non options are copied but will fail in the argument parser */ |
1197 | 0 | *newargv++ = *argv++; |
1198 | 0 | argc--; |
1199 | |
|
1200 | 0 | } else { |
1201 | 0 | char *flag; |
1202 | |
|
1203 | 0 | flag = *argv + 1; |
1204 | |
|
1205 | 0 | if (flag[0] == '-' && flag[1] == 0) { |
1206 | | /* "--\0" copy every arguments till the end of argv */ |
1207 | 0 | *newargv++ = *argv++; |
1208 | 0 | argc--; |
1209 | |
|
1210 | 0 | while (argc > 0) { |
1211 | 0 | *newargv++ = *argv++; |
1212 | 0 | argc--; |
1213 | 0 | } |
1214 | 0 | } else { |
1215 | 0 | switch (*flag) { |
1216 | 0 | case 's': |
1217 | | /* -sf / -st and their parameters are ignored */ |
1218 | 0 | if (flag[1] == 'f' || flag[1] == 't') { |
1219 | 0 | argc--; |
1220 | 0 | argv++; |
1221 | | /* The list can't contain a negative value since the only |
1222 | | way to know the end of this list is by looking for the |
1223 | | next option or the end of the options */ |
1224 | 0 | while (argc > 0 && argv[0][0] != '-') { |
1225 | 0 | argc--; |
1226 | 0 | argv++; |
1227 | 0 | } |
1228 | 0 | } else { |
1229 | 0 | argc--; |
1230 | 0 | argv++; |
1231 | |
|
1232 | 0 | } |
1233 | 0 | break; |
1234 | | |
1235 | 0 | case 'x': |
1236 | | /* this option and its parameter are ignored */ |
1237 | 0 | argc--; |
1238 | 0 | argv++; |
1239 | 0 | if (argc > 0) { |
1240 | 0 | argc--; |
1241 | 0 | argv++; |
1242 | 0 | } |
1243 | 0 | break; |
1244 | | |
1245 | 0 | case 'C': |
1246 | 0 | case 'n': |
1247 | 0 | case 'm': |
1248 | 0 | case 'N': |
1249 | 0 | case 'L': |
1250 | 0 | case 'f': |
1251 | 0 | case 'p': |
1252 | 0 | case 'S': |
1253 | | /* these options have only 1 parameter which must be copied and can start with a '-' */ |
1254 | 0 | *newargv++ = *argv++; |
1255 | 0 | argc--; |
1256 | 0 | if (argc == 0) |
1257 | 0 | goto error; |
1258 | 0 | *newargv++ = *argv++; |
1259 | 0 | argc--; |
1260 | 0 | break; |
1261 | 0 | default: |
1262 | | /* for other options just copy them without parameters, this is also done |
1263 | | * for options like "--foo", but this will fail in the argument parser. |
1264 | | * */ |
1265 | 0 | *newargv++ = *argv++; |
1266 | 0 | argc--; |
1267 | 0 | break; |
1268 | 0 | } |
1269 | 0 | } |
1270 | 0 | } |
1271 | 0 | } |
1272 | | |
1273 | 0 | return retargv; |
1274 | | |
1275 | 0 | error: |
1276 | 0 | free(retargv); |
1277 | 0 | return NULL; |
1278 | 0 | } |
1279 | | |
1280 | | |
1281 | | /* Performs basic random seed initialization. The main issue with this is that |
1282 | | * srandom_r() only takes 32 bits and purposely provides a reproducible sequence, |
1283 | | * which means that there will only be 4 billion possible random sequences once |
1284 | | * srandom() is called, regardless of the internal state. Not calling it is |
1285 | | * even worse as we'll always produce the same randoms sequences. What we do |
1286 | | * here is to create an initial sequence from various entropy sources, hash it |
1287 | | * using SHA1 and keep the resulting 160 bits available globally. |
1288 | | * |
1289 | | * We initialize the current process with the first 32 bits before starting the |
1290 | | * polling loop, where all this will be changed to have process specific and |
1291 | | * thread specific sequences. |
1292 | | * |
1293 | | * Before starting threads, it's still possible to call random() as srandom() |
1294 | | * is initialized from this, but after threads and/or processes are started, |
1295 | | * only ha_random() is expected to be used to guarantee distinct sequences. |
1296 | | */ |
1297 | | static void ha_random_boot(char *const *argv) |
1298 | 0 | { |
1299 | 0 | unsigned char message[256]; |
1300 | 0 | unsigned char *m = message; |
1301 | 0 | struct timeval tv; |
1302 | 0 | blk_SHA_CTX ctx; |
1303 | 0 | unsigned long l; |
1304 | 0 | int fd; |
1305 | 0 | int i; |
1306 | | |
1307 | | /* start with current time as pseudo-random seed */ |
1308 | 0 | gettimeofday(&tv, NULL); |
1309 | 0 | write_u32(m, tv.tv_sec); m += 4; |
1310 | 0 | write_u32(m, tv.tv_usec); m += 4; |
1311 | | |
1312 | | /* PID and PPID add some OS-based randomness */ |
1313 | 0 | write_u16(m, getpid()); m += 2; |
1314 | 0 | write_u16(m, getppid()); m += 2; |
1315 | | |
1316 | | /* take up to 160 bits bytes from /dev/urandom if available (non-blocking) */ |
1317 | 0 | fd = open("/dev/urandom", O_RDONLY); |
1318 | 0 | if (fd >= 0) { |
1319 | 0 | i = read(fd, m, 20); |
1320 | 0 | if (i > 0) |
1321 | 0 | m += i; |
1322 | 0 | close(fd); |
1323 | 0 | } |
1324 | | |
1325 | | /* take up to 160 bits bytes from openssl (non-blocking) */ |
1326 | | #ifdef USE_OPENSSL |
1327 | | if (RAND_bytes(m, 20) == 1) |
1328 | | m += 20; |
1329 | | #endif |
1330 | | |
1331 | | /* take 160 bits from existing random in case it was already initialized */ |
1332 | 0 | for (i = 0; i < 5; i++) { |
1333 | 0 | write_u32(m, random()); |
1334 | 0 | m += 4; |
1335 | 0 | } |
1336 | | |
1337 | | /* stack address (benefit from operating system's ASLR) */ |
1338 | 0 | l = (unsigned long)&m; |
1339 | 0 | memcpy(m, &l, sizeof(l)); m += sizeof(l); |
1340 | | |
1341 | | /* argv address (benefit from operating system's ASLR) */ |
1342 | 0 | l = (unsigned long)&argv; |
1343 | 0 | memcpy(m, &l, sizeof(l)); m += sizeof(l); |
1344 | | |
1345 | | /* use tv_usec again after all the operations above */ |
1346 | 0 | gettimeofday(&tv, NULL); |
1347 | 0 | write_u32(m, tv.tv_usec); m += 4; |
1348 | | |
1349 | | /* |
1350 | | * At this point, ~84-92 bytes have been used |
1351 | | */ |
1352 | | |
1353 | | /* finish with the hostname */ |
1354 | 0 | strncpy((char *)m, hostname, message + sizeof(message) - m); |
1355 | 0 | m += strlen(hostname); |
1356 | | |
1357 | | /* total message length */ |
1358 | 0 | l = m - message; |
1359 | |
|
1360 | 0 | memset(&ctx, 0, sizeof(ctx)); |
1361 | 0 | blk_SHA1_Init(&ctx); |
1362 | 0 | blk_SHA1_Update(&ctx, message, l); |
1363 | 0 | blk_SHA1_Final(boot_seed, &ctx); |
1364 | |
|
1365 | 0 | srandom(read_u32(boot_seed)); |
1366 | 0 | ha_random_seed(boot_seed, sizeof(boot_seed)); |
1367 | 0 | } |
1368 | | |
1369 | | |
1370 | | /* Evaluates a condition provided within a conditional block of the |
1371 | | * configuration. Makes process to exit with 0, if the condition is true, with |
1372 | | * 1, if the condition is false or with 2, if parse_line encounters an error. |
1373 | | */ |
1374 | | static void do_check_condition() |
1375 | 0 | { |
1376 | 0 | int result; |
1377 | 0 | uint32_t err; |
1378 | 0 | const char *errptr; |
1379 | 0 | char *errmsg = NULL; |
1380 | |
|
1381 | 0 | char *args[MAX_LINE_ARGS+1]; |
1382 | 0 | int arg = sizeof(args) / sizeof(*args); |
1383 | 0 | size_t outlen; |
1384 | 0 | char *w; |
1385 | |
|
1386 | 0 | if (!check_condition) |
1387 | 0 | usage(progname); |
1388 | |
|
1389 | 0 | outlen = strlen(check_condition) + 1; |
1390 | 0 | err = parse_line(check_condition, check_condition, &outlen, args, &arg, |
1391 | 0 | PARSE_OPT_ENV | PARSE_OPT_WORD_EXPAND | PARSE_OPT_DQUOTE | PARSE_OPT_SQUOTE | PARSE_OPT_BKSLASH, |
1392 | 0 | &errptr); |
1393 | |
|
1394 | 0 | if (err & PARSE_ERR_QUOTE) { |
1395 | 0 | ha_alert("Syntax Error in condition: Unmatched quote.\n"); |
1396 | 0 | exit(2); |
1397 | 0 | } |
1398 | | |
1399 | 0 | if (err & PARSE_ERR_HEX) { |
1400 | 0 | ha_alert("Syntax Error in condition: Truncated or invalid hexadecimal sequence.\n"); |
1401 | 0 | exit(2); |
1402 | 0 | } |
1403 | | |
1404 | 0 | if (err & (PARSE_ERR_TOOLARGE|PARSE_ERR_OVERLAP)) { |
1405 | 0 | ha_alert("Error in condition: Line too long.\n"); |
1406 | 0 | exit(2); |
1407 | 0 | } |
1408 | | |
1409 | 0 | if (err & PARSE_ERR_TOOMANY) { |
1410 | 0 | ha_alert("Error in condition: Too many words.\n"); |
1411 | 0 | exit(2); |
1412 | 0 | } |
1413 | | |
1414 | 0 | if (err) { |
1415 | 0 | ha_alert("Unhandled error in condition, please report this to the developers.\n"); |
1416 | 0 | exit(2); |
1417 | 0 | } |
1418 | | |
1419 | | /* remerge all words into a single expression */ |
1420 | 0 | for (w = *args; (w += strlen(w)) < check_condition + outlen - 1; *w = ' ') |
1421 | 0 | ; |
1422 | |
|
1423 | 0 | result = cfg_eval_condition(args, &errmsg, &errptr); |
1424 | |
|
1425 | 0 | if (result < 0) { |
1426 | 0 | if (errmsg) |
1427 | 0 | ha_alert("Failed to evaluate condition: %s\n", errmsg); |
1428 | |
|
1429 | 0 | exit(2); |
1430 | 0 | } |
1431 | | |
1432 | 0 | exit(result ? 0 : 1); |
1433 | 0 | } |
1434 | | |
1435 | | /* This performs th every basic early initialization at the end of the PREPARE |
1436 | | * init stage. It may only assume that list heads are initialized, but not that |
1437 | | * anything else is correct. It will initialize a number of variables that |
1438 | | * depend on command line and will pre-parse the command line. If it fails, it |
1439 | | * directly exits. |
1440 | | */ |
1441 | | static void init_early(int argc, char **argv) |
1442 | 0 | { |
1443 | 0 | char *tmp; |
1444 | 0 | int len; |
1445 | |
|
1446 | 0 | setenv("HAPROXY_STARTUP_VERSION", haproxy_version, 0); |
1447 | | |
1448 | | /* First, let's initialize most global variables */ |
1449 | 0 | totalconn = actconn = listeners = stopping = 0; |
1450 | 0 | killed = pid = 0; |
1451 | | |
1452 | | /* cast to one byte in order to fill better a 3 bytes hole in the global struct, |
1453 | | * we hopefully will never start with > than 255 args |
1454 | | */ |
1455 | 0 | global.argc = (unsigned char)argc; |
1456 | 0 | global.argv = argv; |
1457 | 0 | global.maxsock = 10; /* reserve 10 fds ; will be incremented by socket eaters */ |
1458 | 0 | global.rlimit_memmax_all = HAPROXY_MEMMAX; |
1459 | 0 | global.mode = MODE_STARTING; |
1460 | | |
1461 | | /* if we were in mworker mode, we should restart in mworker mode */ |
1462 | 0 | if (getenv("HAPROXY_MWORKER_REEXEC") != NULL) |
1463 | 0 | global.mode |= MODE_MWORKER; |
1464 | | |
1465 | | /* initialize date, time, and pid */ |
1466 | 0 | tzset(); |
1467 | 0 | clock_init_process_date(); |
1468 | 0 | start_date = date; |
1469 | 0 | start_time_ns = now_ns; |
1470 | 0 | pid = getpid(); |
1471 | | |
1472 | | /* Set local host name and adjust some environment variables. |
1473 | | * NB: POSIX does not make it mandatory for gethostname() to |
1474 | | * NULL-terminate the string in case of truncation, and at least |
1475 | | * FreeBSD appears not to do it. |
1476 | | */ |
1477 | 0 | memset(hostname, 0, sizeof(hostname)); |
1478 | 0 | gethostname(hostname, sizeof(hostname) - 1); |
1479 | |
|
1480 | 0 | localpeer = strdup(hostname); |
1481 | 0 | if (!localpeer) { |
1482 | 0 | ha_alert("Cannot allocate memory for local peer.\n"); |
1483 | 0 | exit(EXIT_FAILURE); |
1484 | 0 | } |
1485 | | |
1486 | | /* extract the program name from argv[0], it will be used for the logs |
1487 | | * and error messages. |
1488 | | */ |
1489 | 0 | progname = *argv; |
1490 | 0 | while ((tmp = strchr(progname, '/')) != NULL) |
1491 | 0 | progname = tmp + 1; |
1492 | |
|
1493 | 0 | len = strlen(progname); |
1494 | 0 | progname = strdup(progname); |
1495 | 0 | if (!progname) { |
1496 | 0 | ha_alert("Cannot allocate memory for progname.\n"); |
1497 | 0 | exit(EXIT_FAILURE); |
1498 | 0 | } |
1499 | | |
1500 | 0 | chunk_initlen(&global.log_tag, strdup(progname), len, len); |
1501 | 0 | if (b_orig(&global.log_tag) == NULL) { |
1502 | 0 | ha_alert("Cannot allocate memory for log_tag.\n"); |
1503 | 0 | exit(EXIT_FAILURE); |
1504 | 0 | } |
1505 | 0 | } |
1506 | | |
1507 | | void haproxy_init_args(int argc, char **argv) |
1508 | 0 | { |
1509 | 0 | char *err_msg = NULL; |
1510 | | |
1511 | | /* skip program name and start */ |
1512 | 0 | argc--; argv++; |
1513 | 0 | while (argc > 0) { |
1514 | 0 | char *flag; |
1515 | |
|
1516 | 0 | if (**argv == '-') { |
1517 | 0 | flag = *argv+1; |
1518 | | |
1519 | | /* 1 arg */ |
1520 | 0 | if (*flag == 'v') { |
1521 | 0 | if (flag[1] == 'q' && flag[2] == 's' && flag[3] == '\0') { |
1522 | 0 | display_version_plain(0); // -vqs |
1523 | 0 | deinit_and_exit(0); |
1524 | 0 | } |
1525 | 0 | else if (flag[1] == 'q' && flag[2] == 'b' && flag[3] == '\0') { |
1526 | 0 | display_version_plain(2); // -vqb |
1527 | 0 | deinit_and_exit(0); |
1528 | 0 | } |
1529 | 0 | else if (flag[1] == 'q' && flag[2] == '\0') { |
1530 | 0 | display_version_plain(1); // -vq |
1531 | 0 | deinit_and_exit(0); |
1532 | 0 | } |
1533 | 0 | else { |
1534 | 0 | display_version(); |
1535 | 0 | if (flag[1] == 'v') // -vv |
1536 | 0 | display_build_opts(); |
1537 | 0 | deinit_and_exit(0); |
1538 | 0 | } |
1539 | 0 | } |
1540 | | #if defined(USE_EPOLL) |
1541 | | else if (*flag == 'd' && flag[1] == 'e') |
1542 | | global.tune.options &= ~GTUNE_USE_EPOLL; |
1543 | | #endif |
1544 | 0 | #if defined(USE_POLL) |
1545 | 0 | else if (*flag == 'd' && flag[1] == 'p') |
1546 | 0 | global.tune.options &= ~GTUNE_USE_POLL; |
1547 | 0 | #endif |
1548 | | #if defined(USE_KQUEUE) |
1549 | | else if (*flag == 'd' && flag[1] == 'k') |
1550 | | global.tune.options &= ~GTUNE_USE_KQUEUE; |
1551 | | #endif |
1552 | | #if defined(USE_EVPORTS) |
1553 | | else if (*flag == 'd' && flag[1] == 'v') |
1554 | | global.tune.options &= ~GTUNE_USE_EVPORTS; |
1555 | | #endif |
1556 | | #if defined(USE_LINUX_SPLICE) |
1557 | | else if (*flag == 'd' && flag[1] == 'S') |
1558 | | global.tune.options &= ~GTUNE_USE_SPLICE; |
1559 | | #endif |
1560 | | #if defined(USE_GETADDRINFO) |
1561 | | else if (*flag == 'd' && flag[1] == 'G') |
1562 | | global.tune.options &= ~GTUNE_USE_GAI; |
1563 | | #endif |
1564 | 0 | #if defined(SO_REUSEPORT) |
1565 | 0 | else if (*flag == 'd' && flag[1] == 'R') |
1566 | 0 | protocol_clrf_all(PROTO_F_REUSEPORT_SUPPORTED); |
1567 | 0 | #endif |
1568 | | #if defined(USE_CPU_AFFINITY) |
1569 | | else if (*flag == 'd' && flag[1] == 'c') |
1570 | | global.tune.debug |= GDBG_CPU_AFFINITY; |
1571 | | #endif |
1572 | 0 | else if (*flag == 'd' && flag[1] == 'F') |
1573 | 0 | global.tune.options &= ~GTUNE_USE_FAST_FWD; |
1574 | 0 | else if (*flag == 'd' && flag[1] == 'I') |
1575 | 0 | global.tune.options |= GTUNE_INSECURE_FORK; |
1576 | 0 | else if (*flag == 'd' && flag[1] == 'V') |
1577 | 0 | global.ssl_server_verify = SSL_SERVER_VERIFY_NONE; |
1578 | 0 | else if (*flag == 'd' && flag[1] == 'Z') |
1579 | 0 | global.tune.no_zero_copy_fwd |= NO_ZERO_COPY_FWD; |
1580 | 0 | else if (*flag == 'V') |
1581 | 0 | arg_mode |= MODE_VERBOSE; |
1582 | 0 | else if (*flag == 'd' && flag[1] == 'C') { |
1583 | 0 | char *end; |
1584 | 0 | char *key; |
1585 | |
|
1586 | 0 | key = flag + 2; |
1587 | 0 | for (;key && *key; key = end) { |
1588 | 0 | end = strchr(key, ','); |
1589 | 0 | if (end) |
1590 | 0 | *(end++) = 0; |
1591 | |
|
1592 | 0 | if (strcmp(key, "line") == 0) |
1593 | 0 | arg_mode |= MODE_DUMP_NB_L; |
1594 | |
|
1595 | 0 | } |
1596 | 0 | arg_mode |= MODE_DUMP_CFG; |
1597 | 0 | HA_ATOMIC_STORE(&global.anon_key, atoll(flag + 2)); |
1598 | 0 | } |
1599 | 0 | else if (*flag == 'd' && flag[1] == 'b') |
1600 | 0 | arg_mode |= MODE_FOREGROUND; |
1601 | 0 | else if (*flag == 'd' && flag[1] == 'D') |
1602 | 0 | arg_mode |= MODE_DIAG; |
1603 | 0 | else if (*flag == 'd' && flag[1] == 'W') |
1604 | 0 | arg_mode |= MODE_ZERO_WARNING; |
1605 | 0 | else if (*flag == 'd' && flag[1] == 'M') { |
1606 | 0 | int ret = pool_parse_debugging(flag + 2, &err_msg); |
1607 | |
|
1608 | 0 | if (ret <= -1) { |
1609 | 0 | if (ret < -1) |
1610 | 0 | ha_alert("-dM: %s\n", err_msg); |
1611 | 0 | else |
1612 | 0 | printf("%s\n", err_msg); |
1613 | 0 | ha_free(&err_msg); |
1614 | 0 | exit(ret < -1 ? EXIT_FAILURE : 0); |
1615 | 0 | } else if (ret == 0) { |
1616 | 0 | ha_warning("-dM: %s\n", err_msg); |
1617 | 0 | ha_free(&err_msg); |
1618 | 0 | } |
1619 | 0 | } |
1620 | 0 | else if (*flag == 'd' && flag[1] == 'r') |
1621 | 0 | global.tune.options |= GTUNE_RESOLVE_DONTFAIL; |
1622 | | #if defined(HA_HAVE_DUMP_LIBS) |
1623 | | else if (*flag == 'd' && flag[1] == 'L') |
1624 | | arg_mode |= MODE_DUMP_LIBS; |
1625 | | #endif |
1626 | 0 | else if (*flag == 'd' && flag[1] == 'K') { |
1627 | 0 | arg_mode |= MODE_DUMP_KWD; |
1628 | 0 | kwd_dump = flag + 2; |
1629 | 0 | } |
1630 | 0 | else if (*flag == 'd' && flag[1] == 't') { |
1631 | 0 | char *arg = flag + 2; |
1632 | 0 | int ret; |
1633 | |
|
1634 | 0 | if (!*arg && argc > 1 && argv[1][0] != '-') { |
1635 | 0 | arg = argv[1]; |
1636 | 0 | argc--; argv++; |
1637 | 0 | } |
1638 | |
|
1639 | 0 | ret = trace_parse_cmd(arg, &err_msg); |
1640 | 0 | if (ret <= -1) { |
1641 | 0 | if (ret < -1) { |
1642 | 0 | ha_alert("-dt: %s.\n", err_msg); |
1643 | 0 | ha_free(&err_msg); |
1644 | 0 | exit(EXIT_FAILURE); |
1645 | 0 | } |
1646 | 0 | else { |
1647 | 0 | printf("%s\n", err_msg); |
1648 | 0 | ha_free(&err_msg); |
1649 | 0 | exit(0); |
1650 | 0 | } |
1651 | 0 | } |
1652 | 0 | } |
1653 | | #ifdef HA_USE_KTLS |
1654 | | else if (*flag == 'd' && flag[1] == 'T') { |
1655 | | global.tune.options |= GTUNE_NO_KTLS; |
1656 | | } |
1657 | | #endif |
1658 | 0 | else if (*flag == 'd') |
1659 | 0 | arg_mode |= MODE_DEBUG; |
1660 | 0 | else if (*flag == 'c' && flag[1] == 'c') { |
1661 | 0 | arg_mode |= MODE_CHECK_CONDITION; |
1662 | 0 | argv++; |
1663 | 0 | argc--; |
1664 | 0 | check_condition = *argv; |
1665 | 0 | } |
1666 | 0 | else if (*flag == '4') |
1667 | 0 | resolv_accept_families = RSLV_ACCEPT_IPV4 | RSLV_FORCED_FAMILY; |
1668 | 0 | else if (*flag == 'c') |
1669 | 0 | arg_mode |= MODE_CHECK; |
1670 | 0 | else if (*flag == 'D') |
1671 | 0 | arg_mode |= MODE_DAEMON; |
1672 | 0 | else if (*flag == 'W' && flag[1] == 's') { |
1673 | 0 | arg_mode |= MODE_MWORKER | MODE_FOREGROUND; |
1674 | 0 | global.tune.options |= GTUNE_USE_SYSTEMD; |
1675 | 0 | } |
1676 | 0 | else if (*flag == 'W') |
1677 | 0 | arg_mode |= MODE_MWORKER; |
1678 | 0 | else if (*flag == 'q') |
1679 | 0 | arg_mode |= MODE_QUIET; |
1680 | 0 | else if (*flag == 'x') { |
1681 | 0 | if (argc <= 1) { |
1682 | 0 | ha_alert("Unix socket path expected with the -x flag\n\n"); |
1683 | 0 | usage(progname); |
1684 | 0 | } |
1685 | 0 | if (old_unixsocket) |
1686 | 0 | ha_warning("-x option already set, overwriting the value\n"); |
1687 | 0 | old_unixsocket = argv[1]; |
1688 | |
|
1689 | 0 | argv++; |
1690 | 0 | argc--; |
1691 | 0 | } |
1692 | 0 | else if (*flag == 'S') { |
1693 | 0 | struct wordlist *c; |
1694 | |
|
1695 | 0 | if (argc <= 1) { |
1696 | 0 | ha_alert("Socket and optional bind parameters expected with the -S flag\n"); |
1697 | 0 | usage(progname); |
1698 | 0 | } |
1699 | 0 | if ((c = malloc(sizeof(*c))) == NULL || (c->s = strdup(argv[1])) == NULL) { |
1700 | 0 | ha_alert("Cannot allocate memory\n"); |
1701 | 0 | exit(EXIT_FAILURE); |
1702 | 0 | } |
1703 | 0 | LIST_INSERT(&mworker_cli_conf, &c->list); |
1704 | |
|
1705 | 0 | argv++; |
1706 | 0 | argc--; |
1707 | 0 | } |
1708 | 0 | else if (*flag == 's' && (flag[1] == 'f' || flag[1] == 't')) { |
1709 | | /* list of pids to finish ('f') or terminate ('t') */ |
1710 | |
|
1711 | 0 | if (flag[1] == 'f') |
1712 | 0 | oldpids_sig = SIGUSR1; /* finish then exit */ |
1713 | 0 | else |
1714 | 0 | oldpids_sig = SIGTERM; /* terminate immediately */ |
1715 | 0 | while (argc > 1 && argv[1][0] != '-') { |
1716 | 0 | char * endptr = NULL; |
1717 | 0 | oldpids = realloc(oldpids, (nb_oldpids + 1) * sizeof(int)); |
1718 | 0 | if (!oldpids) { |
1719 | 0 | ha_alert("Cannot allocate old pid : out of memory.\n"); |
1720 | 0 | exit(1); |
1721 | 0 | } |
1722 | 0 | argc--; argv++; |
1723 | 0 | errno = 0; |
1724 | 0 | oldpids[nb_oldpids] = strtol(*argv, &endptr, 10); |
1725 | 0 | if (errno) { |
1726 | 0 | ha_alert("-%2s option: failed to parse {%s}: %s\n", |
1727 | 0 | flag, |
1728 | 0 | *argv, strerror(errno)); |
1729 | 0 | exit(1); |
1730 | 0 | } else if (endptr && strlen(endptr)) { |
1731 | 0 | while (isspace((unsigned char)*endptr)) endptr++; |
1732 | 0 | if (*endptr != 0) { |
1733 | 0 | ha_alert("-%2s option: some bytes unconsumed in PID list {%s}\n", |
1734 | 0 | flag, endptr); |
1735 | 0 | exit(1); |
1736 | 0 | } |
1737 | 0 | } |
1738 | 0 | if (oldpids[nb_oldpids] <= 0) |
1739 | 0 | usage(progname); |
1740 | 0 | nb_oldpids++; |
1741 | 0 | } |
1742 | 0 | } |
1743 | | #ifdef DEBUG_UNIT |
1744 | | else if (*flag == 'U') { |
1745 | | if (argc <= 1) { |
1746 | | ha_alert("-U takes a least a unittest name in argument, and must be the last option\n"); |
1747 | | usage(progname); |
1748 | | } |
1749 | | /* this is the last option, we keep the option position */ |
1750 | | argv++; |
1751 | | argc--; |
1752 | | unittest_argc = argc; |
1753 | | break; |
1754 | | } |
1755 | | #endif |
1756 | 0 | else if (flag[0] == '-' && flag[1] == 0) { /* "--" */ |
1757 | | /* now that's a cfgfile list */ |
1758 | 0 | argv++; argc--; |
1759 | 0 | while (argc > 0) { |
1760 | 0 | if (!list_append_cfgfile(&cfg_cfgfiles, *argv, &err_msg)) { |
1761 | 0 | ha_alert("Cannot load configuration file/directory %s : %s\n", |
1762 | 0 | *argv, |
1763 | 0 | err_msg); |
1764 | 0 | exit(1); |
1765 | 0 | } |
1766 | 0 | argv++; argc--; |
1767 | 0 | } |
1768 | 0 | break; |
1769 | 0 | } |
1770 | 0 | else { /* >=2 args */ |
1771 | 0 | argv++; argc--; |
1772 | 0 | if (argc == 0) |
1773 | 0 | usage(progname); |
1774 | |
|
1775 | 0 | switch (*flag) { |
1776 | 0 | case 'C' : change_dir = *argv; break; |
1777 | 0 | case 'n' : cfg_maxconn = atol(*argv); break; |
1778 | 0 | case 'm' : global.rlimit_memmax_all = atol(*argv); break; |
1779 | 0 | case 'N' : cfg_maxpconn = atol(*argv); break; |
1780 | 0 | case 'L' : |
1781 | 0 | free(localpeer); |
1782 | 0 | if ((localpeer = strdup(*argv)) == NULL) { |
1783 | 0 | ha_alert("Cannot allocate memory for local peer.\n"); |
1784 | 0 | exit(EXIT_FAILURE); |
1785 | 0 | } |
1786 | 0 | global.localpeer_cmdline = 1; |
1787 | 0 | break; |
1788 | 0 | case 'f' : |
1789 | 0 | if (!list_append_cfgfile(&cfg_cfgfiles, *argv, &err_msg)) { |
1790 | 0 | ha_alert("Cannot load configuration file/directory %s : %s\n", |
1791 | 0 | *argv, |
1792 | 0 | err_msg); |
1793 | 0 | exit(1); |
1794 | 0 | } |
1795 | 0 | break; |
1796 | 0 | case 'p' : |
1797 | 0 | free(global.pidfile); |
1798 | 0 | if ((global.pidfile = strdup(*argv)) == NULL) { |
1799 | 0 | ha_alert("Cannot allocate memory for pidfile.\n"); |
1800 | 0 | exit(EXIT_FAILURE); |
1801 | 0 | } |
1802 | 0 | break; |
1803 | 0 | default: usage(progname); |
1804 | 0 | } |
1805 | 0 | } |
1806 | 0 | } |
1807 | 0 | else |
1808 | 0 | usage(progname); |
1809 | 0 | argv++; argc--; |
1810 | 0 | } |
1811 | 0 | free(err_msg); |
1812 | 0 | } |
1813 | | |
1814 | | /* handles program arguments. Very minimal parsing is performed, variables are |
1815 | | * fed with some values, and lists are completed with other ones. In case of |
1816 | | * error, it will exit. |
1817 | | */ |
1818 | | static void init_args(int argc, char **argv) |
1819 | 0 | { |
1820 | | /* pre-fill in the global tuning options before we let the cmdline |
1821 | | * change them. |
1822 | | */ |
1823 | 0 | global.tune.options |= GTUNE_USE_SELECT; /* select() is always available */ |
1824 | 0 | #if defined(USE_POLL) |
1825 | 0 | global.tune.options |= GTUNE_USE_POLL; |
1826 | 0 | #endif |
1827 | | #if defined(USE_EPOLL) |
1828 | | global.tune.options |= GTUNE_USE_EPOLL; |
1829 | | #endif |
1830 | | #if defined(USE_KQUEUE) |
1831 | | global.tune.options |= GTUNE_USE_KQUEUE; |
1832 | | #endif |
1833 | | #if defined(USE_EVPORTS) |
1834 | | global.tune.options |= GTUNE_USE_EVPORTS; |
1835 | | #endif |
1836 | | #if defined(USE_LINUX_SPLICE) |
1837 | | global.tune.options |= GTUNE_USE_SPLICE; |
1838 | | #endif |
1839 | | #if defined(USE_GETADDRINFO) |
1840 | | global.tune.options |= GTUNE_USE_GAI; |
1841 | | #endif |
1842 | | #ifdef USE_THREAD |
1843 | | global.tune.options |= GTUNE_IDLE_POOL_SHARED; |
1844 | | #endif |
1845 | 0 | global.tune.options |= GTUNE_STRICT_LIMITS; |
1846 | |
|
1847 | 0 | global.tune.options |= GTUNE_USE_FAST_FWD; /* Use fast-forward by default */ |
1848 | | |
1849 | | /* Use zero-copy forwarding by default */ |
1850 | 0 | global.tune.no_zero_copy_fwd = 0; |
1851 | | |
1852 | | /* keep a copy of original arguments for the master process */ |
1853 | 0 | old_argv = copy_argv(argc, argv); |
1854 | 0 | if (!old_argv) { |
1855 | 0 | ha_alert("failed to copy argv.\n"); |
1856 | 0 | exit(EXIT_FAILURE); |
1857 | 0 | } |
1858 | | |
1859 | 0 | haproxy_init_args(argc, argv); |
1860 | 0 | } |
1861 | | |
1862 | | /* call the various keyword dump functions based on the comma-delimited list of |
1863 | | * classes in kwd_dump. |
1864 | | */ |
1865 | | static void dump_registered_keywords(void) |
1866 | 0 | { |
1867 | 0 | char *end; |
1868 | 0 | int all __maybe_unused = 0; |
1869 | |
|
1870 | 0 | for (; kwd_dump && *kwd_dump; kwd_dump = end) { |
1871 | 0 | end = strchr(kwd_dump, ','); |
1872 | 0 | if (end) |
1873 | 0 | *(end++) = 0; |
1874 | |
|
1875 | 0 | if (strcmp(kwd_dump, "help") == 0) { |
1876 | 0 | printf("# List of supported keyword classes:\n"); |
1877 | 0 | printf("all: list all keywords\n"); |
1878 | 0 | printf("acl: ACL keywords\n"); |
1879 | 0 | printf("cfg: configuration keywords\n"); |
1880 | 0 | printf("cli: CLI keywords\n"); |
1881 | 0 | printf("cnv: sample converter keywords\n"); |
1882 | 0 | printf("flt: filter names\n"); |
1883 | 0 | printf("smp: sample fetch functions\n"); |
1884 | 0 | printf("svc: service names\n"); |
1885 | 0 | continue; |
1886 | 0 | } |
1887 | 0 | else if (strcmp(kwd_dump, "all") == 0) { |
1888 | 0 | all = 1; |
1889 | 0 | } |
1890 | | |
1891 | 0 | if (all || strcmp(kwd_dump, "acl") == 0) { |
1892 | 0 | printf("# List of registered ACL keywords:\n"); |
1893 | 0 | acl_dump_kwd(); |
1894 | 0 | } |
1895 | |
|
1896 | 0 | if (all || strcmp(kwd_dump, "cfg") == 0) { |
1897 | 0 | printf("# List of registered configuration keywords:\n"); |
1898 | 0 | cfg_dump_registered_keywords(); |
1899 | 0 | } |
1900 | |
|
1901 | 0 | if (all || strcmp(kwd_dump, "cli") == 0) { |
1902 | 0 | printf("# List of registered CLI keywords:\n"); |
1903 | 0 | cli_list_keywords(); |
1904 | 0 | } |
1905 | |
|
1906 | 0 | if (all || strcmp(kwd_dump, "cnv") == 0) { |
1907 | 0 | printf("# List of registered sample converter functions:\n"); |
1908 | 0 | smp_dump_conv_kw(); |
1909 | 0 | } |
1910 | |
|
1911 | 0 | if (all || strcmp(kwd_dump, "flt") == 0) { |
1912 | 0 | printf("# List of registered filter names:\n"); |
1913 | 0 | flt_dump_kws(NULL); |
1914 | 0 | } |
1915 | |
|
1916 | 0 | if (all || strcmp(kwd_dump, "smp") == 0) { |
1917 | 0 | printf("# List of registered sample fetch functions:\n"); |
1918 | 0 | smp_dump_fetch_kw(); |
1919 | 0 | } |
1920 | |
|
1921 | 0 | if (all || strcmp(kwd_dump, "svc") == 0) { |
1922 | 0 | printf("# List of registered service names:\n"); |
1923 | 0 | list_services(NULL); |
1924 | 0 | } |
1925 | 0 | } |
1926 | 0 | } |
1927 | | |
1928 | | /* Generate a random cluster-secret in case the setting is not provided in the |
1929 | | * configuration. This allows to use features which rely on it albeit with some |
1930 | | * limitations. |
1931 | | */ |
1932 | | static void generate_random_cluster_secret() |
1933 | 0 | { |
1934 | | /* used as a default random cluster-secret if none defined. */ |
1935 | 0 | uint64_t rand; |
1936 | | |
1937 | | /* The caller must not overwrite an already defined secret. */ |
1938 | 0 | BUG_ON(cluster_secret_isset); |
1939 | |
|
1940 | 0 | rand = ha_random64(); |
1941 | 0 | memcpy(global.cluster_secret, &rand, sizeof(rand)); |
1942 | 0 | rand = ha_random64(); |
1943 | 0 | memcpy(global.cluster_secret + sizeof(rand), &rand, sizeof(rand)); |
1944 | 0 | cluster_secret_isset = 1; |
1945 | 0 | } |
1946 | | |
1947 | | /* |
1948 | | * This function does daemonization fork. It only returns if everything is OK. |
1949 | | * If something fails, it exits. |
1950 | | */ |
1951 | | static void apply_daemon_mode() |
1952 | 0 | { |
1953 | 0 | int ret; |
1954 | 0 | int wstatus = 0; |
1955 | 0 | int exitcode = 0; |
1956 | 0 | pid_t child_pid; |
1957 | 0 | char buf[2]; |
1958 | |
|
1959 | 0 | if (pipe(daemon_fd) < 0) { |
1960 | 0 | ha_alert("[%s.main()] Cannot create pipe for getting the status of " |
1961 | 0 | "child process: %s.\n", progname, strerror(errno)); |
1962 | |
|
1963 | 0 | exit(EXIT_FAILURE); |
1964 | 0 | } |
1965 | | |
1966 | 0 | ret = fork(); |
1967 | 0 | switch(ret) { |
1968 | 0 | case -1: |
1969 | 0 | ha_alert("[%s.main()] Cannot fork.\n", progname); |
1970 | 0 | protocol_unbind_all(); |
1971 | 0 | exit(1); /* there has been an error */ |
1972 | 0 | case 0: |
1973 | | /* in child, change the process group ID, in the master-worker |
1974 | | * mode, this will be the master process |
1975 | | */ |
1976 | 0 | close(daemon_fd[0]); |
1977 | 0 | daemon_fd[0] = -1; |
1978 | 0 | setsid(); |
1979 | |
|
1980 | 0 | break; |
1981 | 0 | default: |
1982 | | /* in parent */ |
1983 | 0 | close(daemon_fd[1]); |
1984 | 0 | daemon_fd[1] = -1; |
1985 | | /* In standalone + daemon modes: parent (launcher process) tries |
1986 | | * to read the child's (daemonized process) "READY" message. Child |
1987 | | * writes this message, when he has finished initialization. If |
1988 | | * child failed to start, we get his status. |
1989 | | * In master-worker mode: daemonized process is the master. He |
1990 | | * sends his READY message to launcher, only when |
1991 | | * he has received the READY message from the worker, see |
1992 | | * _send_status(). |
1993 | | */ |
1994 | 0 | if (read(daemon_fd[0], buf, 1) == 0) { |
1995 | 0 | child_pid = waitpid(ret, &wstatus, 0); |
1996 | 0 | if (child_pid < 0) { |
1997 | 0 | ha_alert("[%s.main()] waitpid() failed: %s\n", |
1998 | 0 | progname, strerror(errno)); |
1999 | 0 | exit(EXIT_FAILURE); |
2000 | 0 | } |
2001 | 0 | if (WIFEXITED(wstatus)) |
2002 | 0 | wstatus = WEXITSTATUS(wstatus); |
2003 | 0 | else if (WIFSIGNALED(wstatus)) |
2004 | 0 | wstatus = 128 + WTERMSIG(wstatus); |
2005 | 0 | else |
2006 | 0 | wstatus = 255; |
2007 | |
|
2008 | 0 | ha_alert("Process %d exited with code %d (%s)\n", |
2009 | 0 | child_pid, wstatus, (wstatus >= 128) ? strsignal(wstatus - 128) : "Exit"); |
2010 | 0 | if (wstatus != 0 && wstatus != 143) |
2011 | 0 | exitcode = wstatus; |
2012 | 0 | } |
2013 | 0 | exit(exitcode); |
2014 | 0 | } |
2015 | 0 | } |
2016 | | |
2017 | | /* Returns 0, if everything is OK. If open() fails, returns -1. */ |
2018 | | int handle_pidfile(void) |
2019 | 0 | { |
2020 | 0 | char pidstr[100]; |
2021 | |
|
2022 | 0 | unlink(global.pidfile); |
2023 | 0 | pidfd = open(global.pidfile, O_CREAT | O_WRONLY | O_TRUNC, 0644); |
2024 | 0 | if (pidfd < 0) { |
2025 | 0 | ha_alert("[%s.main()] Cannot create pidfile %s\n", progname, global.pidfile); |
2026 | 0 | return -1; |
2027 | 0 | } |
2028 | 0 | snprintf(pidstr, sizeof(pidstr), "%d\n", (int)getpid()); |
2029 | 0 | DISGUISE(write(pidfd, pidstr, strlen(pidstr))); |
2030 | 0 | close(pidfd); |
2031 | | /* We won't ever use this anymore */ |
2032 | 0 | ha_free(&global.pidfile); |
2033 | |
|
2034 | 0 | return 0; |
2035 | 0 | } |
2036 | | |
2037 | | static void get_listeners_fd() |
2038 | 0 | { |
2039 | | /* Try to get the listeners FD from the previous process using |
2040 | | * _getsocks on the stat socket, it must never been done in wait mode |
2041 | | * and check mode |
2042 | | */ |
2043 | |
|
2044 | 0 | if (strcmp("/dev/null", old_unixsocket) != 0) { |
2045 | 0 | if (sock_get_old_sockets(old_unixsocket) != 0) { |
2046 | 0 | ha_alert("Failed to get the sockets from the old process!\n"); |
2047 | 0 | if (!(global.mode & MODE_MWORKER)) |
2048 | 0 | exit(1); |
2049 | 0 | } |
2050 | 0 | } |
2051 | 0 | } |
2052 | | |
2053 | | static void bind_listeners() |
2054 | 0 | { |
2055 | 0 | int err, retry; |
2056 | | |
2057 | | /* We will loop at most 100 times with 10 ms delay each time. |
2058 | | * That's at most 1 second. We only send a signal to old pids |
2059 | | * if we cannot grab at least one port. |
2060 | | */ |
2061 | 0 | retry = MAX_START_RETRIES; |
2062 | 0 | err = ERR_NONE; |
2063 | 0 | while (retry >= 0) { |
2064 | 0 | struct timeval w; |
2065 | 0 | err = protocol_bind_all(retry == 0 || nb_oldpids == 0); |
2066 | | /* exit the loop on no error or fatal error */ |
2067 | 0 | if ((err & (ERR_RETRYABLE|ERR_FATAL)) != ERR_RETRYABLE) |
2068 | 0 | break; |
2069 | 0 | if (nb_oldpids == 0 || retry == 0) |
2070 | 0 | break; |
2071 | | |
2072 | | /* FIXME-20060514: Solaris and OpenBSD do not support shutdown() on |
2073 | | * listening sockets. So on those platforms, it would be wiser to |
2074 | | * simply send SIGUSR1, which will not be undoable. |
2075 | | */ |
2076 | 0 | if (tell_old_pids(SIGTTOU) == 0) { |
2077 | | /* no need to wait if we can't contact old pids */ |
2078 | 0 | retry = 0; |
2079 | 0 | continue; |
2080 | 0 | } |
2081 | | /* give some time to old processes to stop listening */ |
2082 | 0 | w.tv_sec = 0; |
2083 | 0 | w.tv_usec = 10*1000; |
2084 | 0 | select(0, NULL, NULL, NULL, &w); |
2085 | 0 | retry--; |
2086 | 0 | } |
2087 | | /* Note: protocol_bind_all() sends an alert when it fails. */ |
2088 | 0 | if ((err & ~ERR_WARN) != ERR_NONE) { |
2089 | 0 | ha_alert("[%s.main()] Some protocols failed to start their listeners! Exiting.\n", progname); |
2090 | 0 | if (retry != MAX_START_RETRIES && nb_oldpids) |
2091 | 0 | tell_old_pids(SIGTTIN); |
2092 | 0 | protocol_unbind_all(); /* cleanup everything we can */ |
2093 | 0 | exit(1); |
2094 | 0 | } |
2095 | 0 | } |
2096 | | |
2097 | | /* |
2098 | | * This function does some initialization steps, which are better to perform |
2099 | | * before config parsing. It only returns if everything is OK. If something |
2100 | | * fails, it exits. |
2101 | | */ |
2102 | | static void step_init_1() |
2103 | 0 | { |
2104 | | #ifdef USE_OPENSSL |
2105 | | #ifdef USE_OPENSSL_WOLFSSL |
2106 | | wolfSSL_Init(); |
2107 | | wolfSSL_Debugging_ON(); |
2108 | | #endif |
2109 | | |
2110 | | #ifdef OPENSSL_IS_AWSLC |
2111 | | const char *version_str = OpenSSL_version(OPENSSL_VERSION); |
2112 | | if (strncmp(version_str, "AWS-LC", 6) != 0) { |
2113 | | ha_alert("HAPRoxy built with AWS-LC but running with %s.\n", version_str); |
2114 | | exit(1); |
2115 | | } |
2116 | | #endif |
2117 | | |
2118 | | #if (HA_OPENSSL_VERSION_NUMBER < 0x1010000fL) |
2119 | | /* Initialize the error strings of OpenSSL |
2120 | | * It only needs to be done explicitly with older versions of the SSL |
2121 | | * library. On newer versions, errors strings are loaded during start |
2122 | | * up. */ |
2123 | | SSL_load_error_strings(); |
2124 | | #endif |
2125 | | #endif /* USE_OPENSSL */ |
2126 | | |
2127 | | /* saves ptr to ring in startup_logs var */ |
2128 | 0 | startup_logs_init(); |
2129 | |
|
2130 | 0 | if (init_acl() != 0) |
2131 | 0 | exit(1); |
2132 | | |
2133 | | /* Initialise lua. */ |
2134 | 0 | hlua_init(); |
2135 | | |
2136 | | /* set modes given from cmdline */ |
2137 | 0 | global.mode |= (arg_mode & (MODE_DAEMON | MODE_MWORKER | MODE_FOREGROUND | MODE_VERBOSE |
2138 | 0 | | MODE_QUIET | MODE_CHECK | MODE_DEBUG | MODE_ZERO_WARNING |
2139 | 0 | | MODE_DIAG | MODE_CHECK_CONDITION | MODE_DUMP_LIBS | MODE_DUMP_KWD |
2140 | 0 | | MODE_DUMP_CFG | MODE_DUMP_NB_L)); |
2141 | | |
2142 | | /* Do check_condition, if we started with -cc, and exit. */ |
2143 | 0 | if (global.mode & MODE_CHECK_CONDITION) |
2144 | 0 | do_check_condition(); |
2145 | |
|
2146 | 0 | if (change_dir && chdir(change_dir) < 0) { |
2147 | 0 | ha_alert("Could not change to directory %s : %s\n", change_dir, strerror(errno)); |
2148 | 0 | exit(1); |
2149 | 0 | } |
2150 | 0 | } |
2151 | | |
2152 | | /* |
2153 | | * This is a second part of the late init (previous init() function). It should |
2154 | | * be called after the stage, when all basic runtime modes (daemon, master-worker) |
2155 | | * are already applied. It calls routines from pre_check_list and also functions, |
2156 | | * which allocate pools, initialize proxies, compute ideal maxconn, it also |
2157 | | * initializes postmortem structure at the end. It only returns if everything is |
2158 | | * OK. If something fails, it exits. |
2159 | | */ |
2160 | | static void step_init_2(int argc, char** argv) |
2161 | 0 | { |
2162 | 0 | int err_code = 0; |
2163 | 0 | struct proxy *px; |
2164 | 0 | struct post_check_fct *pcf; |
2165 | 0 | struct pre_check_fct *prcf; |
2166 | 0 | const char *cc, *cflags, *opts; |
2167 | | |
2168 | | /* Free last defaults if it is unnamed and unreferenced. */ |
2169 | 0 | if (last_defproxy && last_defproxy->id[0] == '\0' && |
2170 | 0 | !last_defproxy->conf.refcount) { |
2171 | 0 | defaults_px_destroy(last_defproxy); |
2172 | 0 | } |
2173 | 0 | last_defproxy = NULL; /* This variable is not used after parsing. */ |
2174 | |
|
2175 | 0 | if (global.tune.options & GTUNE_PURGE_DEFAULTS) { |
2176 | | /* destroy unreferenced defaults proxies */ |
2177 | 0 | defaults_px_destroy_all_unref(); |
2178 | 0 | } |
2179 | 0 | else { |
2180 | 0 | defaults_px_ref_all(); |
2181 | 0 | } |
2182 | |
|
2183 | 0 | list_for_each_entry(prcf, &pre_check_list, list) { |
2184 | 0 | err_code |= prcf->fct(); |
2185 | 0 | if (err_code & (ERR_ABORT|ERR_FATAL)) { |
2186 | 0 | ha_alert("Fatal errors found in configuration.\n"); |
2187 | 0 | exit(1); |
2188 | 0 | } |
2189 | 0 | } |
2190 | | |
2191 | | /* update the ready date that will be used to count the startup time |
2192 | | * during config checks (e.g. to schedule certain tasks if needed) |
2193 | | */ |
2194 | 0 | clock_update_date(0, 1); |
2195 | 0 | clock_adjust_now_offset(); |
2196 | 0 | ready_date = date; |
2197 | |
|
2198 | | #ifdef USE_CPU_AFFINITY |
2199 | | /* we've already read the config and know what CPUs are expected |
2200 | | * to be used. Let's check which of these are usable. |
2201 | | */ |
2202 | | cpu_detect_usable(); |
2203 | | |
2204 | | /* Now detect how CPUs are arranged */ |
2205 | | cpu_detect_topology(); |
2206 | | |
2207 | | /* fixup missing info */ |
2208 | | cpu_fixup_topology(); |
2209 | | |
2210 | | /* compose clusters */ |
2211 | | cpu_compose_clusters(); |
2212 | | |
2213 | | /* refine topology-based CPU sets */ |
2214 | | cpu_refine_cpusets(); |
2215 | | #endif |
2216 | | |
2217 | | /* detect the optimal thread-groups and nbthreads if not set */ |
2218 | 0 | thread_detect_count(); |
2219 | | |
2220 | | /* Note: global.nbthread will be initialized as part of this call */ |
2221 | 0 | err_code |= check_config_validity(); |
2222 | 0 | if (*initial_cwd && chdir(initial_cwd) == -1) { |
2223 | 0 | ha_alert("Impossible to get back to initial directory '%s' : %s\n", initial_cwd, strerror(errno)); |
2224 | 0 | exit(1); |
2225 | 0 | } |
2226 | | |
2227 | | /* now that config was parsed and checked |
2228 | | * prepare and preload shm-stats-file (if set) |
2229 | | */ |
2230 | 0 | err_code |= shm_stats_file_prepare(); |
2231 | 0 | if (err_code & (ERR_ABORT|ERR_FATAL)) |
2232 | 0 | exit(1); |
2233 | | |
2234 | | /* update the ready date to also account for the check time */ |
2235 | 0 | clock_update_date(0, 1); |
2236 | 0 | clock_adjust_now_offset(); |
2237 | 0 | ready_date = date; |
2238 | |
|
2239 | 0 | list_for_each_entry(px, &proxies, global_list) { |
2240 | 0 | struct server *srv; |
2241 | 0 | struct post_proxy_check_fct *ppcf; |
2242 | 0 | struct post_server_check_fct *pscf; |
2243 | |
|
2244 | 0 | if (px->flags & (PR_FL_DISABLED|PR_FL_STOPPED)) |
2245 | 0 | continue; |
2246 | | |
2247 | 0 | list_for_each_entry(pscf, &post_server_check_list, list) { |
2248 | 0 | for (srv = px->srv; srv; srv = srv->next) { |
2249 | 0 | err_code |= pscf->fct(srv); |
2250 | 0 | if (err_code & (ERR_ABORT|ERR_FATAL)) { |
2251 | 0 | ha_alert("Fatal errors found in configuration.\n"); |
2252 | 0 | exit(1); |
2253 | 0 | } |
2254 | 0 | } |
2255 | 0 | } |
2256 | 0 | list_for_each_entry(ppcf, &post_proxy_check_list, list) { |
2257 | 0 | err_code |= ppcf->fct(px); |
2258 | 0 | if (err_code & (ERR_ABORT|ERR_FATAL)) { |
2259 | 0 | ha_alert("Fatal errors found in configuration.\n"); |
2260 | 0 | exit(1); |
2261 | 0 | } |
2262 | |
|
2263 | 0 | } |
2264 | 0 | px->flags |= PR_FL_CHECKED; |
2265 | 0 | } |
2266 | | |
2267 | 0 | err_code |= pattern_finalize_config(); |
2268 | 0 | if (err_code & (ERR_ABORT|ERR_FATAL)) { |
2269 | 0 | ha_alert("Failed to finalize pattern config.\n"); |
2270 | 0 | exit(1); |
2271 | 0 | } |
2272 | | |
2273 | 0 | if (global.rlimit_memmax_all) |
2274 | 0 | global.rlimit_memmax = global.rlimit_memmax_all; |
2275 | |
|
2276 | | #ifdef USE_NS |
2277 | | err_code |= netns_init(); |
2278 | | if (err_code & (ERR_ABORT|ERR_FATAL)) { |
2279 | | ha_alert("Failed to initialize namespace support.\n"); |
2280 | | exit(1); |
2281 | | } |
2282 | | #endif |
2283 | |
|
2284 | 0 | thread_detect_binding_discrepancies(); |
2285 | 0 | thread_detect_more_than_cpus(); |
2286 | | |
2287 | | /* Apply server states */ |
2288 | 0 | apply_server_state(); |
2289 | | |
2290 | | /* Preload internal counters. */ |
2291 | 0 | apply_stats_file(); |
2292 | |
|
2293 | 0 | for (px = proxies_list; px; px = px->next) |
2294 | 0 | srv_compute_all_admin_states(px); |
2295 | | |
2296 | | /* Apply servers' configured address */ |
2297 | 0 | err_code |= srv_init_addr(); |
2298 | 0 | if (err_code & (ERR_ABORT|ERR_FATAL)) { |
2299 | 0 | ha_alert("Failed to initialize server(s) addr.\n"); |
2300 | 0 | exit(1); |
2301 | 0 | } |
2302 | | |
2303 | 0 | if (warned & WARN_ANY && global.mode & MODE_ZERO_WARNING) { |
2304 | 0 | ha_alert("Some warnings were found and 'zero-warning' is set. Aborting.\n"); |
2305 | 0 | exit(1); |
2306 | 0 | } |
2307 | | |
2308 | | #if defined(HA_HAVE_DUMP_LIBS) |
2309 | | if (global.mode & MODE_DUMP_LIBS && !master) { |
2310 | | qfprintf(stdout, "List of loaded object files:\n"); |
2311 | | chunk_reset(&trash); |
2312 | | if (dump_libs(&trash, ((arg_mode & (MODE_QUIET|MODE_VERBOSE)) == MODE_VERBOSE))) |
2313 | | printf("%s", trash.area); |
2314 | | } |
2315 | | #endif |
2316 | | |
2317 | 0 | if (global.mode & MODE_DUMP_KWD && !master) |
2318 | 0 | dump_registered_keywords(); |
2319 | |
|
2320 | 0 | if (global.mode & MODE_DIAG) { |
2321 | 0 | cfg_run_diagnostics(); |
2322 | 0 | } |
2323 | |
|
2324 | 0 | if (global.mode & MODE_CHECK) { |
2325 | 0 | struct peers *pr; |
2326 | 0 | struct proxy *px; |
2327 | |
|
2328 | 0 | if (warned & WARN_ANY) |
2329 | 0 | qfprintf(stdout, "Warnings were found.\n"); |
2330 | |
|
2331 | 0 | for (pr = cfg_peers; pr; pr = pr->next) |
2332 | 0 | if (pr->peers_fe) |
2333 | 0 | break; |
2334 | |
|
2335 | 0 | for (px = proxies_list; px; px = px->next) |
2336 | 0 | if (!(px->flags & (PR_FL_DISABLED|PR_FL_STOPPED)) && px->li_all) |
2337 | 0 | break; |
2338 | |
|
2339 | 0 | if (!px) { |
2340 | | /* We may only have log-forward section */ |
2341 | 0 | for (px = cfg_log_forward; px; px = px->next) |
2342 | 0 | if (!(px->flags & (PR_FL_DISABLED|PR_FL_STOPPED)) && px->li_all) |
2343 | 0 | break; |
2344 | 0 | } |
2345 | |
|
2346 | 0 | if (pr || px) { |
2347 | | /* At least one peer or one listener has been found */ |
2348 | 0 | if (global.mode & MODE_VERBOSE) |
2349 | 0 | qfprintf(stdout, "Configuration file is valid\n"); |
2350 | 0 | deinit_and_exit(0); |
2351 | 0 | } |
2352 | 0 | qfprintf(stdout, "Configuration file has no error but will not start (no listener) => exit(2).\n"); |
2353 | 0 | exit(2); |
2354 | 0 | } |
2355 | | |
2356 | 0 | if (global.mode & MODE_DUMP_CFG) |
2357 | 0 | deinit_and_exit(0); |
2358 | | |
2359 | | /* now we know the buffer size, we can initialize the channels and buffers */ |
2360 | 0 | if (!init_buffer()) |
2361 | 0 | exit(1); // error already reported |
2362 | | |
2363 | 0 | list_for_each_entry(pcf, &post_check_list, list) { |
2364 | 0 | err_code |= pcf->fct(); |
2365 | 0 | if (err_code & (ERR_ABORT|ERR_FATAL)) |
2366 | 0 | exit(1); |
2367 | 0 | } |
2368 | | |
2369 | | /* set the default maxconn in the master, but let it be rewritable with -n */ |
2370 | 0 | if (master) |
2371 | 0 | global.maxconn = MASTER_MAXCONN; |
2372 | |
|
2373 | 0 | if (cfg_maxconn > 0) |
2374 | 0 | global.maxconn = cfg_maxconn; |
2375 | |
|
2376 | 0 | if (global.cli_fe) |
2377 | 0 | global.maxsock += global.cli_fe->maxconn; |
2378 | |
|
2379 | 0 | if (cfg_peers) { |
2380 | | /* peers also need to bypass global maxconn */ |
2381 | 0 | struct peers *p = cfg_peers; |
2382 | |
|
2383 | 0 | for (p = cfg_peers; p; p = p->next) |
2384 | 0 | if (p->peers_fe) |
2385 | 0 | global.maxsock += p->peers_fe->maxconn; |
2386 | 0 | } |
2387 | | |
2388 | | /* count listeners, checks, plus 1 poller and one wake-up pipe (2fd) per thread */ |
2389 | 0 | global.est_fd_usage = global.maxsock + 3 * global.nbthread; |
2390 | | |
2391 | | /* Compute the global.maxconn and possibly global.maxsslconn values */ |
2392 | 0 | set_global_maxconn(); |
2393 | 0 | global.maxsock = compute_ideal_maxsock(global.maxconn); |
2394 | 0 | global.hardmaxconn = global.maxconn; |
2395 | 0 | if (!global.maxpipes) |
2396 | 0 | global.maxpipes = compute_ideal_maxpipes(); |
2397 | | |
2398 | | /* update connection pool thresholds */ |
2399 | 0 | global.tune.pool_low_count = ((long long)global.maxsock * global.tune.pool_low_ratio + 99) / 100; |
2400 | 0 | global.tune.pool_high_count = ((long long)global.maxsock * global.tune.pool_high_ratio + 99) / 100; |
2401 | |
|
2402 | 0 | proxy_adjust_all_maxconn(); |
2403 | |
|
2404 | 0 | if (global.tune.maxpollevents <= 0) |
2405 | 0 | global.tune.maxpollevents = MAX_POLL_EVENTS; |
2406 | |
|
2407 | 0 | if (global.tune.max_rules_at_once <= 0) |
2408 | 0 | global.tune.max_rules_at_once = MAX_RULES_AT_ONCE; |
2409 | |
|
2410 | 0 | if (global.tune.runqueue_depth <= 0) { |
2411 | | /* tests on various thread counts from 1 to 64 have shown an |
2412 | | * optimal queue depth following roughly 1/sqrt(threads). |
2413 | | */ |
2414 | 0 | int s = my_flsl(global.nbthread); |
2415 | 0 | s += (global.nbthread / s); // roughly twice the sqrt. |
2416 | 0 | global.tune.runqueue_depth = RUNQUEUE_DEPTH * 2 / s; |
2417 | 0 | } |
2418 | |
|
2419 | 0 | if (global.tune.recv_enough == 0) |
2420 | 0 | global.tune.recv_enough = MIN_RECV_AT_ONCE_ENOUGH; |
2421 | |
|
2422 | 0 | if (global.tune.maxrewrite >= global.tune.bufsize / 2) |
2423 | 0 | global.tune.maxrewrite = global.tune.bufsize / 2; |
2424 | | |
2425 | | /* Realloc trash buffers because global.tune.bufsize may have changed */ |
2426 | 0 | if (!init_trash_buffers(0)) { |
2427 | 0 | ha_alert("failed to initialize trash buffers.\n"); |
2428 | 0 | exit(1); |
2429 | 0 | } |
2430 | | |
2431 | 0 | if (!init_log_buffers()) { |
2432 | 0 | ha_alert("failed to initialize log buffers.\n"); |
2433 | 0 | exit(1); |
2434 | 0 | } |
2435 | | |
2436 | 0 | if (!cluster_secret_isset) |
2437 | 0 | generate_random_cluster_secret(); |
2438 | | |
2439 | | /* |
2440 | | * Note: we could register external pollers here. |
2441 | | * Built-in pollers have been registered before main(). |
2442 | | */ |
2443 | |
|
2444 | 0 | if (!(global.tune.options & GTUNE_USE_KQUEUE)) |
2445 | 0 | disable_poller("kqueue"); |
2446 | |
|
2447 | 0 | if (!(global.tune.options & GTUNE_USE_EVPORTS)) |
2448 | 0 | disable_poller("evports"); |
2449 | |
|
2450 | 0 | if (!(global.tune.options & GTUNE_USE_EPOLL)) |
2451 | 0 | disable_poller("epoll"); |
2452 | |
|
2453 | 0 | if (!(global.tune.options & GTUNE_USE_POLL)) |
2454 | 0 | disable_poller("poll"); |
2455 | |
|
2456 | 0 | if (!(global.tune.options & GTUNE_USE_SELECT)) |
2457 | 0 | disable_poller("select"); |
2458 | | |
2459 | | /* Note: we could disable any poller by name here */ |
2460 | |
|
2461 | 0 | if ((global.mode & (MODE_VERBOSE|MODE_DEBUG)) && !master) { |
2462 | 0 | list_pollers(stderr); |
2463 | 0 | fprintf(stderr, "\n"); |
2464 | 0 | list_filters(stderr); |
2465 | 0 | } |
2466 | |
|
2467 | 0 | if (!init_pollers()) { |
2468 | 0 | ha_alert("No polling mechanism available.\n" |
2469 | 0 | " This may happen when using thread-groups with old pollers (poll/select), or\n" |
2470 | 0 | " it is possible that haproxy was built with TARGET=generic and that FD_SETSIZE\n" |
2471 | 0 | " is too low on this platform to support maxconn and the number of listeners\n" |
2472 | 0 | " and servers. You should rebuild haproxy specifying your system using TARGET=\n" |
2473 | 0 | " in order to support other polling systems (poll, epoll, kqueue) or reduce the\n" |
2474 | 0 | " global maxconn setting to accommodate the system's limitation. For reference,\n" |
2475 | 0 | " FD_SETSIZE=%d on this system, global.maxconn=%d resulting in a maximum of\n" |
2476 | 0 | " %d file descriptors. You should thus reduce global.maxconn by %d. Also,\n" |
2477 | 0 | " check build settings using 'haproxy -vv'.\n\n", |
2478 | 0 | FD_SETSIZE, global.maxconn, global.maxsock, (global.maxsock + 1 - FD_SETSIZE) / 2); |
2479 | 0 | exit(1); |
2480 | 0 | } |
2481 | 0 | if (global.mode & (MODE_VERBOSE|MODE_DEBUG)) { |
2482 | 0 | printf("Using %s() as the polling mechanism.\n", cur_poller.name); |
2483 | 0 | } |
2484 | |
|
2485 | 0 | if (!global.node) |
2486 | 0 | global.node = strdup(hostname); |
2487 | | |
2488 | | /* stop disabled proxies */ |
2489 | 0 | for (px = proxies_list; px; px = px->next) { |
2490 | 0 | if (px->flags & (PR_FL_DISABLED|PR_FL_STOPPED)) |
2491 | 0 | stop_proxy(px); |
2492 | 0 | } |
2493 | |
|
2494 | 0 | if (!hlua_post_init()) |
2495 | 0 | exit(1); |
2496 | | |
2497 | | /* Set the per-thread pool cache size to the default value if not set. |
2498 | | * This is the right place to decide to automatically adjust it (e.g. |
2499 | | * check L2 cache size, thread counts or take into account certain |
2500 | | * expensive pools). |
2501 | | */ |
2502 | 0 | if (!global.tune.pool_cache_size) |
2503 | 0 | global.tune.pool_cache_size = CONFIG_HAP_POOL_CACHE_SIZE; |
2504 | | |
2505 | | /* fill in a few info about our version and build options */ |
2506 | 0 | chunk_reset(&trash); |
2507 | | |
2508 | | /* toolchain */ |
2509 | 0 | cc = chunk_newstr(&trash); |
2510 | 0 | #if defined(__clang_version__) |
2511 | 0 | chunk_appendf(&trash, "clang-" __clang_version__); |
2512 | | #elif defined(__VERSION__) |
2513 | | chunk_appendf(&trash, "gcc-" __VERSION__); |
2514 | | #endif |
2515 | | #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) |
2516 | | chunk_appendf(&trash, "+asan"); |
2517 | | #endif |
2518 | | /* toolchain opts */ |
2519 | 0 | cflags = chunk_newstr(&trash); |
2520 | 0 | chunk_appendf(&trash, "%s", pm_toolchain_opts); |
2521 | | |
2522 | | /* settings */ |
2523 | 0 | opts = chunk_newstr(&trash); |
2524 | 0 | chunk_appendf(&trash, "TARGET='%s'", pm_target_opts); |
2525 | |
|
2526 | 0 | post_mortem_add_component("haproxy", haproxy_version, cc, cflags, opts, argv[0]); |
2527 | 0 | } |
2528 | | |
2529 | | /* This is a third part of the late init sequence, where we register signals for |
2530 | | * process in worker and in standalone modes. We also check here, if the |
2531 | | * global.maxsock calculated in step_init_2() could be applied as the nofile limit |
2532 | | * for the process. Memory limit, if set, will be applied here as well. If some |
2533 | | * capabilities were set on the haproxy binary by administrator, we will try to |
2534 | | * put it into the process Effective capabilities set. It only returns if |
2535 | | * everything is OK. If something fails, it exits. |
2536 | | */ |
2537 | | static void step_init_3(void) |
2538 | 0 | { |
2539 | 0 | if (master) { |
2540 | 0 | signal_register_fct(SIGQUIT, NULL, 0); |
2541 | 0 | signal_register_fct(SIGUSR1, NULL, 0); |
2542 | 0 | signal_register_fct(SIGHUP, NULL, 0); |
2543 | 0 | signal_register_fct(SIGUSR2, NULL, 0); |
2544 | 0 | } else { |
2545 | 0 | signal_register_fct(SIGQUIT, dump, SIGQUIT); |
2546 | 0 | signal_register_fct(SIGUSR1, sig_soft_stop, SIGUSR1); |
2547 | 0 | signal_register_fct(SIGHUP, sig_dump_state, SIGHUP); |
2548 | 0 | signal_register_fct(SIGUSR2, NULL, 0); |
2549 | 0 | } |
2550 | | |
2551 | | /* Always catch SIGPIPE even on platforms which define MSG_NOSIGNAL. |
2552 | | * Some recent FreeBSD setups report broken pipes, and MSG_NOSIGNAL |
2553 | | * was defined there, so let's stay on the safe side. |
2554 | | */ |
2555 | 0 | signal_register_fct(SIGPIPE, NULL, 0); |
2556 | | |
2557 | | /* ulimits */ |
2558 | 0 | apply_nofile_limit(); |
2559 | 0 | apply_memory_limit(); |
2560 | |
|
2561 | | #if defined(USE_LINUX_CAP) |
2562 | | /* If CAP_NET_BIND_SERVICE is in binary file permitted set and process |
2563 | | * is started and run under the same non-root user, this allows |
2564 | | * binding to privileged ports. |
2565 | | */ |
2566 | | if (!master) |
2567 | | prepare_caps_from_permitted_set(geteuid(), global.uid); |
2568 | | #endif |
2569 | 0 | } |
2570 | | |
2571 | | /* This is a forth part of the late init sequence, where we apply verbosity |
2572 | | * modes, check nofile current limit, preallocate fds, update the ready date |
2573 | | * the last time, and close PID fd. It only returns if everything is OK. If |
2574 | | * something fails, it exits. |
2575 | | */ |
2576 | | static void step_init_4(void) |
2577 | 0 | { |
2578 | | /* MODE_QUIET is applied here, it can inhibit alerts and warnings below this line */ |
2579 | 0 | if (getenv("HAPROXY_MWORKER_REEXEC") != NULL) { |
2580 | | /* either stdin/out/err are already closed or should stay as they are. */ |
2581 | 0 | if ((global.mode & MODE_DAEMON)) { |
2582 | | /* daemon mode re-executing, stdin/stdout/stderr are already closed so keep quiet */ |
2583 | 0 | global.mode &= ~MODE_VERBOSE; |
2584 | 0 | global.mode |= MODE_QUIET; /* ensure that we won't say anything from now */ |
2585 | 0 | } |
2586 | 0 | } else { |
2587 | 0 | if ((global.mode & MODE_QUIET) && !(global.mode & MODE_VERBOSE)) { |
2588 | | /* detach from the tty */ |
2589 | 0 | stdio_quiet(devnullfd); |
2590 | 0 | } |
2591 | 0 | } |
2592 | | |
2593 | | /* Note that any error at this stage will be fatal because we will not |
2594 | | * be able to restart the old pids. |
2595 | | */ |
2596 | | |
2597 | | /* check current nofile limit reported via getrlimit() and check if we |
2598 | | * can preallocate FDs, if global.prealloc_fd is set. |
2599 | | */ |
2600 | 0 | check_nofile_lim_and_prealloc_fd(); |
2601 | | |
2602 | | /* update the ready date a last time to also account for final setup time */ |
2603 | 0 | clock_update_date(0, 1); |
2604 | 0 | clock_adjust_now_offset(); |
2605 | 0 | ready_date = date; |
2606 | 0 | } |
2607 | | |
2608 | | /* This function sets verbosity modes. Should be called after the first |
2609 | | * configuration read in order that in master-worker mode, both master and |
2610 | | * worker have the same verbosiness. |
2611 | | */ |
2612 | 0 | static void set_verbosity(void) { |
2613 | |
|
2614 | 0 | if (arg_mode & (MODE_DEBUG | MODE_FOREGROUND)) { |
2615 | | /* command line debug mode inhibits configuration mode */ |
2616 | 0 | global.mode &= ~(MODE_DAEMON | MODE_QUIET); |
2617 | 0 | global.mode |= (arg_mode & (MODE_DEBUG | MODE_FOREGROUND)); |
2618 | 0 | } |
2619 | |
|
2620 | 0 | if (arg_mode & MODE_DAEMON) { |
2621 | | /* command line daemon mode inhibits foreground and debug modes mode */ |
2622 | 0 | global.mode &= ~(MODE_DEBUG | MODE_FOREGROUND); |
2623 | 0 | global.mode |= arg_mode & MODE_DAEMON; |
2624 | 0 | } |
2625 | |
|
2626 | 0 | global.mode |= (arg_mode & (MODE_QUIET | MODE_VERBOSE)); |
2627 | |
|
2628 | 0 | if ((global.mode & MODE_DEBUG) && (global.mode & (MODE_DAEMON | MODE_QUIET))) { |
2629 | 0 | ha_warning("<debug> mode incompatible with <quiet> and <daemon>. Keeping <debug> only.\n"); |
2630 | 0 | global.mode &= ~(MODE_DAEMON | MODE_QUIET); |
2631 | 0 | } |
2632 | 0 | } |
2633 | | |
2634 | | static void run_master_in_recovery_mode(int argc, char **argv) |
2635 | 0 | { |
2636 | 0 | struct mworker_proc *proc; |
2637 | 0 | char *errmsg = NULL; |
2638 | | |
2639 | | /* load_status is global and checked in cli_io_handler_show_cli_sock() to |
2640 | | * dump master startup logs with its alerts/warnings via master CLI sock. |
2641 | | */ |
2642 | 0 | load_status = 0; |
2643 | | |
2644 | | /* increment the number failed reloads */ |
2645 | 0 | list_for_each_entry(proc, &proc_list, list) { |
2646 | 0 | proc->failedreloads++; |
2647 | 0 | } |
2648 | | /* the sd_notify API is not able to send a reload failure signal. So |
2649 | | * the READY=1 signal still need to be sent */ |
2650 | 0 | if (global.tune.options & GTUNE_USE_SYSTEMD) |
2651 | 0 | sd_notify(0, "READY=1\nSTATUS=Reload failed (master failed to load or to parse new configuration)!\n"); |
2652 | |
|
2653 | 0 | global.nbtgroups = 1; |
2654 | 0 | global.nbthread = 1; |
2655 | 0 | master = 1; |
2656 | 0 | atexit(exit_on_failure); |
2657 | 0 | set_verbosity(); |
2658 | | |
2659 | | /* creates MASTER proxy */ |
2660 | 0 | if (mworker_cli_create_master_proxy(&errmsg) < 0) { |
2661 | 0 | ha_alert("Can't create MASTER proxy: %s\n", errmsg); |
2662 | 0 | free(errmsg); |
2663 | 0 | exit(EXIT_FAILURE); |
2664 | 0 | } |
2665 | | |
2666 | | /* attaches servers to all existed workers on its shared MCLI sockpair ends, ipc_fd[0] */ |
2667 | 0 | if (mworker_cli_attach_server(&errmsg) < 0) { |
2668 | 0 | ha_alert("Can't attach servers needed for master CLI %s\n", errmsg ? errmsg : ""); |
2669 | 0 | free(errmsg); |
2670 | 0 | exit(EXIT_FAILURE); |
2671 | 0 | } |
2672 | | |
2673 | | /* master CLI */ |
2674 | 0 | mworker_create_master_cli(); |
2675 | 0 | step_init_2(argc, argv); |
2676 | 0 | step_init_3(); |
2677 | 0 | if (protocol_bind_all(1) != 0) { |
2678 | 0 | ha_alert("Master failed to bind master CLI socket.\n"); |
2679 | 0 | exit(1); |
2680 | 0 | } |
2681 | | |
2682 | 0 | step_init_4(); |
2683 | | |
2684 | | /* set quiet mode if MODE_DAEMON */ |
2685 | 0 | if ((!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) && |
2686 | 0 | (global.mode & MODE_DAEMON)) { |
2687 | | /* detach from the tty, this is required to properly daemonize. */ |
2688 | 0 | if ((getenv("HAPROXY_MWORKER_REEXEC") == NULL)) |
2689 | 0 | stdio_quiet(devnullfd); |
2690 | 0 | global.mode &= ~MODE_VERBOSE; |
2691 | 0 | global.mode |= MODE_QUIET; /* ensure that we won't say anything from now */ |
2692 | 0 | } |
2693 | |
|
2694 | 0 | mworker_unblock_signals(); |
2695 | | /* enter in master polling loop */ |
2696 | 0 | mworker_run_master(); |
2697 | 0 | } |
2698 | | |
2699 | | /* parse conf in discovery mode and set modes from config */ |
2700 | | static void read_cfg_in_discovery_mode(int argc, char **argv) |
2701 | 0 | { |
2702 | 0 | struct cfgfile *cfg, *cfg_tmp; |
2703 | | |
2704 | | /* load configs in memory and parse only global section (MODE_DISCOVERY) */ |
2705 | 0 | global.mode |= MODE_DISCOVERY; |
2706 | |
|
2707 | 0 | usermsgs_clr("config"); |
2708 | 0 | if (load_cfg() < 0) { |
2709 | 0 | if (getenv("HAPROXY_MWORKER_REEXEC") != NULL) { |
2710 | 0 | ha_warning("Master failed to load new configuration and " |
2711 | 0 | "can't start a new worker. Already running worker " |
2712 | 0 | "will be kept. Please, check configuration file path " |
2713 | 0 | "and memory limits and reload %s.\n", progname); |
2714 | | /* failed to load new conf, so setup master CLI for master side, |
2715 | | * do some init steps and just enter in mworker_loop |
2716 | | * to monitor the existed worker from previous start |
2717 | | */ |
2718 | 0 | run_master_in_recovery_mode(argc, argv); |
2719 | | /* never get there */ |
2720 | 0 | } else |
2721 | 0 | exit(1); |
2722 | 0 | } |
2723 | | |
2724 | 0 | if (read_cfg() < 0) { |
2725 | 0 | list_for_each_entry_safe(cfg, cfg_tmp, &cfg_cfgfiles, list) { |
2726 | 0 | ha_free(&cfg->content); |
2727 | 0 | ha_free(&cfg->filename); |
2728 | 0 | } |
2729 | 0 | if (getenv("HAPROXY_MWORKER_REEXEC") != NULL) { |
2730 | 0 | ha_warning("Master failed to parse new configuration and " |
2731 | 0 | "can't start a new worker. Already running worker " |
2732 | 0 | "will be kept. Please, check global section settings " |
2733 | 0 | "and memory limits and reload %s.\n", progname); |
2734 | | /* failed to load new conf, so setup master CLI for master side, |
2735 | | * do some init steps and just enter in mworker_loop |
2736 | | * to monitor the existed worker from previous start |
2737 | | */ |
2738 | 0 | run_master_in_recovery_mode(argc, argv); |
2739 | | /* never get there */ |
2740 | 0 | } else |
2741 | 0 | exit(1); |
2742 | 0 | } |
2743 | 0 | usermsgs_clr(NULL); |
2744 | |
|
2745 | 0 | global.mode &= ~MODE_DISCOVERY; |
2746 | |
|
2747 | 0 | if (!LIST_ISEMPTY(&mworker_cli_conf) && !(arg_mode & MODE_MWORKER)) { |
2748 | 0 | ha_alert("a master CLI socket was defined, but master-worker mode (-W) is not enabled.\n"); |
2749 | 0 | exit(EXIT_FAILURE); |
2750 | 0 | } |
2751 | | |
2752 | | /* in MODE_CHECK and in MODE_DUMP_CFG we just need to parse the |
2753 | | * configuration and exit, see step_init_2() |
2754 | | */ |
2755 | 0 | if ((global.mode & MODE_MWORKER) && (global.mode & (MODE_CHECK | MODE_DUMP_CFG))) |
2756 | 0 | global.mode &= ~MODE_MWORKER; |
2757 | 0 | } |
2758 | | |
2759 | | void deinit(void) |
2760 | 0 | { |
2761 | 0 | struct proxy *p = proxies_list, *p0; |
2762 | 0 | struct cfgfile *cfg, *cfg_tmp; |
2763 | 0 | struct logger *log, *logb; |
2764 | 0 | struct build_opts_str *bol, *bolb; |
2765 | 0 | struct post_deinit_fct *pdf, *pdfb; |
2766 | 0 | struct proxy_deinit_fct *pxdf, *pxdfb; |
2767 | 0 | struct server_deinit_fct *srvdf, *srvdfb; |
2768 | 0 | struct per_thread_init_fct *tif, *tifb; |
2769 | 0 | struct per_thread_deinit_fct *tdf, *tdfb; |
2770 | 0 | struct per_thread_alloc_fct *taf, *tafb; |
2771 | 0 | struct per_thread_free_fct *tff, *tffb; |
2772 | 0 | struct post_server_check_fct *pscf, *pscfb; |
2773 | 0 | struct post_check_fct *pcf, *pcfb; |
2774 | 0 | struct post_proxy_check_fct *ppcf, *ppcfb; |
2775 | 0 | struct pre_check_fct *prcf, *prcfb; |
2776 | 0 | struct cfg_postparser *pprs, *pprsb; |
2777 | 0 | char **tmp = init_env; |
2778 | 0 | int cur_fd; |
2779 | | |
2780 | | /* the user may want to skip this phase */ |
2781 | 0 | if (global.tune.options & GTUNE_QUICK_EXIT) |
2782 | 0 | return; |
2783 | | |
2784 | | /* At this point the listeners state is weird: |
2785 | | * - most listeners are still bound and referenced in their protocol |
2786 | | * - some might be zombies that are not in their proto anymore, but |
2787 | | * still appear in their proxy's listeners with a valid FD. |
2788 | | * - some might be stopped and still appear in their proxy as FD #-1 |
2789 | | * - among all of them, some might be inherited hence shared and we're |
2790 | | * not allowed to pause them or whatever, we must just close them. |
2791 | | * - finally some are not listeners (pipes, logs, stdout, etc) and |
2792 | | * must be left intact. |
2793 | | * |
2794 | | * The safe way to proceed is to unbind (and close) whatever is not yet |
2795 | | * unbound so that no more receiver/listener remains alive. Then close |
2796 | | * remaining listener FDs, which correspond to zombie listeners (those |
2797 | | * belonging to disabled proxies that were in another process). |
2798 | | * objt_listener() would be cleaner here but not converted yet. |
2799 | | */ |
2800 | 0 | protocol_unbind_all(); |
2801 | |
|
2802 | 0 | for (cur_fd = 0; cur_fd < global.maxsock; cur_fd++) { |
2803 | 0 | if (!fdtab || !fdtab[cur_fd].owner) |
2804 | 0 | continue; |
2805 | | |
2806 | 0 | if (fdtab[cur_fd].iocb == &sock_accept_iocb) { |
2807 | 0 | struct listener *l = fdtab[cur_fd].owner; |
2808 | |
|
2809 | 0 | BUG_ON(l->state != LI_INIT); |
2810 | 0 | unbind_listener(l); |
2811 | 0 | } |
2812 | 0 | } |
2813 | | |
2814 | 0 | deinit_signals(); |
2815 | 0 | while (p) { |
2816 | 0 | p0 = p; |
2817 | 0 | p = p->next; |
2818 | 0 | free_proxy(p0); |
2819 | 0 | }/* end while(p) */ |
2820 | | |
2821 | | /* we don't need to free sink_proxies_list nor cfg_log_forward proxies since |
2822 | | * they are respectively cleaned up in sink_deinit() and deinit_log_forward() |
2823 | | */ |
2824 | | |
2825 | | /* If named defaults were preserved, ensure refcount is resetted. */ |
2826 | 0 | if (!(global.tune.options & GTUNE_PURGE_DEFAULTS)) |
2827 | 0 | defaults_px_unref_all(); |
2828 | | /* All proxies are removed now, so every defaults should also be freed |
2829 | | * when their refcount reached zero. |
2830 | | */ |
2831 | 0 | BUG_ON(!LIST_ISEMPTY(&defaults_list)); |
2832 | |
|
2833 | 0 | userlist_free(userlist); |
2834 | |
|
2835 | 0 | cfg_unregister_sections(); |
2836 | |
|
2837 | 0 | deinit_log_buffers(); |
2838 | |
|
2839 | 0 | list_for_each_entry(pdf, &post_deinit_list, list) |
2840 | 0 | pdf->fct(); |
2841 | |
|
2842 | 0 | ha_free(&global.log_send_hostname); |
2843 | 0 | chunk_destroy(&global.log_tag); |
2844 | 0 | ha_free(&global.chroot); |
2845 | 0 | ha_free(&global.pidfile); |
2846 | 0 | ha_free(&global.node); |
2847 | 0 | ha_free(&global.desc); |
2848 | 0 | ha_free(&oldpids); |
2849 | 0 | ha_free(&old_argv); |
2850 | 0 | ha_free(&localpeer); |
2851 | 0 | ha_free(&global.server_state_base); |
2852 | 0 | ha_free(&global.server_state_file); |
2853 | 0 | ha_free(&global.stats_file); |
2854 | 0 | task_destroy(idle_conn_task); |
2855 | 0 | idle_conn_task = NULL; |
2856 | |
|
2857 | 0 | list_for_each_entry_safe(log, logb, &global.loggers, list) { |
2858 | 0 | LIST_DEL_INIT(&log->list); |
2859 | 0 | free_logger(log); |
2860 | 0 | } |
2861 | |
|
2862 | 0 | list_for_each_entry_safe(cfg, cfg_tmp, &cfg_cfgfiles, list) { |
2863 | 0 | ha_free(&cfg->filename); |
2864 | 0 | LIST_DELETE(&cfg->list); |
2865 | 0 | ha_free(&cfg); |
2866 | 0 | } |
2867 | |
|
2868 | 0 | list_for_each_entry_safe(bol, bolb, &build_opts_list, list) { |
2869 | 0 | if (bol->must_free) |
2870 | 0 | free((void *)bol->str); |
2871 | 0 | LIST_DELETE(&bol->list); |
2872 | 0 | free(bol); |
2873 | 0 | } |
2874 | |
|
2875 | 0 | list_for_each_entry_safe(pxdf, pxdfb, &proxy_deinit_list, list) { |
2876 | 0 | LIST_DELETE(&pxdf->list); |
2877 | 0 | free(pxdf); |
2878 | 0 | } |
2879 | |
|
2880 | 0 | list_for_each_entry_safe(pdf, pdfb, &post_deinit_list, list) { |
2881 | 0 | LIST_DELETE(&pdf->list); |
2882 | 0 | free(pdf); |
2883 | 0 | } |
2884 | |
|
2885 | 0 | list_for_each_entry_safe(srvdf, srvdfb, &server_deinit_list, list) { |
2886 | 0 | LIST_DELETE(&srvdf->list); |
2887 | 0 | free(srvdf); |
2888 | 0 | } |
2889 | |
|
2890 | 0 | list_for_each_entry_safe(pcf, pcfb, &post_check_list, list) { |
2891 | 0 | LIST_DELETE(&pcf->list); |
2892 | 0 | free(pcf); |
2893 | 0 | } |
2894 | |
|
2895 | 0 | list_for_each_entry_safe(pscf, pscfb, &post_server_check_list, list) { |
2896 | 0 | LIST_DELETE(&pscf->list); |
2897 | 0 | free(pscf); |
2898 | 0 | } |
2899 | |
|
2900 | 0 | list_for_each_entry_safe(ppcf, ppcfb, &post_proxy_check_list, list) { |
2901 | 0 | LIST_DELETE(&ppcf->list); |
2902 | 0 | free(ppcf); |
2903 | 0 | } |
2904 | |
|
2905 | 0 | list_for_each_entry_safe(prcf, prcfb, &pre_check_list, list) { |
2906 | 0 | LIST_DELETE(&prcf->list); |
2907 | 0 | free(prcf); |
2908 | 0 | } |
2909 | |
|
2910 | 0 | list_for_each_entry_safe(tif, tifb, &per_thread_init_list, list) { |
2911 | 0 | LIST_DELETE(&tif->list); |
2912 | 0 | free(tif); |
2913 | 0 | } |
2914 | |
|
2915 | 0 | list_for_each_entry_safe(tdf, tdfb, &per_thread_deinit_list, list) { |
2916 | 0 | LIST_DELETE(&tdf->list); |
2917 | 0 | free(tdf); |
2918 | 0 | } |
2919 | |
|
2920 | 0 | list_for_each_entry_safe(taf, tafb, &per_thread_alloc_list, list) { |
2921 | 0 | LIST_DELETE(&taf->list); |
2922 | 0 | free(taf); |
2923 | 0 | } |
2924 | |
|
2925 | 0 | list_for_each_entry_safe(tff, tffb, &per_thread_free_list, list) { |
2926 | 0 | LIST_DELETE(&tff->list); |
2927 | 0 | free(tff); |
2928 | 0 | } |
2929 | |
|
2930 | 0 | list_for_each_entry_safe(pprs, pprsb, &postparsers, list) { |
2931 | 0 | LIST_DELETE(&pprs->list); |
2932 | 0 | free(pprs); |
2933 | 0 | } |
2934 | |
|
2935 | 0 | vars_prune(&proc_vars, NULL, NULL); |
2936 | 0 | free_all_file_names(); |
2937 | 0 | pool_destroy_all(); |
2938 | 0 | deinit_pollers(); |
2939 | | |
2940 | | /* free env variables backup */ |
2941 | 0 | if (init_env) { |
2942 | 0 | while (*tmp) { |
2943 | 0 | free(*tmp); |
2944 | 0 | tmp++; |
2945 | 0 | } |
2946 | 0 | free(init_env); |
2947 | 0 | } |
2948 | 0 | free(progname); |
2949 | |
|
2950 | 0 | } /* end deinit() */ |
2951 | | |
2952 | | __attribute__((noreturn)) void deinit_and_exit(int status) |
2953 | 0 | { |
2954 | 0 | global.mode |= MODE_STOPPING; |
2955 | 0 | deinit(); |
2956 | 0 | exit(status); |
2957 | 0 | } |
2958 | | |
2959 | | /* Runs the polling loop */ |
2960 | | void run_poll_loop() |
2961 | 0 | { |
2962 | 0 | int next, wake; |
2963 | |
|
2964 | 0 | _HA_ATOMIC_OR(&th_ctx->flags, TH_FL_IN_LOOP); |
2965 | |
|
2966 | 0 | clock_update_date(0,1); |
2967 | 0 | while (1) { |
2968 | 0 | wake_expired_tasks(); |
2969 | | |
2970 | | /* check if we caught some signals and process them in the |
2971 | | first thread */ |
2972 | 0 | if (signal_queue_len && tid == 0) { |
2973 | 0 | activity[tid].wake_signal++; |
2974 | 0 | signal_process_queue(); |
2975 | 0 | } |
2976 | | |
2977 | | /* Process a few tasks */ |
2978 | 0 | process_runnable_tasks(); |
2979 | | |
2980 | | /* also stop if we failed to cleanly stop all tasks */ |
2981 | 0 | if (killed > 1) |
2982 | 0 | break; |
2983 | | |
2984 | | /* expire immediately if events or signals are pending */ |
2985 | 0 | wake = 1; |
2986 | 0 | if (thread_has_tasks()) |
2987 | 0 | activity[tid].wake_tasks++; |
2988 | 0 | else { |
2989 | 0 | unsigned int flags = _HA_ATOMIC_LOAD(&th_ctx->flags); |
2990 | |
|
2991 | 0 | while (unlikely(!HA_ATOMIC_CAS(&th_ctx->flags, &flags, (flags | TH_FL_SLEEPING) & ~TH_FL_NOTIFIED))) |
2992 | 0 | __ha_cpu_relax(); |
2993 | |
|
2994 | 0 | if (thread_has_tasks()) { |
2995 | 0 | activity[tid].wake_tasks++; |
2996 | 0 | _HA_ATOMIC_AND(&th_ctx->flags, ~TH_FL_SLEEPING); |
2997 | 0 | } else if (signal_queue_len && tid == 0) { |
2998 | | /* this check is required after setting TH_FL_SLEEPING to avoid |
2999 | | * a race with wakeup on signals using wake_threads() */ |
3000 | 0 | _HA_ATOMIC_AND(&th_ctx->flags, ~TH_FL_SLEEPING); |
3001 | 0 | } else |
3002 | 0 | wake = 0; |
3003 | 0 | } |
3004 | | |
3005 | | /* Note below: threads only check the quit condition when idle, |
3006 | | * but for tid>0 we also need to skip that if the signal queue |
3007 | | * is non-empty otherwise we risk quitting too early. |
3008 | | */ |
3009 | 0 | if (!wake && !signal_queue_len) { |
3010 | 0 | int i; |
3011 | |
|
3012 | 0 | if (stopping) { |
3013 | 0 | int old_detected; |
3014 | | |
3015 | | /* stop muxes/quic-conns before acknowledging stopping */ |
3016 | 0 | if (!(tg_ctx->stopping_threads & ti->ltid_bit)) { |
3017 | 0 | task_wakeup(mux_stopping_data[tid].task, TASK_WOKEN_OTHER); |
3018 | 0 | wake = 1; |
3019 | 0 | } |
3020 | |
|
3021 | 0 | old_detected = stop_detected; |
3022 | | |
3023 | | /* |
3024 | | * Check if ze're the first to detect the |
3025 | | * stop |
3026 | | */ |
3027 | 0 | while (old_detected == 0 && |
3028 | 0 | !_HA_ATOMIC_CAS(&stop_detected, &old_detected, 1)); |
3029 | |
|
3030 | 0 | if (old_detected == 0) { |
3031 | | /* first one to detect it, notify all threads that stopping was just set */ |
3032 | 0 | for (i = 0; i < global.nbthread; i++) { |
3033 | 0 | if (_HA_ATOMIC_LOAD(&ha_thread_info[i].tg->threads_enabled) & |
3034 | 0 | ha_thread_info[i].ltid_bit & |
3035 | 0 | ~_HA_ATOMIC_LOAD(&ha_thread_info[i].tg_ctx->stopping_threads)) |
3036 | 0 | wake_thread(i); |
3037 | 0 | } |
3038 | 0 | } |
3039 | 0 | if (!(tg_ctx->stopping_threads & ti->ltid_bit) && |
3040 | 0 | _HA_ATOMIC_OR_FETCH(&tg_ctx->stopping_threads, |
3041 | 0 | ti->ltid_bit) == tg->threads_enabled) { |
3042 | | /* |
3043 | | * All threads from the thread group |
3044 | | * are stopped, let it been known. |
3045 | | */ |
3046 | 0 | _HA_ATOMIC_INC(&stopped_tgroups); |
3047 | 0 | } |
3048 | 0 | } |
3049 | | |
3050 | | /* stop when there's nothing left to do */ |
3051 | 0 | if ((jobs - unstoppable_jobs) == 0 && |
3052 | 0 | (_HA_ATOMIC_LOAD(&stopped_tgroups) == global.nbtgroups)) { |
3053 | | #ifdef USE_THREAD |
3054 | | for (i = 0; i < global.nbthread; i++) |
3055 | | if (i != tid && _HA_ATOMIC_LOAD(&ha_thread_info[i].tg->threads_enabled) & ha_thread_info[i].ltid_bit) |
3056 | | wake_thread(i); |
3057 | | #endif |
3058 | 0 | break; |
3059 | 0 | } |
3060 | 0 | } |
3061 | | |
3062 | | /* If we have to sleep, measure how long */ |
3063 | 0 | next = wake ? TICK_ETERNITY : next_timer_expiry(); |
3064 | | |
3065 | | /* The poller will ensure it returns around <next> */ |
3066 | 0 | cur_poller.poll(&cur_poller, next, wake); |
3067 | |
|
3068 | 0 | activity[tid].loops++; |
3069 | 0 | } |
3070 | |
|
3071 | 0 | _HA_ATOMIC_AND(&th_ctx->flags, ~TH_FL_IN_LOOP); |
3072 | 0 | } |
3073 | | |
3074 | | void *run_thread_poll_loop(void *data) |
3075 | 0 | { |
3076 | 0 | struct per_thread_alloc_fct *ptaf; |
3077 | 0 | struct per_thread_init_fct *ptif; |
3078 | 0 | struct per_thread_deinit_fct *ptdf; |
3079 | 0 | struct per_thread_free_fct *ptff; |
3080 | 0 | static int init_left = 0; |
3081 | 0 | __decl_thread(static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER); |
3082 | 0 | __decl_thread(static pthread_cond_t init_cond = PTHREAD_COND_INITIALIZER); |
3083 | |
|
3084 | 0 | ha_set_thread(data); |
3085 | 0 | set_thread_cpu_affinity(); |
3086 | 0 | clock_set_local_source(); |
3087 | |
|
3088 | | #ifdef USE_THREAD |
3089 | | ha_thread_info[tid].pth_id = ha_get_pthread_id(tid); |
3090 | | #endif |
3091 | 0 | ha_thread_info[tid].stack_top = __builtin_frame_address(0); |
3092 | | |
3093 | | /* Assign the ring queue. Contrary to an intuitive thought, this does |
3094 | | * not benefit from locality and it's counter-productive to group |
3095 | | * threads from a same group or range number in the same queue. In some |
3096 | | * sense it arranges us because it means we can use a modulo and ensure |
3097 | | * that even small numbers of threads are well spread. |
3098 | | */ |
3099 | 0 | ha_thread_info[tid].ring_queue = |
3100 | 0 | (tid % MIN(global.nbthread, |
3101 | 0 | (global.tune.ring_queues ? |
3102 | 0 | global.tune.ring_queues : |
3103 | 0 | RING_DFLT_QUEUES))) % RING_WAIT_QUEUES; |
3104 | | |
3105 | | /* thread is started, from now on it is not idle nor harmless */ |
3106 | 0 | thread_harmless_end(); |
3107 | 0 | thread_idle_end(); |
3108 | 0 | _HA_ATOMIC_OR(&th_ctx->flags, TH_FL_STARTED); |
3109 | | |
3110 | | /* Now, initialize one thread init at a time. This is better since |
3111 | | * some init code is a bit tricky and may release global resources |
3112 | | * after reallocating them locally. This will also ensure there is |
3113 | | * no race on file descriptors allocation. |
3114 | | */ |
3115 | | #ifdef USE_THREAD |
3116 | | pthread_mutex_lock(&init_mutex); |
3117 | | #endif |
3118 | | /* The first thread must set the number of threads left */ |
3119 | 0 | if (!init_left) |
3120 | 0 | init_left = global.nbthread; |
3121 | 0 | init_left--; |
3122 | |
|
3123 | 0 | clock_init_thread_date(); |
3124 | | |
3125 | | /* per-thread alloc calls performed here are not allowed to snoop on |
3126 | | * other threads, so they are free to initialize at their own rhythm |
3127 | | * as long as they act as if they were alone. None of them may rely |
3128 | | * on resources initialized by the other ones. |
3129 | | */ |
3130 | 0 | list_for_each_entry(ptaf, &per_thread_alloc_list, list) { |
3131 | 0 | if (!ptaf->fct()) { |
3132 | 0 | ha_alert("failed to allocate resources for thread %u.\n", tid); |
3133 | | #ifdef USE_THREAD |
3134 | | pthread_mutex_unlock(&init_mutex); |
3135 | | #endif |
3136 | 0 | exit(1); |
3137 | 0 | } |
3138 | 0 | } |
3139 | | |
3140 | | /* per-thread init calls performed here are not allowed to snoop on |
3141 | | * other threads, so they are free to initialize at their own rhythm |
3142 | | * as long as they act as if they were alone. |
3143 | | */ |
3144 | 0 | list_for_each_entry(ptif, &per_thread_init_list, list) { |
3145 | 0 | if (!ptif->fct()) { |
3146 | 0 | ha_alert("failed to initialize thread %u.\n", tid); |
3147 | | #ifdef USE_THREAD |
3148 | | pthread_mutex_unlock(&init_mutex); |
3149 | | #endif |
3150 | 0 | exit(1); |
3151 | 0 | } |
3152 | 0 | } |
3153 | | |
3154 | | /* enabling protocols will result in fd_insert() calls to be performed, |
3155 | | * we want all threads to have already allocated their local fd tables |
3156 | | * before doing so, thus only the last thread does it. |
3157 | | */ |
3158 | 0 | if (init_left == 0) |
3159 | 0 | protocol_enable_all(); |
3160 | |
|
3161 | | #ifdef USE_THREAD |
3162 | | pthread_cond_broadcast(&init_cond); |
3163 | | pthread_mutex_unlock(&init_mutex); |
3164 | | |
3165 | | /* now wait for other threads to finish starting */ |
3166 | | pthread_mutex_lock(&init_mutex); |
3167 | | while (init_left) |
3168 | | pthread_cond_wait(&init_cond, &init_mutex); |
3169 | | pthread_mutex_unlock(&init_mutex); |
3170 | | #endif |
3171 | |
|
3172 | | #if defined(PR_SET_NO_NEW_PRIVS) && defined(USE_PRCTL) |
3173 | | /* Let's refrain from using setuid executables. This way the impact of |
3174 | | * an eventual vulnerability in a library remains limited. It may |
3175 | | * impact external checks but who cares about them anyway ? In the |
3176 | | * worst case it's possible to disable the option. Obviously we do this |
3177 | | * in workers only. We can't hard-fail on this one as it really is |
3178 | | * implementation dependent though we're interested in feedback, hence |
3179 | | * the warning. |
3180 | | */ |
3181 | | if (!(global.tune.options & GTUNE_INSECURE_SETUID) && !master) { |
3182 | | static int warn_fail; |
3183 | | if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1 && !_HA_ATOMIC_FETCH_ADD(&warn_fail, 1)) { |
3184 | | ha_warning("Failed to disable setuid, please report to developers with detailed " |
3185 | | "information about your operating system. You can silence this warning " |
3186 | | "by adding 'insecure-setuid-wanted' in the 'global' section.\n"); |
3187 | | } |
3188 | | } |
3189 | | #endif |
3190 | |
|
3191 | 0 | #if defined(RLIMIT_NPROC) |
3192 | | /* all threads have started, it's now time to prevent any new thread |
3193 | | * or process from starting. Obviously we do this in workers only. We |
3194 | | * can't hard-fail on this one as it really is implementation dependent |
3195 | | * though we're interested in feedback, hence the warning. |
3196 | | */ |
3197 | 0 | if (!(global.tune.options & GTUNE_INSECURE_FORK) && !master) { |
3198 | 0 | struct rlimit limit = { .rlim_cur = 0, .rlim_max = 0 }; |
3199 | 0 | static int warn_fail; |
3200 | |
|
3201 | 0 | if (setrlimit(RLIMIT_NPROC, &limit) == -1 && !_HA_ATOMIC_FETCH_ADD(&warn_fail, 1)) { |
3202 | 0 | ha_warning("Failed to disable forks, please report to developers with detailed " |
3203 | 0 | "information about your operating system. You can silence this warning " |
3204 | 0 | "by adding 'insecure-fork-wanted' in the 'global' section.\n"); |
3205 | 0 | } |
3206 | 0 | } |
3207 | 0 | #endif |
3208 | 0 | run_poll_loop(); |
3209 | |
|
3210 | 0 | list_for_each_entry(ptdf, &per_thread_deinit_list, list) |
3211 | 0 | ptdf->fct(); |
3212 | |
|
3213 | 0 | list_for_each_entry(ptff, &per_thread_free_list, list) |
3214 | 0 | ptff->fct(); |
3215 | |
|
3216 | | #ifdef USE_THREAD |
3217 | | _HA_ATOMIC_AND(&ha_tgroup_info[ti->tgid-1].threads_enabled, ~ti->ltid_bit); |
3218 | | _HA_ATOMIC_AND_FETCH(&tg_ctx->stopping_threads, ~ti->ltid_bit); |
3219 | | if (tid > 0) |
3220 | | pthread_exit(NULL); |
3221 | | #endif |
3222 | 0 | return NULL; |
3223 | 0 | } |
3224 | | |
3225 | | /* set uid/gid depending on global settings */ |
3226 | | static void set_identity(const char *program_name) |
3227 | 0 | { |
3228 | 0 | int from_uid __maybe_unused = geteuid(); |
3229 | |
|
3230 | 0 | if (global.gid > 0) { |
3231 | 0 | if (getgroups(0, NULL) > 0 && setgroups(0, NULL) == -1) |
3232 | 0 | ha_warning("[%s.main()] Failed to drop supplementary groups. Using 'gid'/'group'" |
3233 | 0 | " without 'uid'/'user' is generally useless.\n", program_name); |
3234 | |
|
3235 | 0 | if (setgid(global.gid) == -1) { |
3236 | 0 | ha_alert("[%s.main()] Cannot set gid %d.\n", program_name, global.gid); |
3237 | 0 | protocol_unbind_all(); |
3238 | 0 | exit(1); |
3239 | 0 | } |
3240 | 0 | } |
3241 | | |
3242 | | #if defined(USE_LINUX_CAP) |
3243 | | if (prepare_caps_for_setuid(from_uid, global.uid) < 0) { |
3244 | | ha_alert("[%s.main()] Cannot switch uid to %d.\n", program_name, global.uid); |
3245 | | protocol_unbind_all(); |
3246 | | exit(1); |
3247 | | } |
3248 | | #endif |
3249 | | |
3250 | 0 | if (global.uid > 0 && setuid(global.uid) == -1) { |
3251 | 0 | ha_alert("[%s.main()] Cannot set uid %d.\n", program_name, global.uid); |
3252 | 0 | protocol_unbind_all(); |
3253 | 0 | exit(1); |
3254 | 0 | } |
3255 | |
|
3256 | | #if defined(USE_LINUX_CAP) |
3257 | | if (finalize_caps_after_setuid(from_uid, global.uid) < 0) { |
3258 | | ha_alert("[%s.main()] Cannot switch uid to %d.\n", program_name, global.uid); |
3259 | | protocol_unbind_all(); |
3260 | | exit(1); |
3261 | | } |
3262 | | #endif |
3263 | 0 | } |
3264 | | |
3265 | | int main2(int argc, char **argv) |
3266 | 0 | { |
3267 | 0 | struct rlimit limit; |
3268 | 0 | int intovf = (unsigned char)argc + 1; /* let the compiler know it's strictly positive */ |
3269 | 0 | struct cfgfile *cfg, *cfg_tmp; |
3270 | 0 | struct ring *tmp_startup_logs = NULL; |
3271 | 0 | struct mworker_proc *proc; |
3272 | | |
3273 | | /* Catch broken toolchains */ |
3274 | 0 | if (sizeof(long) != sizeof(void *) || (intovf + 0x7FFFFFFF >= intovf)) { |
3275 | 0 | const char *msg; |
3276 | |
|
3277 | 0 | if (sizeof(long) != sizeof(void *)) |
3278 | | /* Apparently MingW64 was not made for us and can also break openssl */ |
3279 | 0 | msg = "The compiler this program was built with uses unsupported integral type sizes.\n" |
3280 | 0 | "Most likely it follows the unsupported LLP64 model. Never try to link HAProxy\n" |
3281 | 0 | "against libraries built with that compiler either! Please only use a compiler\n" |
3282 | 0 | "producing ILP32 or LP64 programs for both programs and libraries.\n"; |
3283 | 0 | else if (intovf + 0x7FFFFFFF >= intovf) |
3284 | | /* Catch forced CFLAGS that miss 2-complement integer overflow */ |
3285 | 0 | msg = "The source code was miscompiled by the compiler, which usually indicates that\n" |
3286 | 0 | "some of the CFLAGS needed to work around overzealous compiler optimizations\n" |
3287 | 0 | "were overwritten at build time. Please do not force CFLAGS, and read Makefile\n" |
3288 | 0 | "and INSTALL files to decide on the best way to pass your local build options.\n"; |
3289 | 0 | else |
3290 | 0 | msg = "Bug in the compiler bug detection code, please report it to developers!\n"; |
3291 | |
|
3292 | 0 | fprintf(stderr, |
3293 | 0 | "FATAL ERROR: invalid code detected -- cannot go further, please recompile!\n" |
3294 | 0 | "%s" |
3295 | 0 | "\nBuild options :%s" |
3296 | 0 | "\n\n", msg, build_opts_string); |
3297 | |
|
3298 | 0 | return 1; |
3299 | 0 | } |
3300 | | |
3301 | 0 | setvbuf(stdout, NULL, _IONBF, 0); |
3302 | | |
3303 | | /* take a copy of initial limits before we possibly change them */ |
3304 | 0 | getrlimit(RLIMIT_NOFILE, &limit); |
3305 | |
|
3306 | 0 | if (limit.rlim_max == RLIM_INFINITY) |
3307 | 0 | limit.rlim_max = limit.rlim_cur; |
3308 | 0 | rlim_fd_cur_at_boot = limit.rlim_cur; |
3309 | 0 | rlim_fd_max_at_boot = limit.rlim_max; |
3310 | |
|
3311 | | #ifdef USE_OPENSSL |
3312 | | |
3313 | | /* Initialize SSL random generator. Must be called before chroot for |
3314 | | * access to /dev/urandom, and before ha_random_boot() which may use |
3315 | | * RAND_bytes(). |
3316 | | */ |
3317 | | if (!ssl_initialize_random()) { |
3318 | | ha_alert("OpenSSL random data generator initialization failed.\n"); |
3319 | | exit(EXIT_FAILURE); |
3320 | | } |
3321 | | #endif |
3322 | 0 | ha_random_boot(argv); // the argv pointer brings some kernel-fed entropy |
3323 | | |
3324 | | /* process all initcalls in order of potential dependency */ |
3325 | 0 | RUN_INITCALLS(STG_PREPARE); |
3326 | 0 | RUN_INITCALLS(STG_LOCK); |
3327 | 0 | RUN_INITCALLS(STG_REGISTER); |
3328 | | |
3329 | | /* now's time to initialize early boot variables */ |
3330 | 0 | init_early(argc, argv); |
3331 | | |
3332 | | /* handles argument parsing */ |
3333 | 0 | init_args(argc, argv); |
3334 | |
|
3335 | 0 | RUN_INITCALLS(STG_ALLOC); |
3336 | 0 | RUN_INITCALLS(STG_POOL); |
3337 | | |
3338 | | /* some code really needs to have the trash properly allocated */ |
3339 | 0 | if (!trash.area) { |
3340 | 0 | ha_alert("failed to initialize trash buffers.\n"); |
3341 | 0 | exit(1); |
3342 | 0 | } |
3343 | | |
3344 | 0 | RUN_INITCALLS(STG_INIT); |
3345 | | |
3346 | | /* Late init step: SSL crypto libs init and check, Lua lib init, ACL init, |
3347 | | * set modes from cmdline and change dir, if this option is provided via |
3348 | | * cmdline. |
3349 | | */ |
3350 | 0 | step_init_1(); |
3351 | | |
3352 | | /* call a function to be called with -U in order to make some tests */ |
3353 | | #ifdef DEBUG_UNIT |
3354 | | if (unittest_argc > -1) { |
3355 | | struct unittest_fct *unit; |
3356 | | int ret = 1; |
3357 | | int argc_start = argc - unittest_argc; |
3358 | | |
3359 | | list_for_each_entry(unit, &unittest_list, list) { |
3360 | | |
3361 | | if (strcmp(unit->name, argv[argc_start]) == 0) { |
3362 | | ret = unit->fct(unittest_argc, argv + argc_start); |
3363 | | break; |
3364 | | } |
3365 | | } |
3366 | | |
3367 | | exit(ret); |
3368 | | } |
3369 | | #endif |
3370 | | |
3371 | | |
3372 | | /* deserialize processes list, if we do reload in master-worker mode */ |
3373 | 0 | if ((getenv("HAPROXY_MWORKER_REEXEC") != NULL)) { |
3374 | 0 | if (mworker_env_to_proc_list() < 0) { |
3375 | 0 | ha_alert("Master failed to deserialize monitored processes list, " |
3376 | 0 | "it's a non-recoverable error, exiting.\n"); |
3377 | 0 | exit(EXIT_FAILURE); |
3378 | 0 | } |
3379 | 0 | } |
3380 | | |
3381 | | /* backup initial process env, because parse_cfg() could modify it with |
3382 | | * setenv/unsetenv/presetenv/resetenv keywords. |
3383 | | */ |
3384 | 0 | if (backup_env() != 0) |
3385 | 0 | exit(EXIT_FAILURE); |
3386 | | |
3387 | 0 | if (!fileless_mode) |
3388 | | /* parse conf in discovery mode and set modes from config */ |
3389 | 0 | read_cfg_in_discovery_mode(argc, argv); |
3390 | 0 | else { |
3391 | 0 | int ret; |
3392 | |
|
3393 | 0 | ret = parse_cfg(&fileless_cfg); |
3394 | 0 | if (ret != 0) |
3395 | 0 | exit(EXIT_FAILURE); |
3396 | 0 | } |
3397 | | |
3398 | | /* From this stage all runtime modes are known. So let's do below some |
3399 | | * preparation steps and then let's apply all discovered modes. |
3400 | | */ |
3401 | 0 | set_verbosity(); |
3402 | | |
3403 | | /* We might need to use this devnullfd during configuration parsing. */ |
3404 | 0 | devnullfd = open("/dev/null", O_RDWR, 0); |
3405 | 0 | if (devnullfd < 0) { |
3406 | 0 | ha_alert("Cannot open /dev/null\n"); |
3407 | 0 | exit(EXIT_FAILURE); |
3408 | 0 | } |
3409 | 0 | if (fcntl(devnullfd, FD_CLOEXEC) != 0) { |
3410 | 0 | ha_alert("Cannot make /dev/null CLOEXEC\n"); |
3411 | 0 | close(devnullfd); |
3412 | 0 | exit(EXIT_FAILURE); |
3413 | 0 | } |
3414 | | |
3415 | | /* Add entries for master and worker in proc_list, create sockpair, |
3416 | | * that will be copied to both processes after master-worker fork to |
3417 | | * enable the master CLI at worker side (worker can send messages to master), |
3418 | | * setenv("HAPROXY_MWORKER", "1", 1). |
3419 | | */ |
3420 | 0 | if (global.mode & MODE_MWORKER) |
3421 | 0 | mworker_prepare_master(); |
3422 | | |
3423 | | /* If we are in a daemon mode and we might be also in master-worker mode: |
3424 | | * we should do daemonization fork here to put the main process (which |
3425 | | * will become then a master) in background, before it will fork a |
3426 | | * worker, because the worker should be also in background for this case. |
3427 | | */ |
3428 | 0 | if ((getenv("HAPROXY_MWORKER_REEXEC") == NULL) && (global.mode & MODE_DAEMON) |
3429 | 0 | && !(global.mode & MODE_CHECK)) |
3430 | 0 | apply_daemon_mode(); |
3431 | | |
3432 | | /* Master-worker and program forks */ |
3433 | 0 | if (global.mode & MODE_MWORKER) { |
3434 | | /* fork worker */ |
3435 | 0 | mworker_apply_master_worker_mode(); |
3436 | 0 | } |
3437 | | |
3438 | | /* Worker, daemon, foreground, configuration with files modes read the rest |
3439 | | * of the config. |
3440 | | */ |
3441 | 0 | if (!master && !fileless_mode) { |
3442 | 0 | usermsgs_clr("config"); |
3443 | 0 | if (global.mode & MODE_MWORKER) { |
3444 | 0 | if (clean_env() != 0) { |
3445 | 0 | ha_alert("Worker failed to clean its env, exiting.\n"); |
3446 | 0 | exit(EXIT_FAILURE); |
3447 | 0 | } |
3448 | | |
3449 | 0 | if (restore_env() != 0) { |
3450 | 0 | ha_alert("Worker failed to restore its env, exiting.\n"); |
3451 | 0 | exit(EXIT_FAILURE); |
3452 | 0 | } |
3453 | 0 | setenv("HAPROXY_MWORKER", "1", 1); |
3454 | 0 | } |
3455 | | |
3456 | | /* localpeer default value could be redefined via 'localpeer' keyword |
3457 | | * from the global section, which has already parsed in MODE_DISCOVERY by |
3458 | | * read_cfg_in_discovery_mode(). So, let's set HAPROXY_LOCALPEER explicitly |
3459 | | * here. |
3460 | | */ |
3461 | 0 | setenv("HAPROXY_LOCALPEER", localpeer, 1); |
3462 | | |
3463 | | /* nbthread and *thread keywords parsers are sensible to global |
3464 | | * section position, it should be placed as the first in |
3465 | | * the configuration, if these keywords are inside. So, let's |
3466 | | * reset non_global_section_parsed counter for the second |
3467 | | * configuration reading |
3468 | | */ |
3469 | 0 | non_global_section_parsed = 0; |
3470 | 0 | if (read_cfg() < 0) { |
3471 | 0 | list_for_each_entry_safe(cfg, cfg_tmp, &cfg_cfgfiles, list) { |
3472 | 0 | ha_free(&cfg->content); |
3473 | 0 | ha_free(&cfg->filename); |
3474 | 0 | } |
3475 | 0 | exit(1); |
3476 | 0 | } |
3477 | | /* all sections have been parsed, we can free the content */ |
3478 | 0 | list_for_each_entry_safe(cfg, cfg_tmp, &cfg_cfgfiles, list) |
3479 | 0 | ha_free(&cfg->content); |
3480 | |
|
3481 | 0 | usermsgs_clr(NULL); |
3482 | 0 | } |
3483 | | |
3484 | | /* Late init step: routines from pre_check_list, functions, which |
3485 | | * allocate pools, initialize proxies, compute ideal maxconn and |
3486 | | * initialize postmortem structure. |
3487 | | */ |
3488 | 0 | step_init_2(argc, argv); |
3489 | |
|
3490 | 0 | RUN_INITCALLS(STG_INIT_2); |
3491 | | /* Late init step: register signals for worker and standalon modes, apply |
3492 | | * nofile and memory limits, apply capabilities from binary, if any. |
3493 | | */ |
3494 | 0 | step_init_3(); |
3495 | | |
3496 | | /* In standalone or in worker mode get the listeners fds from the previous |
3497 | | * process using _getsocks on stat socket or on the master CLI socket |
3498 | | * respectively. |
3499 | | */ |
3500 | 0 | if (!master && old_unixsocket) |
3501 | 0 | get_listeners_fd(); |
3502 | |
|
3503 | 0 | bind_listeners(); |
3504 | | |
3505 | | /* worker context: now listeners fds were transferred from the previous |
3506 | | * worker, all listeners fd are bound. So we can close ipc_fd[0]s of all |
3507 | | * previous workers, which are still referenced in the proc_list, i.e. |
3508 | | * they are not exited yet at the moment, when this current worker was |
3509 | | * forked. Thus the current worker inherits ipc_fd[0]s from the previous |
3510 | | * ones by it's parent, master, because we have to keep shared sockpair |
3511 | | * ipc_fd[0] always opened in master (master CLI server is listening on |
3512 | | * this fd). It's safe to call close() at this point on these inherited |
3513 | | * ipc_fd[0]s, as they are inherited after master re-exec unbound, we |
3514 | | * keep them like this during bind_listeners() call. So, these fds were |
3515 | | * never referenced in the current worker's fdtab. |
3516 | | */ |
3517 | 0 | if ((global.mode & MODE_MWORKER) && !master) { |
3518 | 0 | list_for_each_entry(proc, &proc_list, list) { |
3519 | 0 | if ((proc->options & PROC_O_TYPE_WORKER) && (proc->options & PROC_O_LEAVING)) { |
3520 | 0 | close(proc->ipc_fd[0]); |
3521 | 0 | proc->ipc_fd[0] = -1; |
3522 | 0 | } |
3523 | 0 | } |
3524 | 0 | } |
3525 | | |
3526 | | /* Exit in standalone mode, if no listeners found */ |
3527 | 0 | if (!(global.mode & MODE_MWORKER) && listeners == 0) { |
3528 | 0 | ha_alert("[%s.main()] No enabled listener found (check for 'bind' directives) ! Exiting.\n", argv[0]); |
3529 | | /* Note: we don't have to send anything to the old pids because we |
3530 | | * never stopped them. */ |
3531 | 0 | exit(1); |
3532 | 0 | } |
3533 | | |
3534 | | /* Ok, all listeners should now be bound, close any leftover sockets |
3535 | | * the previous process gave us, we don't need them anymore |
3536 | | */ |
3537 | 0 | sock_drop_unused_old_sockets(); |
3538 | | |
3539 | | /* prepare pause/play signals */ |
3540 | 0 | signal_register_fct(SIGTTOU, sig_pause, SIGTTOU); |
3541 | 0 | signal_register_fct(SIGTTIN, sig_listen, SIGTTIN); |
3542 | | |
3543 | | /* Apply verbosity modes, check the process current nofile limit, |
3544 | | * update the ready date and close the pidfile. |
3545 | | */ |
3546 | 0 | step_init_4(); |
3547 | | |
3548 | | /* Master enters in its polling loop */ |
3549 | 0 | if (master) { |
3550 | 0 | mworker_run_master(); |
3551 | | /* never get there in master context */ |
3552 | 0 | } |
3553 | | |
3554 | | /* End of initialization for standalone and worker modes */ |
3555 | | |
3556 | | /* applies the renice value in the worker or standalone after configuration parsing |
3557 | | * but before changing identity */ |
3558 | 0 | if (!master && global.tune.renice_runtime) { |
3559 | 0 | if (setpriority(PRIO_PROCESS, 0, global.tune.renice_runtime - 100) == -1) { |
3560 | 0 | ha_warning("[%s.main()] couldn't set the runtime nice value to %d: %s\n", |
3561 | 0 | argv[0], global.tune.renice_runtime - 100, strerror(errno)); |
3562 | 0 | } |
3563 | 0 | } |
3564 | | |
3565 | | /* Open PID file before the chroot. In master-worker mode, it's master |
3566 | | * who will create the pidfile, see _send_status(). |
3567 | | */ |
3568 | 0 | if (!(global.mode & MODE_MWORKER)) { |
3569 | 0 | if (global.mode & MODE_DAEMON && (global.pidfile != NULL)) { |
3570 | 0 | if (handle_pidfile() < 0) { |
3571 | 0 | if (nb_oldpids) { |
3572 | 0 | tell_old_pids(SIGTTIN); |
3573 | 0 | protocol_unbind_all(); |
3574 | 0 | } |
3575 | 0 | exit(1); |
3576 | 0 | } |
3577 | 0 | } |
3578 | 0 | } |
3579 | | |
3580 | | /* Must chroot and setgid/setuid in the children */ |
3581 | | /* chroot if needed */ |
3582 | 0 | if (global.chroot != NULL) { |
3583 | 0 | if (chroot(global.chroot) == -1 || chdir("/") == -1) { |
3584 | 0 | ha_alert("[%s.main()] Cannot chroot(%s).\n", argv[0], global.chroot); |
3585 | 0 | if (nb_oldpids) |
3586 | 0 | tell_old_pids(SIGTTIN); |
3587 | 0 | protocol_unbind_all(); |
3588 | 0 | exit(1); |
3589 | 0 | } |
3590 | 0 | } |
3591 | | |
3592 | 0 | ha_free(&global.chroot); |
3593 | | |
3594 | | |
3595 | | /* In standalone mode send USR1/TERM to the previous worker, |
3596 | | * launched with -sf $(cat pidfile). |
3597 | | * In master-worker mode, see _send_status(): master process sends |
3598 | | * USR1/TERM to previous workers up to receiving status READY from the |
3599 | | * worker, which is newly forked. Then master sends USR1 or TERM to previous |
3600 | | * master, if it was launched with (-W -D -sf $(cat pidfile). |
3601 | | */ |
3602 | 0 | if (!(global.mode & MODE_MWORKER) && (nb_oldpids > 0)) { |
3603 | 0 | nb_oldpids = tell_old_pids(oldpids_sig); |
3604 | 0 | } |
3605 | | |
3606 | | /* oldpids_sig was sent to the previous process, can change uid/gid now */ |
3607 | 0 | set_identity(argv[0]); |
3608 | | |
3609 | | /* set_identity() above might have dropped LSTCHK_NETADM or/and |
3610 | | * LSTCHK_SYSADM if it changed to a new UID while preserving enough |
3611 | | * permissions to honnor LSTCHK_NETADM/LSTCHK_SYSADM. |
3612 | | */ |
3613 | 0 | if ((global.last_checks & (LSTCHK_NETADM|LSTCHK_SYSADM)) && getuid()) { |
3614 | | /* If global.uid is present in config, it is already set as euid |
3615 | | * and ruid by set_identity() just above, so it's better to |
3616 | | * remind the user to fix uncoherent settings. |
3617 | | */ |
3618 | 0 | if (global.uid > 0) { |
3619 | 0 | ha_alert("[%s.main()] Some configuration options require full " |
3620 | 0 | "privileges, so global.uid cannot be changed.\n", argv[0]); |
3621 | | #if defined(USE_LINUX_CAP) |
3622 | | ha_alert("[%s.main()] Alternately, if your system supports " |
3623 | | "Linux capabilities, you may also consider using " |
3624 | | "'setcap cap_net_raw', 'setcap cap_net_admin' or " |
3625 | | "'setcap cap_sys_admin' in the 'global' section.\n", argv[0]); |
3626 | | #endif |
3627 | 0 | protocol_unbind_all(); |
3628 | 0 | exit(1); |
3629 | 0 | } |
3630 | | /* If the user is not root, we'll still let them try the configuration |
3631 | | * but we inform them that unexpected behaviour may occur. |
3632 | | */ |
3633 | 0 | ha_warning("[%s.main()] Some options which require full privileges" |
3634 | 0 | " might not work well.\n", argv[0]); |
3635 | 0 | } |
3636 | | |
3637 | 0 | if (global.uid < 0 && geteuid() == 0) |
3638 | 0 | ha_warning("[%s.main()] HAProxy was started as the root user and does " |
3639 | 0 | "not make use of 'user' nor 'uid' global options to drop the " |
3640 | 0 | "privileges. This is generally considered as a bad practice " |
3641 | 0 | "security-wise. If running as root is intentional, please make " |
3642 | 0 | "it explicit using 'uid 0' or 'user root', and also please " |
3643 | 0 | "consider using the 'chroot' directive to isolate the process " |
3644 | 0 | "into a totally empty and read-only directory if possible." |
3645 | | #if defined(USE_LINUX_CAP) |
3646 | | " Also, since your operating system supports it, always prefer " |
3647 | | "relying on capabilities with unprivileged users than running " |
3648 | | "with full privileges (look for 'setcap' in the configuration" |
3649 | | "manual)." |
3650 | | #endif |
3651 | 0 | "\n", argv[0]); |
3652 | | |
3653 | | /* |
3654 | | * This is only done in daemon mode because we might want the |
3655 | | * logs on stdout in mworker mode. If we're NOT in QUIET mode, |
3656 | | * we should now close the 3 first FDs to ensure that we can |
3657 | | * detach from the TTY. We MUST NOT do it in other cases since |
3658 | | * it would have already be done, and 0-2 would have been |
3659 | | * affected to listening sockets |
3660 | | */ |
3661 | 0 | if ((global.mode & MODE_DAEMON) && |
3662 | 0 | (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) { |
3663 | | /* detach from the tty */ |
3664 | 0 | stdio_quiet(devnullfd); |
3665 | 0 | global.mode &= ~MODE_VERBOSE; |
3666 | 0 | global.mode |= MODE_QUIET; /* ensure that we won't say anything from now */ |
3667 | 0 | } |
3668 | 0 | close(devnullfd); |
3669 | 0 | devnullfd = -1; |
3670 | 0 | pid = getpid(); /* update pid */ |
3671 | | |
3672 | | /* This call is expensive, as it creates a new poller, scans and tries |
3673 | | * to migrate to it all existing FDs until the highest known one. With |
3674 | | * very high numbers of FDs, this can take several seconds to start. |
3675 | | * So, it's only desirable for modes, when we perform a fork(). |
3676 | | */ |
3677 | 0 | if (global.mode & MODE_DAEMON) |
3678 | 0 | fork_poller(); |
3679 | | |
3680 | | /* pass through every cli socket, and check if it's bound to |
3681 | | * the current process and if it exposes listeners sockets. |
3682 | | * Caution: the GTUNE_SOCKET_TRANSFER is now set after the fork. |
3683 | | * */ |
3684 | |
|
3685 | 0 | if (global.cli_fe) { |
3686 | 0 | struct bind_conf *bind_conf; |
3687 | |
|
3688 | 0 | list_for_each_entry(bind_conf, &global.cli_fe->conf.bind, by_fe) { |
3689 | 0 | if (bind_conf->level & ACCESS_FD_LISTENERS) { |
3690 | 0 | global.tune.options |= GTUNE_SOCKET_TRANSFER; |
3691 | 0 | break; |
3692 | 0 | } |
3693 | 0 | } |
3694 | 0 | } |
3695 | | |
3696 | | /* Note that here we can't be in the parent/master anymore */ |
3697 | | #if !defined(USE_THREAD) && defined(USE_CPU_AFFINITY) |
3698 | | if (ha_cpuset_count(&cpu_map[0].thread[0])) { /* only do this if the process has a CPU map */ |
3699 | | |
3700 | | #if defined(CPUSET_USE_CPUSET) || defined(__DragonFly__) |
3701 | | struct hap_cpuset *set = &cpu_map[0].thread[0]; |
3702 | | sched_setaffinity(0, sizeof(set->cpuset), &set->cpuset); |
3703 | | #elif defined(__FreeBSD__) |
3704 | | struct hap_cpuset *set = &cpu_map[0].thread[0]; |
3705 | | ret = cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(set->cpuset), &set->cpuset); |
3706 | | #endif |
3707 | | } |
3708 | | #endif |
3709 | | /* try our best to re-enable core dumps depending on system capabilities. |
3710 | | * What is addressed here : |
3711 | | * - remove file size limits |
3712 | | * - remove core size limits |
3713 | | * - mark the process dumpable again if it lost it due to user/group |
3714 | | */ |
3715 | 0 | if (global.tune.options & GTUNE_SET_DUMPABLE) { |
3716 | 0 | limit.rlim_cur = limit.rlim_max = RLIM_INFINITY; |
3717 | |
|
3718 | 0 | #if defined(RLIMIT_FSIZE) |
3719 | 0 | if (setrlimit(RLIMIT_FSIZE, &limit) == -1) { |
3720 | 0 | if (global.tune.options & GTUNE_STRICT_LIMITS) { |
3721 | 0 | ha_alert("[%s.main()] Failed to set the raise the maximum " |
3722 | 0 | "file size.\n", argv[0]); |
3723 | 0 | exit(1); |
3724 | 0 | } |
3725 | 0 | else |
3726 | 0 | ha_warning("[%s.main()] Failed to set the raise the maximum " |
3727 | 0 | "file size.\n", argv[0]); |
3728 | 0 | } |
3729 | 0 | #endif |
3730 | | |
3731 | 0 | #if defined(RLIMIT_CORE) |
3732 | 0 | if (setrlimit(RLIMIT_CORE, &limit) == -1) { |
3733 | 0 | if (global.tune.options & GTUNE_STRICT_LIMITS) { |
3734 | 0 | ha_alert("[%s.main()] Failed to set the raise the core " |
3735 | 0 | "dump size.\n", argv[0]); |
3736 | 0 | exit(1); |
3737 | 0 | } |
3738 | 0 | else |
3739 | 0 | ha_warning("[%s.main()] Failed to set the raise the core " |
3740 | 0 | "dump size.\n", argv[0]); |
3741 | 0 | } |
3742 | 0 | #endif |
3743 | |
|
3744 | | #if defined(USE_PRCTL) |
3745 | | if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) |
3746 | | ha_warning("[%s.main()] Failed to set the dumpable flag, " |
3747 | | "no core will be dumped.\n", argv[0]); |
3748 | | #elif defined(USE_PROCCTL) |
3749 | | { |
3750 | | int traceable = PROC_TRACE_CTL_ENABLE; |
3751 | | if (procctl(P_PID, getpid(), PROC_TRACE_CTL, &traceable) == -1) |
3752 | | ha_warning("[%s.main()] Failed to set the traceable flag, " |
3753 | | "no core will be dumped.\n", argv[0]); |
3754 | | } |
3755 | | #endif |
3756 | 0 | } |
3757 | | |
3758 | | |
3759 | | /* start threads 2 and above */ |
3760 | 0 | setup_extra_threads(&run_thread_poll_loop); |
3761 | | |
3762 | | /* when multithreading we need to let only the thread 0 handle the signals */ |
3763 | 0 | haproxy_unblock_signals(); |
3764 | | |
3765 | | /* send "READY" message to remove status PROC_O_INIT for the newly forked worker, |
3766 | | * master will send TERM to the previous in _send_status() |
3767 | | */ |
3768 | 0 | if (global.mode & MODE_MWORKER) { |
3769 | 0 | struct mworker_proc *proc; |
3770 | 0 | int sock_pair[2]; |
3771 | 0 | char *msg = NULL; |
3772 | 0 | char c; |
3773 | 0 | int r __maybe_unused; |
3774 | |
|
3775 | 0 | if (socketpair(PF_UNIX, SOCK_STREAM, 0, sock_pair) == -1) { |
3776 | 0 | ha_alert("[%s.main()] Cannot create socketpair to update the new worker state\n", |
3777 | 0 | argv[0]); |
3778 | |
|
3779 | 0 | exit(1); |
3780 | 0 | } |
3781 | | |
3782 | 0 | list_for_each_entry(proc, &proc_list, list) { |
3783 | 0 | if (proc->pid == -1) |
3784 | 0 | break; |
3785 | 0 | } |
3786 | |
|
3787 | 0 | if (send_fd_uxst(proc->ipc_fd[1], sock_pair[0]) == -1) { |
3788 | 0 | ha_alert("[%s.main()] Cannot transfer connection fd %d over the sockpair@%d\n", |
3789 | 0 | argv[0], sock_pair[0], proc->ipc_fd[1]); |
3790 | 0 | close(sock_pair[0]); |
3791 | 0 | close(sock_pair[1]); |
3792 | |
|
3793 | 0 | exit(1); |
3794 | 0 | } |
3795 | | |
3796 | 0 | memprintf(&msg, "_send_status READY %d\n", getpid()); |
3797 | 0 | if (send(sock_pair[1], msg, strlen(msg), 0) != strlen(msg)) { |
3798 | 0 | ha_alert("[%s.main()] Failed to send READY status to master\n", argv[0]); |
3799 | |
|
3800 | 0 | exit(1); |
3801 | 0 | } |
3802 | | |
3803 | | /* in macOS, the sock_pair[0] might be received in the master |
3804 | | * process after it was closed in the worker, which is a |
3805 | | * documented bug in sendmsg(2). We need to close the fd only |
3806 | | * after confirming receipt of the "\n" from the CLI applet, so |
3807 | | * we make sure that the fd is received correctly. |
3808 | | */ |
3809 | 0 | shutdown(sock_pair[1], SHUT_WR); |
3810 | 0 | r = read(sock_pair[1], &c, 1); |
3811 | 0 | close(sock_pair[1]); |
3812 | 0 | close(sock_pair[0]); |
3813 | 0 | ha_free(&msg); |
3814 | | |
3815 | | /* at this point the worker must have his own startup_logs buffer */ |
3816 | 0 | tmp_startup_logs = startup_logs_dup(startup_logs); |
3817 | 0 | if (tmp_startup_logs == NULL) |
3818 | 0 | exit(EXIT_FAILURE); |
3819 | 0 | startup_logs_free(startup_logs); |
3820 | 0 | startup_logs = tmp_startup_logs; |
3821 | 0 | } |
3822 | | |
3823 | | /* worker is already sent its READY message to master. This applies only |
3824 | | * for daemon standalone mode. Master in daemon mode will "forward" the READY |
3825 | | * message received from the worker to the launching process, see _send_status(). |
3826 | | */ |
3827 | 0 | if ((global.mode & MODE_DAEMON) && !(global.mode & MODE_MWORKER)) { |
3828 | 0 | const char *msg = "READY\n"; |
3829 | |
|
3830 | 0 | if (write(daemon_fd[1], msg, strlen(msg)) < 0) { |
3831 | 0 | ha_alert("[%s.main()] Failed to write into pipe with parent process: %s\n", progname, strerror(errno)); |
3832 | 0 | exit(1); |
3833 | 0 | } |
3834 | 0 | close(daemon_fd[1]); |
3835 | 0 | daemon_fd[1] = -1; |
3836 | 0 | } |
3837 | | /* can't unset MODE_STARTING earlier, otherwise worker's last alerts |
3838 | | * should be not written in startup logs. |
3839 | | */ |
3840 | 0 | global.mode &= ~MODE_STARTING; |
3841 | 0 | reset_usermsgs_ctx(); |
3842 | | |
3843 | | /* Finally, start the poll loop for the first thread */ |
3844 | 0 | run_thread_poll_loop(&ha_thread_info[0]); |
3845 | | |
3846 | | /* wait for all threads to terminate */ |
3847 | 0 | wait_for_threads_completion(); |
3848 | |
|
3849 | 0 | deinit_and_exit(0); |
3850 | 0 | } |
3851 | | |
3852 | | /* |
3853 | | * Local variables: |
3854 | | * c-indent-level: 8 |
3855 | | * c-basic-offset: 8 |
3856 | | * End: |
3857 | | */ |