Coverage Report

Created: 2026-01-13 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/haproxy/src/mworker.c
Line
Count
Source
1
/*
2
 * Master Worker
3
 *
4
 * Copyright HAProxy Technologies 2019 - William Lallemand <wlallemand@haproxy.com>
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version
9
 * 2 of the License, or (at your option) any later version.
10
 *
11
 */
12
13
#define _GNU_SOURCE
14
15
#include <errno.h>
16
#include <fcntl.h>
17
#include <signal.h>
18
#include <stdlib.h>
19
#include <string.h>
20
#include <sys/wait.h>
21
#include <unistd.h>
22
23
#include <haproxy/api.h>
24
#include <haproxy/cfgparse.h>
25
#include <haproxy/cli.h>
26
#include <haproxy/errors.h>
27
#include <haproxy/fd.h>
28
#include <haproxy/global.h>
29
#include <haproxy/log.h>
30
#include <haproxy/list.h>
31
#include <haproxy/listener.h>
32
#include <haproxy/mworker.h>
33
#include <haproxy/peers.h>
34
#include <haproxy/proto_sockpair.h>
35
#include <haproxy/proxy.h>
36
#include <haproxy/ring.h>
37
#include <haproxy/sc_strm.h>
38
#include <haproxy/signal.h>
39
#include <haproxy/ssl_sock.h>
40
#include <haproxy/stconn.h>
41
#include <haproxy/stream.h>
42
#include <haproxy/systemd.h>
43
#include <haproxy/tools.h>
44
#include <haproxy/version.h>
45
46
47
static int exitcode = -1;
48
int max_reloads = 50; /* max number of reloads a worker can have until they are killed */
49
int load_status; /* worker process startup status: 1 - loaded successfully; 0 - load failed */
50
struct mworker_proc *proc_self = NULL; /* process structure of current process */
51
struct list mworker_cli_conf = LIST_HEAD_INIT(mworker_cli_conf); /* master CLI configuration (-S flag) */
52
53
/* ----- children processes handling ----- */
54
55
/*
56
 * Send signal to every known children.
57
 */
58
59
static void mworker_kill(int sig)
60
0
{
61
0
  struct mworker_proc *child;
62
63
0
  list_for_each_entry(child, &proc_list, list) {
64
    /* careful there, we must be sure that the pid > 0, we don't want to emit a kill -1 */
65
0
    if ((child->options & PROC_O_TYPE_WORKER) && (child->pid > 0))
66
0
      kill(child->pid, sig);
67
0
  }
68
0
}
69
70
void mworker_kill_max_reloads(int sig)
71
0
{
72
0
  struct mworker_proc *child;
73
74
0
  list_for_each_entry(child, &proc_list, list) {
75
0
    if (max_reloads != -1 && (child->options & PROC_O_TYPE_WORKER) &&
76
0
        (child->pid > 0) && (child->reloads > max_reloads))
77
0
      kill(child->pid, sig);
78
0
  }
79
0
}
80
81
/* return 1 if a pid is a current child otherwise 0 */
82
int mworker_current_child(int pid)
83
0
{
84
0
  struct mworker_proc *child;
85
86
0
  list_for_each_entry(child, &proc_list, list) {
87
0
    if ((child->options & PROC_O_TYPE_WORKER) && (!(child->options & PROC_O_LEAVING)) && (child->pid == pid))
88
0
      return 1;
89
0
  }
90
0
  return 0;
91
0
}
92
93
/*
94
 * Return the number of new and old children (including workers and external
95
 * processes)
96
 */
97
int mworker_child_nb()
98
0
{
99
0
  struct mworker_proc *child;
100
0
  int ret = 0;
101
102
0
  list_for_each_entry(child, &proc_list, list) {
103
0
    if (child->options & PROC_O_TYPE_WORKER)
104
0
      ret++;
105
0
  }
106
107
0
  return ret;
108
0
}
109
110
111
/*
112
 * serialize the proc list and put it in the environment
113
 */
114
void mworker_proc_list_to_env()
115
0
{
116
0
  char *msg = NULL;
117
0
  struct mworker_proc *child;
118
119
0
  list_for_each_entry(child, &proc_list, list) {
120
0
    char type = '?';
121
122
0
    if (child->options & PROC_O_TYPE_MASTER)
123
0
      type = 'm';
124
0
    else if (child->options &= PROC_O_TYPE_WORKER)
125
0
      type = 'w';
126
127
0
    if (child->pid > -1)
128
0
      memprintf(&msg, "%s|type=%c;fd=%d;cfd=%d;pid=%d;reloads=%d;failedreloads=%d;timestamp=%d;id=%s;version=%s", msg ? msg : "", type, child->ipc_fd[0], child->ipc_fd[1], child->pid, child->reloads, child->failedreloads, child->timestamp, child->id ? child->id : "", child->version);
129
0
  }
130
0
  if (msg)
131
0
    setenv("HAPROXY_PROCESSES", msg, 1);
132
0
}
133
134
struct mworker_proc *mworker_proc_new()
135
0
{
136
0
  struct mworker_proc *child;
137
138
0
  child = calloc(1, sizeof(*child));
139
0
  if (!child)
140
0
    return NULL;
141
142
0
  child->failedreloads = 0;
143
0
  child->reloads = 0;
144
0
  child->pid = -1;
145
0
  child->ipc_fd[0] = -1;
146
0
  child->ipc_fd[1] = -1;
147
0
  child->timestamp = -1;
148
149
0
  return child;
150
0
}
151
152
153
/*
154
 * unserialize the proc list from the environment
155
 * Return < 0 upon error.
156
 */
157
int mworker_env_to_proc_list()
158
0
{
159
0
  char *env, *msg, *omsg = NULL, *token = NULL, *s1;
160
0
  struct mworker_proc *child;
161
0
  int err = 0;
162
163
0
  env = getenv("HAPROXY_PROCESSES");
164
0
  if (!env)
165
0
    goto no_env;
166
167
0
  omsg = msg = strdup(env);
168
0
  if (!msg) {
169
0
    ha_alert("Out of memory while trying to allocate a worker process structure.");
170
0
    err = -1;
171
0
    goto out;
172
0
  }
173
174
0
  while ((token = strtok_r(msg, "|", &s1))) {
175
0
    char *subtoken = NULL;
176
0
    char *s2 = NULL;
177
178
0
    msg = NULL;
179
180
0
    child = mworker_proc_new();
181
0
    if (!child) {
182
0
      ha_alert("out of memory while trying to allocate a worker process structure.");
183
0
      err = -1;
184
0
      goto out;
185
0
    }
186
187
0
    while ((subtoken = strtok_r(token, ";", &s2))) {
188
189
0
      token = NULL;
190
191
0
      if (strncmp(subtoken, "type=", 5) == 0) {
192
0
        char type;
193
194
0
        type = *(subtoken+5);
195
0
        if (type == 'm') { /* we are in the master, assign it */
196
0
          proc_self = child;
197
0
          child->options |= PROC_O_TYPE_MASTER;
198
0
        } else if (type == 'w') {
199
0
          child->options |= PROC_O_TYPE_WORKER;
200
0
        }
201
202
0
      } else if (strncmp(subtoken, "fd=", 3) == 0) {
203
0
        child->ipc_fd[0] = atoi(subtoken+3);
204
0
        if (child->ipc_fd[0] > -1)
205
0
          global.maxsock++;
206
0
      } else if (strncmp(subtoken, "cfd=", 4) == 0) {
207
0
        child->ipc_fd[1] = atoi(subtoken+4);
208
0
        if (child->ipc_fd[1] > -1)
209
0
          global.maxsock++;
210
0
      } else if (strncmp(subtoken, "pid=", 4) == 0) {
211
0
        child->pid = atoi(subtoken+4);
212
0
      } else if (strncmp(subtoken, "reloads=", 8) == 0) {
213
        /* we only increment the number of asked reload */
214
0
        child->reloads = atoi(subtoken+8);
215
0
      } else if (strncmp(subtoken, "failedreloads=", 14) == 0) {
216
0
        child->failedreloads = atoi(subtoken+14);
217
0
      } else if (strncmp(subtoken, "timestamp=", 10) == 0) {
218
0
        child->timestamp = atoi(subtoken+10);
219
0
      } else if (strncmp(subtoken, "id=", 3) == 0) {
220
0
        child->id = strdup(subtoken+3);
221
0
      } else if (strncmp(subtoken, "version=", 8) == 0) {
222
0
        child->version = strdup(subtoken+8);
223
0
      }
224
0
    }
225
0
    if (child->pid > 0) {
226
0
      LIST_APPEND(&proc_list, &child->list);
227
0
    } else {
228
0
      mworker_free_child(child);
229
0
    }
230
0
  }
231
232
  /* set the leaving processes once we know which number of reloads are the current processes */
233
234
0
  list_for_each_entry(child, &proc_list, list) {
235
0
    if (child->reloads > 0)
236
0
      child->options |= PROC_O_LEAVING;
237
0
  }
238
239
0
  unsetenv("HAPROXY_PROCESSES");
240
241
0
no_env:
242
243
0
  if (!proc_self) {
244
245
0
    proc_self = mworker_proc_new();
246
0
    if (!proc_self) {
247
0
      ha_alert("Cannot allocate process structures.\n");
248
0
      err = -1;
249
0
      goto out;
250
0
    }
251
0
    proc_self->options |= PROC_O_TYPE_MASTER;
252
0
    proc_self->pid = pid;
253
0
    proc_self->timestamp = 0; /* we don't know the startime anymore */
254
255
0
    LIST_APPEND(&proc_list, &proc_self->list);
256
0
    ha_warning("The master internals are corrupted or it was started with a too old version (< 1.9). Please restart the master process.\n");
257
0
  }
258
259
0
out:
260
0
  free(omsg);
261
0
  return err;
262
0
}
263
264
/* Signal blocking and unblocking */
265
266
void mworker_block_signals()
267
0
{
268
0
  sigset_t set;
269
270
0
  sigemptyset(&set);
271
0
  sigaddset(&set, SIGUSR1);
272
0
  sigaddset(&set, SIGUSR2);
273
0
  sigaddset(&set, SIGTTIN);
274
0
  sigaddset(&set, SIGTTOU);
275
0
  sigaddset(&set, SIGHUP);
276
0
  sigaddset(&set, SIGCHLD);
277
0
  ha_sigmask(SIG_SETMASK, &set, NULL);
278
0
}
279
280
void mworker_unblock_sigchld()
281
0
{
282
0
  sigset_t set;
283
284
0
  signal_register_fct(SIGCHLD, mworker_catch_sigchld, SIGCHLD);
285
286
0
  sigemptyset(&set);
287
0
  sigaddset(&set, SIGCHLD);
288
289
0
  ha_sigmask(SIG_UNBLOCK, &set, NULL);
290
0
}
291
292
void mworker_unblock_signals()
293
0
{
294
0
  signal_unregister(SIGTTIN);
295
0
  signal_unregister(SIGTTOU);
296
0
  signal_unregister(SIGUSR1);
297
0
  signal_unregister(SIGHUP);
298
0
  signal_unregister(SIGQUIT);
299
300
0
  signal_register_fct(SIGTERM, mworker_catch_sigterm, SIGTERM);
301
0
  signal_register_fct(SIGUSR1, mworker_catch_sigterm, SIGUSR1);
302
0
  signal_register_fct(SIGTTIN, mworker_broadcast_signal, SIGTTIN);
303
0
  signal_register_fct(SIGTTOU, mworker_broadcast_signal, SIGTTOU);
304
0
  signal_register_fct(SIGINT, mworker_catch_sigterm, SIGINT);
305
0
  signal_register_fct(SIGHUP, mworker_catch_sighup, SIGHUP);
306
0
  signal_register_fct(SIGUSR2, mworker_catch_sighup, SIGUSR2);
307
0
  signal_register_fct(SIGCHLD, mworker_catch_sigchld, SIGCHLD);
308
309
0
  haproxy_unblock_signals();
310
0
}
311
312
/* ----- mworker signal handlers ----- */
313
314
/* broadcast the configured signal to the workers */
315
void mworker_broadcast_signal(struct sig_handler *sh)
316
0
{
317
0
  mworker_kill(sh->arg);
318
0
}
319
320
/*
321
 * When called, this function reexec haproxy with -sf followed by current
322
 * children PIDs and possibly old children PIDs if they didn't leave yet.
323
 */
324
static void mworker_reexec(int hardreload)
325
0
{
326
0
  char **next_argv = NULL;
327
0
  int old_argc = 0; /* previous number of argument */
328
0
  int next_argc = 0;
329
0
  int i = 0;
330
0
  char *msg = NULL;
331
0
  struct rlimit limit;
332
0
  struct mworker_proc *current_child = NULL;
333
0
  int x_off = 0; /* disable -x by putting -x /dev/null */
334
335
0
  mworker_block_signals();
336
337
  /* restore initial environment (before parsing the config) and do re-exec.
338
   * The initial process environment should be restored here, preceded by
339
   * clean_env(), which do the same job as clearenv().
340
   * Otherwise, after the re-exec we will start the new worker in the
341
   * environment modified by '*env' keywords from the previous configuration,
342
   * i.e. existed before the reload.
343
   */
344
0
  if (clean_env() != 0) {
345
0
    ha_alert("Master encountered a non-recoverable error, exiting.\n");
346
0
    exit(EXIT_FAILURE);
347
0
  }
348
349
0
  if (restore_env() != 0) {
350
0
    ha_alert("Master encountered a non-recoverable error, exiting.\n");
351
0
    exit(EXIT_FAILURE);
352
0
  }
353
354
0
  setenv("HAPROXY_MWORKER_REEXEC", "1", 1);
355
356
0
  mworker_proc_list_to_env(); /* put the children description in the env */
357
358
  /* during the reload we must ensure that every FDs that can't be
359
   * reuse (ie those that are not referenced in the proc_list)
360
   * are closed or they will leak. */
361
362
  /* close the listeners FD */
363
0
  mworker_cli_proxy_stop();
364
365
0
  if (fdtab)
366
0
    deinit_pollers();
367
368
#ifdef HAVE_SSL_RAND_KEEP_RANDOM_DEVICES_OPEN
369
  /* close random device FDs */
370
  RAND_keep_random_devices_open(0);
371
#endif
372
373
  /* restore the initial FD limits */
374
0
  limit.rlim_cur = rlim_fd_cur_at_boot;
375
0
  limit.rlim_max = rlim_fd_max_at_boot;
376
0
  if (raise_rlim_nofile(&limit, &limit) != 0) {
377
0
    ha_warning("Failed to restore initial FD limits (cur=%u max=%u), using cur=%u max=%u\n",
378
0
         rlim_fd_cur_at_boot, rlim_fd_max_at_boot,
379
0
         (unsigned int)limit.rlim_cur, (unsigned int)limit.rlim_max);
380
0
  }
381
382
  /* compute length  */
383
0
  while (old_argv[old_argc])
384
0
    old_argc++;
385
386
  /* 1 for haproxy -sf, 2 for -x /socket */
387
0
  next_argv = calloc(old_argc + 1 + 2 + mworker_child_nb() + 1,
388
0
         sizeof(*next_argv));
389
0
  if (next_argv == NULL)
390
0
    goto alloc_error;
391
392
  /* copy the program name */
393
0
  next_argv[next_argc++] = old_argv[0];
394
395
  /* we need to reintroduce /dev/null every time */
396
0
  if (old_unixsocket && strcmp(old_unixsocket, "/dev/null") == 0)
397
0
    x_off = 1;
398
399
  /* insert the new options just after argv[0] in case we have a -- */
400
401
  /* add -sf <PID>*  to argv */
402
0
  if (mworker_child_nb() > 0) {
403
0
    struct mworker_proc *child;
404
405
0
    if (hardreload)
406
0
      next_argv[next_argc++] = "-st";
407
0
    else
408
0
      next_argv[next_argc++] = "-sf";
409
410
0
    list_for_each_entry(child, &proc_list, list) {
411
0
      if (!(child->options & PROC_O_LEAVING) && (child->options & PROC_O_TYPE_WORKER))
412
0
        current_child = child;
413
414
0
      if (!(child->options & (PROC_O_TYPE_WORKER)) || child->pid <= -1)
415
0
        continue;
416
0
      if ((next_argv[next_argc++] = memprintf(&msg, "%d", child->pid)) == NULL)
417
0
        goto alloc_error;
418
0
      msg = NULL;
419
0
    }
420
0
  }
421
0
  if (!x_off && current_child) {
422
    /* add the -x option with the socketpair of the current worker */
423
0
    next_argv[next_argc++] = "-x";
424
0
    if ((next_argv[next_argc++] = memprintf(&msg, "sockpair@%d", current_child->ipc_fd[0])) == NULL)
425
0
      goto alloc_error;
426
0
    msg = NULL;
427
0
  }
428
429
0
  if (x_off) {
430
    /* if the cmdline contained a -x /dev/null, continue to use it */
431
0
    next_argv[next_argc++] = "-x";
432
0
    next_argv[next_argc++] = "/dev/null";
433
0
  }
434
435
  /* copy the previous options */
436
0
  for (i = 1; i < old_argc; i++)
437
0
    next_argv[next_argc++] = old_argv[i];
438
439
  /* need to withdraw MODE_STARTING from master, because we have to free
440
   * the startup logs ring here, see more details in print_message()
441
   */
442
0
  global.mode &= ~MODE_STARTING;
443
0
  startup_logs_free(startup_logs);
444
445
0
  signal(SIGPROF, SIG_IGN);
446
0
  execvp(next_argv[0], next_argv);
447
0
  ha_warning("Failed to reexecute the master process [%d]: %s\n", pid, strerror(errno));
448
0
  ha_free(&next_argv);
449
0
  return;
450
451
0
alloc_error:
452
0
  ha_free(&next_argv);
453
0
  ha_warning("Failed to reexecute the master process [%d]: Cannot allocate memory\n", pid);
454
0
  return;
455
0
}
456
457
/* reload haproxy and emit a warning */
458
static void mworker_reload(int hardreload)
459
0
{
460
0
  struct mworker_proc *child;
461
0
  struct per_thread_deinit_fct *ptdf;
462
463
0
  ha_notice("Reloading HAProxy%s\n", hardreload?" (hard-reload)":"");
464
465
  /* close the poller FD and the thread waker pipe FD */
466
0
  list_for_each_entry(ptdf, &per_thread_deinit_list, list)
467
0
    ptdf->fct();
468
469
  /* increment the number of reloads, child->reloads is checked in
470
   * mworker_env_to_proc_list() (after reload) in order to set
471
   * PROC_O_LEAVING flag for the process
472
   */
473
0
  list_for_each_entry(child, &proc_list, list) {
474
0
    child->reloads++;
475
0
  }
476
477
0
  if (global.tune.options & GTUNE_USE_SYSTEMD) {
478
0
    struct timespec ts;
479
480
0
    (void)clock_gettime(CLOCK_MONOTONIC, &ts);
481
482
0
    sd_notifyf(0,
483
0
               "RELOADING=1\n"
484
0
                   "STATUS=Reloading Configuration.\n"
485
0
                   "MONOTONIC_USEC=%" PRIu64 "\n",
486
0
               (ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000ULL));
487
0
  }
488
0
  mworker_reexec(hardreload);
489
0
}
490
491
/*
492
 * When called, this function reexec haproxy with -sf followed by current
493
 * children PIDs and possibly old children PIDs if they didn't leave yet.
494
 */
495
void mworker_catch_sighup(struct sig_handler *sh)
496
0
{
497
0
  mworker_reload(0);
498
0
}
499
500
void mworker_catch_sigterm(struct sig_handler *sh)
501
0
{
502
0
  int sig = sh->arg;
503
504
0
  if (global.tune.options & GTUNE_USE_SYSTEMD) {
505
0
    sd_notify(0, "STOPPING=1");
506
0
  }
507
0
  ha_warning("Exiting Master process...\n");
508
0
  mworker_kill(sig);
509
0
}
510
511
/* handle operations that can't be done in the signal handler */
512
static struct task *mworker_task_child_failure(struct task *task, void *context, unsigned int state)
513
0
{
514
0
  mworker_unblock_signals();
515
0
  task_destroy(task);
516
0
  return NULL;
517
0
}
518
519
/*
520
 * Performs some routines for the worker process, which has failed the reload,
521
 * updates the global load_status.
522
 */
523
static void mworker_on_new_child_failure(int exitpid, int status)
524
0
{
525
0
  struct mworker_proc *child;
526
0
  struct task *t;
527
528
  /* increment the number of failed reloads */
529
0
  list_for_each_entry(child, &proc_list, list) {
530
0
    child->failedreloads++;
531
0
  }
532
533
  /* do not keep unused FDs retrieved from the previous process */
534
0
  sock_drop_unused_old_sockets();
535
536
0
  usermsgs_clr(NULL);
537
0
  load_status = 0;
538
0
  ha_warning("Failed to load worker (%d) exited with code %d (%s)\n", exitpid, status, (status >= 128) ? strsignal(status - 128): "Exit");
539
  /* the sd_notify API is not able to send a reload failure signal. So
540
   * the READY=1 signal still need to be sent */
541
0
  if (global.tune.options & GTUNE_USE_SYSTEMD)
542
0
    sd_notify(0, "READY=1\nSTATUS=Reload failed!\n");
543
544
  /* call a task to unblock the signals from outside the sig handler */
545
0
  if ((t = task_new_here()) == NULL) {
546
0
    ha_warning("Can't restore HAProxy signals!\n");
547
0
    return;
548
0
  }
549
550
0
  t->process = mworker_task_child_failure;
551
0
  task_wakeup(t, TASK_WOKEN_MSG);
552
0
}
553
554
/*
555
 * Wait for every children to exit
556
 */
557
558
void mworker_catch_sigchld(struct sig_handler *sh)
559
0
{
560
0
  int exitpid = -1;
561
0
  int status = 0;
562
0
  int childfound;
563
0
  struct listener *l, *l_next;
564
0
  struct proxy *curproxy;
565
566
0
restart_wait:
567
568
0
  childfound = 0;
569
570
0
  exitpid = waitpid(-1, &status, WNOHANG);
571
0
  if (exitpid > 0) {
572
0
    struct mworker_proc *child, *it;
573
574
0
    if (WIFEXITED(status))
575
0
      status = WEXITSTATUS(status);
576
0
    else if (WIFSIGNALED(status))
577
0
      status = 128 + WTERMSIG(status);
578
0
    else if (WIFSTOPPED(status))
579
0
      status = 128 + WSTOPSIG(status);
580
0
    else
581
0
      status = 255;
582
583
    /* delete the child from the process list */
584
0
    list_for_each_entry_safe(child, it, &proc_list, list) {
585
0
      if (child->pid != exitpid)
586
0
        continue;
587
588
0
      LIST_DELETE(&child->list);
589
0
      childfound = 1;
590
0
      break;
591
0
    }
592
593
0
    if (!childfound) {
594
      /* We didn't find the PID in the list, that shouldn't happen but we can emit a warning */
595
0
      ha_warning("Process %d exited with code %d (%s)\n", exitpid, status, (status >= 128) ? strsignal(status - 128) : "Exit");
596
0
    } else if (child->options & PROC_O_INIT) {
597
0
      mworker_on_new_child_failure(exitpid, status);
598
599
      /* Detach all listeners */
600
0
      for (curproxy = proxies_list; curproxy; curproxy = curproxy->next) {
601
0
        list_for_each_entry_safe(l, l_next, &curproxy->conf.listeners, by_fe) {
602
0
          if ((l->rx.fd == child->ipc_fd[0]) || (l->rx.fd == child->ipc_fd[1])) {
603
0
            unbind_listener(l);
604
0
            delete_listener(l);
605
0
          }
606
0
        }
607
0
      }
608
609
      /* Drop server */
610
0
      if (child->srv) {
611
0
        srv_detach(child->srv);
612
0
        srv_drop(child->srv);
613
0
      }
614
615
      /* Delete fd from poller fdtab, which will close it */
616
0
      fd_delete(child->ipc_fd[0]);
617
0
      child->ipc_fd[0] = -1;
618
0
      mworker_free_child(child);
619
0
      child = NULL;
620
621
      /* When worker fails during the first startup, there is
622
       * no previous workers with state PROC_O_LEAVING, master
623
       * process should exit here as well to keep the
624
       * previous behaviour
625
       */
626
0
      if ((proc_self->options & PROC_O_TYPE_MASTER) && (proc_self->reloads == 0))
627
0
        exit(status);
628
0
    } else {
629
      /* check if exited child is a current child */
630
0
      if (!(child->options & PROC_O_LEAVING)) {
631
0
        if (child->options & PROC_O_TYPE_WORKER) {
632
0
          fd_delete(child->ipc_fd[0]);
633
0
          if (status < 128)
634
0
            ha_warning("Current worker (%d) exited with code %d (%s)\n", exitpid, status, "Exit");
635
0
          else
636
0
            ha_alert("Current worker (%d) exited with code %d (%s)\n", exitpid, status, strsignal(status - 128));
637
0
        }
638
639
0
        if (status != 0 && status != 130 && status != 143) {
640
0
          if (child->options & PROC_O_TYPE_WORKER) {
641
0
            ha_warning("A worker process unexpectedly died and this can only be explained by a bug in haproxy or its dependencies.\nPlease check that you are running an up to date and maintained version of haproxy and open a bug report.\n");
642
0
            display_version();
643
0
          }
644
          /* new worker, which has been launched at reload has status PROC_O_INIT */
645
0
          if (!(global.tune.options & GTUNE_NOEXIT_ONFAILURE) && !(child->options & PROC_O_INIT)) {
646
0
            ha_alert("exit-on-failure: killing every processes with SIGTERM\n");
647
0
            mworker_kill(SIGTERM);
648
0
          }
649
0
        }
650
        /* 0 & SIGTERM (143) are normal, but we should report SIGINT (130) and other signals */
651
0
        if (exitcode < 0 && status != 0 && status != 143)
652
0
          exitcode = status;
653
0
      } else {
654
0
        if (child->options & PROC_O_TYPE_WORKER) {
655
0
          if (child->reloads > max_reloads)
656
0
            ha_warning("Former worker (%d) exited with code %d (%s), as it exceeds max reloads (%d)\n", exitpid, status, (status >= 128) ? strsignal(status - 128) : "Exit", max_reloads);
657
0
          else
658
0
            ha_warning("Former worker (%d) exited with code %d (%s)\n", exitpid, status, (status >= 128) ? strsignal(status - 128) : "Exit");
659
          /* Delete fd from poller fdtab, which will close it */
660
0
          fd_delete(child->ipc_fd[0]);
661
0
          delete_oldpid(exitpid);
662
0
        }
663
0
      }
664
0
      mworker_free_child(child);
665
0
      child = NULL;
666
0
    }
667
668
    /* do it again to check if it was the last worker */
669
0
    goto restart_wait;
670
0
  }
671
  /* Better rely on the system than on a list of process to check if it was the last one */
672
0
  else if (exitpid == -1 && errno == ECHILD) {
673
0
    struct post_deinit_fct *pdff;
674
675
0
    ha_warning("All workers exited. Exiting... (%d)\n", (exitcode > 0) ? exitcode : EXIT_SUCCESS);
676
677
0
    list_for_each_entry(pdff, &post_deinit_master_list, list)
678
0
      pdff->fct();
679
680
0
    atexit_flag = 0;
681
0
    if (exitcode > 0)
682
0
      exit(exitcode); /* parent must leave using the status code that provoked the exit */
683
0
    exit(EXIT_SUCCESS);
684
0
  }
685
686
0
}
687
688
/* ----- IPC FD (sockpair) related ----- */
689
690
/* This wrapper is called from the workers. It is registered instead of the
691
 * normal listener_accept() so the worker can exit() when it detects that the
692
 * master closed the IPC FD. If it's not a close, we just call the regular
693
 * listener_accept() function.
694
 */
695
void mworker_accept_wrapper(int fd)
696
0
{
697
0
  char c;
698
0
  int ret;
699
700
0
  while (1) {
701
0
    ret = recv(fd, &c, 1, MSG_PEEK);
702
0
    if (ret == -1) {
703
0
      if (errno == EINTR)
704
0
        continue;
705
0
      if (errno == EAGAIN || errno == EWOULDBLOCK) {
706
0
        fd_cant_recv(fd);
707
0
        return;
708
0
      }
709
0
      break;
710
0
    } else if (ret > 0) {
711
0
      struct listener *l = fdtab[fd].owner;
712
713
0
      if (l)
714
0
        listener_accept(l);
715
0
      return;
716
0
    } else if (ret == 0) {
717
      /* At this step the master is down before
718
       * this worker perform a 'normal' exit.
719
       * So we want to exit with an error but
720
       * other threads could currently process
721
       * some stuff so we can't perform a clean
722
       * deinit().
723
       */
724
0
      exit(EXIT_FAILURE);
725
0
    }
726
0
  }
727
0
  return;
728
0
}
729
730
/*
731
 * This function registers the accept wrapper for the sockpair of the master
732
 * worker. It's only handled by worker thread #0. Other threads and master do
733
 * nothing here. It always returns 1 (success).
734
 */
735
static int mworker_sockpair_register_per_thread()
736
0
{
737
0
  if (!(global.mode & MODE_MWORKER) || master)
738
0
    return 1;
739
740
0
  if (tid != 0)
741
0
    return 1;
742
743
0
  if (proc_self->ipc_fd[1] < 0) /* proc_self was incomplete and we can't find the socketpair */
744
0
    return 1;
745
746
0
  fd_set_nonblock(proc_self->ipc_fd[1]);
747
  /* register the wrapper to handle read 0 when the master exits */
748
0
  fdtab[proc_self->ipc_fd[1]].iocb = mworker_accept_wrapper;
749
0
  fd_want_recv(proc_self->ipc_fd[1]);
750
0
  return 1;
751
0
}
752
753
REGISTER_PER_THREAD_INIT(mworker_sockpair_register_per_thread);
754
755
/* ----- proxies ----- */
756
/*
757
 * Upon a reload, the master worker needs to close all listeners FDs but the mworker_pipe
758
 * fd, and the FD provided by fd@
759
 */
760
void mworker_cleanlisteners()
761
0
{
762
0
  struct listener *l, *l_next;
763
0
  struct proxy *curproxy;
764
0
  struct peers *curpeers;
765
766
  /* peers proxies cleanup */
767
0
  for (curpeers = cfg_peers; curpeers; curpeers = curpeers->next) {
768
0
    if (!curpeers->peers_fe)
769
0
      continue;
770
771
0
    stop_proxy(curpeers->peers_fe);
772
    /* disable this peer section so that it kills itself */
773
0
    if (curpeers->sighandler)
774
0
      signal_unregister_handler(curpeers->sighandler);
775
0
    task_destroy(curpeers->sync_task);
776
0
    curpeers->sync_task = NULL;
777
0
    curpeers->peers_fe = NULL;
778
0
  }
779
780
  /* main proxies cleanup */
781
0
  for (curproxy = proxies_list; curproxy; curproxy = curproxy->next) {
782
0
    int listen_in_master = 0;
783
784
0
    list_for_each_entry_safe(l, l_next, &curproxy->conf.listeners, by_fe) {
785
      /* remove the listener, but not those we need in the master... */
786
0
      if (!(l->rx.flags & RX_F_MWORKER)) {
787
0
        unbind_listener(l);
788
0
        delete_listener(l);
789
0
      } else {
790
0
        listen_in_master = 1;
791
0
      }
792
0
    }
793
    /* if the proxy shouldn't be in the master, we stop it */
794
0
    if (!listen_in_master)
795
0
      curproxy->flags |= PR_FL_DISABLED;
796
0
  }
797
0
}
798
799
/* Upon a configuration loading error some mworker_proc and FDs/server were
800
 * assigned but the worker was never forked, we must close the FDs and
801
 * remove the server
802
 */
803
void mworker_cleanup_proc()
804
0
{
805
0
  struct mworker_proc *child, *it;
806
807
0
  list_for_each_entry_safe(child, it, &proc_list, list) {
808
809
0
    if (child->pid == -1) {
810
      /* Close the socketpairs. */
811
0
      if (child->ipc_fd[0] > -1)
812
0
        close(child->ipc_fd[0]);
813
0
      if (child->ipc_fd[1] > -1)
814
0
        close(child->ipc_fd[1]);
815
0
      if (child->srv) {
816
        /* only exists if we created a master CLI listener */
817
0
        srv_detach(child->srv);
818
0
        srv_drop(child->srv);
819
0
      }
820
0
      LIST_DELETE(&child->list);
821
0
      mworker_free_child(child);
822
0
    }
823
0
  }
824
0
}
825
826
struct cli_showproc_ctx {
827
  int debug;
828
  int next_reload; /* reload number to resume from, 0 = from the beginning  */
829
};
830
831
/* Append a single worker row to trash (shared between current/old sections) */
832
static void cli_append_worker_row(struct cli_showproc_ctx *ctx, struct mworker_proc *child, time_t tv_sec)
833
0
{
834
0
  char *uptime = NULL;
835
0
  int up = tv_sec - child->timestamp;
836
837
0
  if (up < 0) /* must never be negative because of clock drift */
838
0
    up = 0;
839
840
0
  memprintf(&uptime, "%dd%02dh%02dm%02ds", up / 86400, (up % 86400) / 3600, (up % 3600) / 60, (up % 60));
841
0
  chunk_appendf(&trash, "%-15u %-15s %-15d %-15s %-15s", child->pid, "worker", child->reloads, uptime, child->version);
842
0
  if (ctx->debug)
843
0
    chunk_appendf(&trash, "\t\t %-15d %-15d", child->ipc_fd[0], child->ipc_fd[1]);
844
0
  chunk_appendf(&trash, "\n");
845
0
  ha_free(&uptime);
846
0
}
847
848
/*  Displays workers and processes  */
849
static int cli_io_handler_show_proc(struct appctx *appctx)
850
0
{
851
0
  struct mworker_proc *child;
852
0
  int old = 0;
853
0
  int up = date.tv_sec - proc_self->timestamp;
854
0
  struct cli_showproc_ctx *ctx = appctx->svcctx;
855
0
  char *uptime = NULL;
856
0
  char *reloadtxt = NULL;
857
858
0
  if (up < 0) /* must never be negative because of clock drift */
859
0
    up = 0;
860
861
0
  chunk_reset(&trash);
862
863
0
  if (ctx->next_reload == 0) {
864
0
    memprintf(&reloadtxt, "%d [failed: %d]", proc_self->reloads, proc_self->failedreloads);
865
0
    chunk_printf(&trash, "#%-14s %-15s %-15s %-15s %-15s", "<PID>", "<type>", "<reloads>", "<uptime>", "<version>");
866
0
    if (ctx->debug)
867
0
      chunk_appendf(&trash, "\t\t %-15s %-15s", "<ipc_fd[0]>", "<ipc_fd[1]>");
868
0
    chunk_appendf(&trash, "\n");
869
870
    /* display the master only the first time */
871
0
    memprintf(&uptime, "%dd%02dh%02dm%02ds", up / 86400, (up % 86400) / 3600, (up % 3600) / 60, (up % 60));
872
0
    chunk_appendf(&trash, "%-15u %-15s %-15s %-15s %-15s", (unsigned int)getpid(), "master", reloadtxt, uptime, haproxy_version);
873
0
    if (ctx->debug)
874
0
      chunk_appendf(&trash, "\t\t %-15d %-15d", proc_self->ipc_fd[0], proc_self->ipc_fd[1]);
875
0
    chunk_appendf(&trash, "\n");
876
0
  }
877
0
  ha_free(&reloadtxt);
878
0
  ha_free(&uptime);
879
880
  /* displays current processes */
881
0
  if (ctx->next_reload == 0)
882
0
    chunk_appendf(&trash, "# workers\n");
883
0
  list_for_each_entry(child, &proc_list, list) {
884
885
    /* don't display current worker if we only need the next ones */
886
0
    if (ctx->next_reload != 0)
887
0
      continue;
888
889
0
    if (!(child->options & PROC_O_TYPE_WORKER))
890
0
      continue;
891
892
0
    if (child->options & PROC_O_LEAVING) {
893
0
      old++;
894
0
      continue;
895
0
    }
896
0
    cli_append_worker_row(ctx, child, date.tv_sec);
897
0
  }
898
899
0
  if (applet_putchk(appctx, &trash) == -1)
900
0
    return 0;
901
902
  /* displays old processes */
903
0
  if (old || ctx->next_reload) { /* there's more */
904
0
    if (ctx->next_reload == 0)
905
0
      chunk_appendf(&trash, "# old workers\n");
906
0
    list_for_each_entry(child, &proc_list, list) {
907
      /* If we're resuming, skip entries that were already printed (reload >= ctx->next_reload) */
908
0
      if (ctx->next_reload && child->reloads >= ctx->next_reload)
909
0
        continue;
910
911
0
      if (!(child->options & PROC_O_TYPE_WORKER))
912
0
        continue;
913
914
0
      if (child->options & PROC_O_LEAVING) {
915
0
        cli_append_worker_row(ctx, child, date.tv_sec);
916
917
        /* Try to flush so we can resume after this reload on next page if the buffer is full. */
918
0
        if (applet_putchk(appctx, &trash) == -1) {
919
          /* resume at this reload (exclude it on next pass) */
920
0
          ctx->next_reload = child->reloads; /* resume after entries >= this reload */
921
0
          return 0;
922
0
        }
923
0
        chunk_reset(&trash);
924
0
      }
925
926
0
    }
927
0
  }
928
929
  /* dump complete: reset resume cursor so next 'show proc' starts from the top */
930
0
  ctx->next_reload = 0;
931
0
  return 1;
932
0
}
933
934
/* reload the master process */
935
static int cli_parse_show_proc(char **args, char *payload, struct appctx *appctx, void *private)
936
0
{
937
0
  struct cli_showproc_ctx *ctx;
938
939
0
  ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
940
941
0
  if (!cli_has_level(appctx, ACCESS_LVL_OPER))
942
0
    return 1;
943
944
0
  if (*args[2]) {
945
946
0
    if (strcmp(args[2], "debug") == 0)
947
0
      ctx->debug = 1;
948
0
    else
949
0
      return cli_err(appctx, "'show proc' only supports 'debug' as argument\n");
950
0
  }
951
952
0
  return 0;
953
0
}
954
955
/* reload the master process */
956
static int cli_parse_reload(char **args, char *payload, struct appctx *appctx, void *private)
957
0
{
958
0
  struct stconn *scb = NULL;
959
0
  struct stream *strm = NULL;
960
0
  struct connection *conn = NULL;
961
0
  int fd = -1;
962
0
  int hardreload = 0;
963
0
  struct mworker_proc *proc;
964
965
0
  if (!cli_has_level(appctx, ACCESS_LVL_OPER))
966
0
    return 1;
967
968
0
  list_for_each_entry(proc, &proc_list, list) {
969
    /* if there is a process with PROC_O_INIT, i.e. new worker is
970
     * doing its init routine, block the reload
971
     */
972
0
    if (proc->options & PROC_O_INIT) {
973
0
      chunk_printf(&trash, "Success=0\n");
974
0
      chunk_appendf(&trash, "--\n");
975
0
      chunk_appendf(&trash, "Another reload is still in progress.\n");
976
977
0
      if (applet_putchk(appctx, &trash) == -1)
978
0
        return 0;
979
980
0
      return 1;
981
0
    }
982
0
  }
983
984
  /* hard reload requested */
985
0
  if (*args[0] == 'h')
986
0
    hardreload = 1;
987
988
  /* This ask for a synchronous reload, which means we will keep this FD
989
     instead of closing it. */
990
991
0
  scb = appctx_sc(appctx);
992
0
  if (scb)
993
0
    strm = sc_strm(scb);
994
0
  if (strm && strm->scf)
995
0
    conn = sc_conn(strm->scf);
996
0
  if (conn)
997
0
    fd = conn_fd(conn);
998
999
  /* Send the FD of the current session to the "cli_reload" FD, which won't be polled */
1000
0
  if (fd != -1 && send_fd_uxst(proc_self->ipc_fd[0], fd) == 0) {
1001
0
    fd_delete(fd); /* avoid the leak of the FD after sending it via the socketpair */
1002
0
  }
1003
0
  mworker_reload(hardreload);
1004
1005
0
  return 1;
1006
0
}
1007
1008
/* Displays if the current reload failed or succeed.
1009
 * If the startup-logs is available, dump it.  */
1010
static int cli_io_handler_show_loadstatus(struct appctx *appctx)
1011
0
{
1012
0
  struct mworker_proc *proc;
1013
1014
0
  if (!cli_has_level(appctx, ACCESS_LVL_OPER))
1015
0
    return 1;
1016
1017
  /* if the worker is still in the process of starting, we have to
1018
   * wait a little bit before trying again to get a final status.
1019
   */
1020
0
  list_for_each_entry(proc, &proc_list, list) {
1021
0
    if (proc->options & PROC_O_INIT) {
1022
0
      appctx->t->expire = tick_add(now_ms, 50);
1023
0
      return 0;
1024
0
    }
1025
0
  }
1026
1027
0
  if (load_status == 0)
1028
0
    chunk_printf(&trash, "Success=0\n");
1029
0
  else
1030
0
    chunk_printf(&trash, "Success=1\n");
1031
1032
0
  if (startup_logs && ring_data(startup_logs) > 1)
1033
0
    chunk_appendf(&trash, "--\n");
1034
1035
0
  if (applet_putchk(appctx, &trash) == -1)
1036
0
    return 0;
1037
1038
0
  if (startup_logs) {
1039
0
    appctx->cli_ctx.io_handler = NULL;
1040
0
    ring_attach_cli(startup_logs, appctx, 0);
1041
0
    return 0;
1042
0
  }
1043
0
  return 1;
1044
0
}
1045
1046
static int mworker_parse_global_max_reloads(char **args, int section_type, struct proxy *curpx,
1047
           const struct proxy *defpx, const char *file, int linenum, char **err)
1048
0
{
1049
0
  if (!(global.mode & MODE_DISCOVERY))
1050
0
    return 0;
1051
1052
0
  if (strcmp(args[0], "mworker-max-reloads") == 0) {
1053
0
    if (too_many_args(1, args, err, NULL))
1054
0
      return -1;
1055
1056
0
    if (*(args[1]) == 0) {
1057
0
      memprintf(err, "'%s' expects an integer argument.", args[0]);
1058
0
      return -1;
1059
0
    }
1060
1061
0
    max_reloads = atol(args[1]);
1062
0
    if (max_reloads < 0) {
1063
0
      memprintf(err, "'%s' expects a positive value or zero.", args[0]);
1064
0
      return -1;
1065
0
    }
1066
0
  } else {
1067
0
    BUG_ON(1, "Triggered in mworker_parse_global_max_reloads() by unsupported keyword.");
1068
0
    return -1;
1069
0
  }
1070
1071
0
  return 0;
1072
0
}
1073
1074
void mworker_free_child(struct mworker_proc *child)
1075
0
{
1076
0
  int i;
1077
1078
0
  if (child == NULL)
1079
0
    return;
1080
1081
0
  for (i = 0; child->command && child->command[i]; i++)
1082
0
    ha_free(&child->command[i]);
1083
1084
0
  ha_free(&child->command);
1085
0
  ha_free(&child->id);
1086
0
  ha_free(&child->version);
1087
0
  free(child);
1088
0
}
1089
1090
/* Creates and binds dedicated master CLI 'reload' sockpair and listeners */
1091
void mworker_create_master_cli(void)
1092
0
{
1093
0
  struct wordlist *it, *c;
1094
1095
0
  if (!LIST_ISEMPTY(&mworker_cli_conf)) {
1096
0
    char *path = NULL;
1097
1098
0
    list_for_each_entry_safe(c, it, &mworker_cli_conf, list) {
1099
0
      if (mworker_cli_master_proxy_new_listener(c->s) == NULL) {
1100
0
        ha_alert("Can't create the master's CLI.\n");
1101
0
        exit(EXIT_FAILURE);
1102
0
      }
1103
0
      LIST_DELETE(&c->list);
1104
0
      free(c->s);
1105
0
      free(c);
1106
0
    }
1107
    /* Creates the mcli_reload listener, which is the listener used
1108
     * to retrieve the master CLI session which asked for the reload.
1109
     *
1110
     * ipc_fd[1] will be used as a listener, and ipc_fd[0]
1111
     * will be used to send the FD of the session.
1112
     *
1113
     * Both FDs will be kept in the master. The sockets are
1114
     * created only if they weren't inherited.
1115
     */
1116
0
    if (proc_self->ipc_fd[1] == -1) {
1117
0
      if (socketpair(AF_UNIX, SOCK_STREAM, 0, proc_self->ipc_fd) < 0) {
1118
0
        ha_alert("Can't create the mcli_reload socketpair.\n");
1119
0
        exit(EXIT_FAILURE);
1120
0
      }
1121
0
    }
1122
1123
    /* Create the mcli_reload listener from the proc_self struct */
1124
0
    memprintf(&path, "sockpair@%d", proc_self->ipc_fd[1]);
1125
1126
0
    mcli_reload_bind_conf = mworker_cli_master_proxy_new_listener(path);
1127
0
    if (mcli_reload_bind_conf == NULL) {
1128
0
      ha_alert("Can't create the mcli_reload listener.\n");
1129
0
      exit(EXIT_FAILURE);
1130
0
    }
1131
0
    ha_free(&path);
1132
0
  }
1133
0
}
1134
1135
/* This function fills proc_list for master-worker mode and creates a sockpair,
1136
 * copied after master-worker fork() to each process context to enable master
1137
 * CLI at worker side (worker can send its status to master).It only returns if
1138
 * everything is OK. If something fails, it exits.
1139
 */
1140
void mworker_prepare_master(void)
1141
0
{
1142
0
  struct mworker_proc *tmproc;
1143
1144
0
  setenv("HAPROXY_MWORKER", "1", 1);
1145
1146
0
  if (getenv("HAPROXY_MWORKER_REEXEC") == NULL) {
1147
1148
0
    tmproc = mworker_proc_new();
1149
0
    if (!tmproc) {
1150
0
      ha_alert("Cannot allocate process structures.\n");
1151
0
      exit(EXIT_FAILURE);
1152
0
    }
1153
0
    tmproc->options |= PROC_O_TYPE_MASTER; /* master */
1154
0
    tmproc->pid = pid;
1155
0
    tmproc->timestamp = start_date.tv_sec;
1156
0
    proc_self = tmproc;
1157
1158
0
    LIST_APPEND(&proc_list, &tmproc->list);
1159
0
  }
1160
1161
0
  tmproc = mworker_proc_new();
1162
0
  if (!tmproc) {
1163
0
    ha_alert("Cannot allocate process structures.\n");
1164
0
    exit(EXIT_FAILURE);
1165
0
  }
1166
  /* worker */
1167
0
  tmproc->options |= (PROC_O_TYPE_WORKER | PROC_O_INIT);
1168
1169
  /* create a sockpair to copy it via fork(), thus it will be in
1170
   * master and in worker processes
1171
   */
1172
0
  if (socketpair(AF_UNIX, SOCK_STREAM, 0, tmproc->ipc_fd) < 0) {
1173
0
    ha_alert("Cannot create worker master CLI socketpair.\n");
1174
0
    exit(EXIT_FAILURE);
1175
0
  }
1176
0
  LIST_APPEND(&proc_list, &tmproc->list);
1177
0
}
1178
1179
static void mworker_loop()
1180
0
{
1181
1182
  /* Busy polling makes no sense in the master :-) */
1183
0
  global.tune.options &= ~GTUNE_BUSY_POLLING;
1184
1185
0
  mworker_unblock_sigchld();
1186
0
  mworker_cleantasks();
1187
1188
0
  mworker_catch_sigchld(NULL); /* ensure we clean the children in case
1189
             some SIGCHLD were lost */
1190
1191
0
  jobs++; /* this is the "master" job, we want to take care of the
1192
    signals even if there is no listener so the poll loop don't
1193
    leave */
1194
1195
0
  fork_poller();
1196
0
  run_thread_poll_loop(NULL);
1197
0
}
1198
1199
void mworker_run_master(void)
1200
0
{
1201
0
  struct mworker_proc *child, *it;
1202
1203
0
  close(devnullfd);
1204
0
  devnullfd = -1;
1205
1206
0
  proc_self->failedreloads = 0; /* reset the number of failure */
1207
0
  mworker_loop();
1208
#if defined(USE_OPENSSL) && !defined(OPENSSL_NO_DH)
1209
  ssl_free_dh();
1210
#endif
1211
0
  master = 0;
1212
  /* close useless master sockets */
1213
0
  mworker_cli_proxy_stop();
1214
1215
  /* free proc struct of other processes  */
1216
0
  list_for_each_entry_safe(child, it, &proc_list, list) {
1217
    /* close the FD of the master side for all
1218
     * workers, we don't need to close the worker
1219
     * side of other workers since it's done with
1220
     * the bind_proc */
1221
0
    if (child->ipc_fd[0] >= 0) {
1222
0
      close(child->ipc_fd[0]);
1223
0
      child->ipc_fd[0] = -1;
1224
0
    }
1225
0
    LIST_DELETE(&child->list);
1226
0
    mworker_free_child(child);
1227
0
    child = NULL;
1228
0
  }
1229
  /* master must leave */
1230
0
  exit(0);
1231
0
}
1232
1233
/* This function at first does master-worker fork. It creates then GLOBAL and
1234
 * MASTER proxies, allocates listeners for these proxies and binds a GLOBAL
1235
 * proxy listener in worker process on ipc_fd[1] and MASTER proxy listener
1236
 * in master process on ipc_fd[0]. ipc_fd[0] and ipc_fd[1] are the "ends" of the
1237
 * sockpair, created in prepare_master(). This sockpair is copied via fork to
1238
 * each process and serves as communication channel between master and worker
1239
 * (master CLI applet is attached in master process to MASTER proxy). This
1240
 * function returns only if everything is OK. If something fails, it exits.
1241
 */
1242
void mworker_apply_master_worker_mode(void)
1243
0
{
1244
0
  int worker_pid;
1245
0
  struct mworker_proc *child;
1246
0
  char *sock_name = NULL;
1247
0
  char *errmsg = NULL;
1248
1249
0
  worker_pid = fork();
1250
0
  switch (worker_pid) {
1251
0
  case -1:
1252
0
    ha_alert("[%s.main()] Cannot fork.\n", progname);
1253
1254
0
    exit(EXIT_FAILURE);
1255
0
  case 0:
1256
0
    if (daemon_fd[1] >= 0) {
1257
0
      close(daemon_fd[1]);
1258
0
      daemon_fd[1] = -1;
1259
0
    }
1260
1261
    /* This one must not be exported, it's internal! */
1262
0
    unsetenv("HAPROXY_MWORKER_REEXEC");
1263
0
    ha_random_jump96(1);
1264
1265
0
    list_for_each_entry(child, &proc_list, list) {
1266
0
      if ((child->options & PROC_O_TYPE_WORKER) && (child->options & PROC_O_INIT)) {
1267
0
        close(child->ipc_fd[0]);
1268
0
        child->ipc_fd[0] = -1;
1269
        /* proc_self needs to point to the new forked worker in
1270
         * worker's context, as it's dereferenced in
1271
         * mworker_sockpair_register_per_thread(), called for
1272
         * master and for worker.
1273
         */
1274
0
        proc_self = child;
1275
        /* attach listener to GLOBAL proxy on child->ipc_fd[1] */
1276
0
        if (mworker_cli_global_proxy_new_listener(child) < 0)
1277
0
          exit(EXIT_FAILURE);
1278
1279
0
        break;
1280
0
      }
1281
1282
      /* need to close reload sockpair fds, inherited after master's execvp and fork(),
1283
       * we can't close these fds in master before the fork(), as ipc_fd[1] serves after
1284
       * the mworker_reexec to obtain the MCLI client connection fd, like this we can
1285
       * write to this connection fd the content of the startup_logs ring.
1286
       */
1287
0
      if (child->options & PROC_O_TYPE_MASTER) {
1288
0
        if (child->ipc_fd[0] > 0)
1289
0
          close(child->ipc_fd[0]);
1290
0
        if (child->ipc_fd[1] > 0)
1291
0
          close(child->ipc_fd[1]);
1292
0
      }
1293
0
    }
1294
0
    break;
1295
0
  default:
1296
    /* in parent */
1297
0
    ha_notice("Initializing new worker (%d)\n", worker_pid);
1298
0
    master = 1;
1299
1300
    /* in exec mode, there's always exactly one thread. Failure to
1301
     * set these ones now will result in nbthread being detected
1302
     * automatically.
1303
     */
1304
0
    global.nbtgroups = 1;
1305
0
    global.nbthread = 1;
1306
1307
    /* creates MASTER proxy */
1308
0
    if (mworker_cli_create_master_proxy(&errmsg) < 0) {
1309
0
      ha_alert("Can't create MASTER proxy: %s\n", errmsg);
1310
0
      free(errmsg);
1311
0
      exit(EXIT_FAILURE);
1312
0
    }
1313
1314
    /* attaches servers to all existed workers on its shared MCLI sockpair ends, ipc_fd[0] */
1315
0
    if (mworker_cli_attach_server(&errmsg) < 0) {
1316
0
      ha_alert("Can't attach servers needed for master CLI %s\n", errmsg ? errmsg : "");
1317
0
      free(errmsg);
1318
0
      exit(EXIT_FAILURE);
1319
0
    }
1320
1321
    /* creates reload sockpair and listeners for master CLI (-S) */
1322
0
    mworker_create_master_cli();
1323
1324
    /* find the right mworker_proc */
1325
0
    list_for_each_entry(child, &proc_list, list) {
1326
0
      if ((child->options & PROC_O_TYPE_WORKER) && (child->options & PROC_O_INIT)) {
1327
0
        child->timestamp = date.tv_sec;
1328
0
        child->pid = worker_pid;
1329
0
        child->version = strdup(haproxy_version);
1330
1331
0
        close(child->ipc_fd[1]);
1332
0
        child->ipc_fd[1] = -1;
1333
1334
        /* attach listener to MASTER proxy on child->ipc_fd[0] */
1335
0
        memprintf(&sock_name, "sockpair@%d", child->ipc_fd[0]);
1336
0
        if (mworker_cli_master_proxy_new_listener(sock_name) == NULL) {
1337
0
          ha_free(&sock_name);
1338
0
          exit(EXIT_FAILURE);
1339
0
        }
1340
0
        ha_free(&sock_name);
1341
1342
0
        break;
1343
0
      }
1344
0
    }
1345
0
  }
1346
0
}
1347
1348
static struct cfg_kw_list mworker_kws = {{ }, {
1349
  { CFG_GLOBAL, "mworker-max-reloads", mworker_parse_global_max_reloads, KWF_DISCOVERY },
1350
  { 0, NULL, NULL },
1351
}};
1352
1353
INITCALL1(STG_REGISTER, cfg_register_keywords, &mworker_kws);
1354
1355
1356
/* register cli keywords */
1357
static struct cli_kw_list cli_kws = {{ },{
1358
  { { "@<relative pid>", NULL }, "@<relative pid>                         : send a command to the <relative pid> process", NULL, cli_io_handler_show_proc, NULL, NULL, ACCESS_MASTER_ONLY},
1359
  { { "@!<pid>", NULL },         "@!<pid>                                 : send a command to the <pid> process", cli_parse_default, NULL, NULL, NULL, ACCESS_MASTER_ONLY},
1360
  { { "@master", NULL },         "@master                                 : send a command to the master process", cli_parse_default, NULL, NULL, NULL, ACCESS_MASTER_ONLY},
1361
  { { "show", "proc", NULL },    "show proc                               : show processes status", cli_parse_show_proc, cli_io_handler_show_proc, NULL, NULL, ACCESS_MASTER_ONLY},
1362
  { { "reload", NULL },          "reload                                  : achieve a soft-reload (-sf) of haproxy", cli_parse_reload, NULL, NULL, NULL, ACCESS_MASTER_ONLY},
1363
  { { "hard-reload", NULL },     "hard-reload                             : achieve a hard-reload (-st) of haproxy", cli_parse_reload, NULL, NULL, NULL, ACCESS_MASTER_ONLY},
1364
  { { "_loadstatus", NULL },     NULL,                                                             cli_parse_default, cli_io_handler_show_loadstatus, NULL, NULL, ACCESS_MASTER_ONLY},
1365
  {{},}
1366
}};
1367
1368
INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);