xref: /lighttpd1.4/src/gw_backend.c (revision 49e9f0ac)
1 #include "first.h"
2 
3 #include "gw_backend.h"
4 
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include "sys-socket.h"
8 #ifdef HAVE_SYS_UIO_H
9 #include <sys/uio.h>
10 #endif
11 #ifdef HAVE_SYS_WAIT_H
12 #include <sys/wait.h>
13 #endif
14 
15 #include <errno.h>
16 #include <fcntl.h>
17 #include <limits.h>
18 #include <stdlib.h>
19 #include <signal.h>
20 #include <string.h>
21 #include <unistd.h>
22 
23 #include "base.h"
24 #include "array.h"
25 #include "buffer.h"
26 #include "crc32.h"
27 #include "fdevent.h"
28 #include "log.h"
29 #include "sock_addr.h"
30 
31 
32 
33 
34 #include "status_counter.h"
35 
36 static int * gw_status_get_counter(server *srv, gw_host *host, gw_proc *proc, const char *tag, size_t len) {
37     buffer *b = srv->tmp_buf;
38     buffer_copy_string_len(b, CONST_STR_LEN("gw.backend."));
39     buffer_append_string_buffer(b, host->id);
40     if (proc) {
41         buffer_append_string_len(b, CONST_STR_LEN("."));
42         buffer_append_int(b, proc->id);
43     }
44     buffer_append_string_len(b, tag, len);
45     return status_counter_get_counter(srv, CONST_BUF_LEN(b));
46 }
47 
48 static void gw_proc_tag_inc(server *srv, gw_host *host, gw_proc *proc, const char *tag, size_t len) {
49     ++(*gw_status_get_counter(srv, host, proc, tag, len));
50 }
51 
52 static void gw_proc_load_inc(server *srv, gw_host *host, gw_proc *proc) {
53     *gw_status_get_counter(srv,host,proc,CONST_STR_LEN(".load")) = ++proc->load;
54 
55     status_counter_inc(srv, CONST_STR_LEN("gw.active-requests"));
56 }
57 
58 static void gw_proc_load_dec(server *srv, gw_host *host, gw_proc *proc) {
59     *gw_status_get_counter(srv,host,proc,CONST_STR_LEN(".load")) = --proc->load;
60 
61     status_counter_dec(srv, CONST_STR_LEN("gw.active-requests"));
62 }
63 
64 static void gw_host_assign(server *srv, gw_host *host) {
65     *gw_status_get_counter(srv,host,NULL,CONST_STR_LEN(".load")) = ++host->load;
66 }
67 
68 static void gw_host_reset(server *srv, gw_host *host) {
69     *gw_status_get_counter(srv,host,NULL,CONST_STR_LEN(".load")) = --host->load;
70 }
71 
72 static int gw_status_init(server *srv, gw_host *host, gw_proc *proc) {
73     *gw_status_get_counter(srv, host, proc, CONST_STR_LEN(".disabled")) = 0;
74     *gw_status_get_counter(srv, host, proc, CONST_STR_LEN(".died")) = 0;
75     *gw_status_get_counter(srv, host, proc, CONST_STR_LEN(".overloaded")) = 0;
76     *gw_status_get_counter(srv, host, proc, CONST_STR_LEN(".connected")) = 0;
77     *gw_status_get_counter(srv, host, proc, CONST_STR_LEN(".load")) = 0;
78 
79     *gw_status_get_counter(srv, host, NULL, CONST_STR_LEN(".load")) = 0;
80 
81     return 0;
82 }
83 
84 
85 
86 
87 static void gw_proc_set_state(gw_host *host, gw_proc *proc, int state) {
88     if ((int)proc->state == state) return;
89     if (proc->state == PROC_STATE_RUNNING) {
90         --host->active_procs;
91     } else if (state == PROC_STATE_RUNNING) {
92         ++host->active_procs;
93     }
94     proc->state = state;
95 }
96 
97 
98 static gw_proc *gw_proc_init(void) {
99     gw_proc *f = calloc(1, sizeof(*f));
100     force_assert(f);
101 
102     f->unixsocket = buffer_init();
103     f->connection_name = buffer_init();
104 
105     f->prev = NULL;
106     f->next = NULL;
107     f->state = PROC_STATE_DIED;
108 
109     return f;
110 }
111 
112 static void gw_proc_free(gw_proc *f) {
113     if (!f) return;
114 
115     gw_proc_free(f->next);
116 
117     buffer_free(f->unixsocket);
118     buffer_free(f->connection_name);
119     free(f->saddr);
120 
121     free(f);
122 }
123 
124 static gw_host *gw_host_init(void) {
125     gw_host *f = calloc(1, sizeof(*f));
126     force_assert(f);
127 
128     f->id = buffer_init();
129     f->host = buffer_init();
130     f->unixsocket = buffer_init();
131     f->docroot = buffer_init();
132     f->bin_path = buffer_init();
133     f->bin_env = array_init();
134     f->bin_env_copy = array_init();
135     f->strip_request_uri = buffer_init();
136     f->xsendfile_docroot = array_init();
137 
138     return f;
139 }
140 
141 static void gw_host_free(gw_host *h) {
142     if (!h) return;
143     if (h->refcount) {
144         --h->refcount;
145         return;
146     }
147 
148     buffer_free(h->id);
149     buffer_free(h->host);
150     buffer_free(h->unixsocket);
151     buffer_free(h->docroot);
152     buffer_free(h->bin_path);
153     buffer_free(h->strip_request_uri);
154     array_free(h->bin_env);
155     array_free(h->bin_env_copy);
156     array_free(h->xsendfile_docroot);
157 
158     gw_proc_free(h->first);
159     gw_proc_free(h->unused_procs);
160 
161     for (size_t i = 0; i < h->args.used; ++i) free(h->args.ptr[i]);
162     free(h->args.ptr);
163     free(h);
164 }
165 
166 static gw_exts *gw_extensions_init(void) {
167     gw_exts *f = calloc(1, sizeof(*f));
168     force_assert(f);
169     return f;
170 }
171 
172 static void gw_extensions_free(gw_exts *f) {
173     if (!f) return;
174     for (size_t i = 0; i < f->used; ++i) {
175         gw_extension *fe = f->exts[i];
176         for (size_t j = 0; j < fe->used; ++j) {
177             gw_host_free(fe->hosts[j]);
178         }
179         buffer_free(fe->key);
180         free(fe->hosts);
181         free(fe);
182     }
183     free(f->exts);
184     free(f);
185 }
186 
187 static int gw_extension_insert(gw_exts *ext, buffer *key, gw_host *fh) {
188     gw_extension *fe = NULL;
189     for (size_t i = 0; i < ext->used; ++i) {
190         if (buffer_is_equal(key, ext->exts[i]->key)) {
191             fe = ext->exts[i];
192             break;
193         }
194     }
195 
196     if (NULL == fe) {
197         fe = calloc(1, sizeof(*fe));
198         force_assert(fe);
199         fe->key = buffer_init();
200         fe->last_used_ndx = -1;
201         buffer_copy_buffer(fe->key, key);
202 
203         if (ext->used == ext->size) {
204             ext->size += 8;
205             ext->exts = realloc(ext->exts, ext->size * sizeof(*(ext->exts)));
206             force_assert(ext->exts);
207         }
208         ext->exts[ext->used++] = fe;
209         fe->size = 4;
210         fe->hosts = malloc(fe->size * sizeof(*(fe->hosts)));
211         force_assert(fe->hosts);
212     } else if (fe->size == fe->used) {
213         fe->size += 4;
214         fe->hosts = realloc(fe->hosts, fe->size * sizeof(*(fe->hosts)));
215         force_assert(fe->hosts);
216     }
217 
218     fe->hosts[fe->used++] = fh;
219     return 0;
220 }
221 
222 static void gw_proc_connect_success(server *srv, gw_host *host, gw_proc *proc, int debug) {
223     gw_proc_tag_inc(srv, host, proc, CONST_STR_LEN(".connected"));
224     proc->last_used = srv->cur_ts;
225 
226     if (debug) {
227         log_error_write(srv, __FILE__, __LINE__, "ssdsbsd",
228                         "got proc:",
229                         "pid:", proc->pid,
230                         "socket:", proc->connection_name,
231                         "load:", proc->load);
232     }
233 }
234 
235 static void gw_proc_connect_error(server *srv, gw_host *host, gw_proc *proc, pid_t pid, int errnum, int debug) {
236     log_error_write(srv, __FILE__, __LINE__, "sssb",
237                     "establishing connection failed:", strerror(errnum),
238                     "socket:", proc->connection_name);
239 
240     if (!proc->is_local) {
241         proc->disabled_until = srv->cur_ts + host->disable_time;
242         gw_proc_set_state(host, proc, PROC_STATE_OVERLOADED);
243     }
244     else if (proc->pid == pid && proc->state == PROC_STATE_RUNNING) {
245         /* several requests from lighttpd might reference the same proc
246          *
247          * Only one of them should mark the proc
248          * and all other ones should just take a new one.
249          *
250          * If a new proc was started with the old struct, this might
251          * otherwise lead to marking a perfectly good proc as dead
252          */
253         log_error_write(srv, __FILE__, __LINE__, "sdssd",
254                         "backend error; we'll disable for", host->disable_time,
255                         "secs and send the request to another backend instead:",
256                         "load:", host->load);
257         if (EAGAIN == errnum) {
258             /* - EAGAIN: cool down the backend; it is overloaded */
259           #ifdef __linux__
260             log_error_write(srv, __FILE__, __LINE__, "s",
261               "If this happened on Linux: You have run out of local ports. "
262               "Check the manual, section Performance how to handle this.");
263           #endif
264             if (debug) {
265                 log_error_write(srv, __FILE__, __LINE__, "sbsd",
266                   "This means that you have more incoming requests than your "
267                   "FastCGI backend can handle in parallel.  It might help to "
268                   "spawn more FastCGI backends or PHP children; if not, "
269                   "decrease server.max-connections.  The load for this FastCGI "
270                   "backend", proc->connection_name, "is", proc->load);
271             }
272             proc->disabled_until = srv->cur_ts + host->disable_time;
273             gw_proc_set_state(host, proc, PROC_STATE_OVERLOADED);
274         }
275         else {
276             /* we got a hard error from the backend like
277              * - ECONNREFUSED for tcp-ip sockets
278              * - ENOENT for unix-domain-sockets
279              */
280           #if 0
281             gw_proc_set_state(host, proc, PROC_STATE_DIED_WAIT_FOR_PID);
282           #else  /* treat as overloaded (future: unless we send kill() signal)*/
283             proc->disabled_until = srv->cur_ts + host->disable_time;
284             gw_proc_set_state(host, proc, PROC_STATE_OVERLOADED);
285           #endif
286         }
287     }
288 
289     if (EAGAIN == errnum) {
290         gw_proc_tag_inc(srv, host, proc, CONST_STR_LEN(".overloaded"));
291     }
292     else {
293         gw_proc_tag_inc(srv, host, proc, CONST_STR_LEN(".died"));
294     }
295 }
296 
297 static void gw_proc_release(server *srv, gw_host *host, gw_proc *proc, int debug) {
298     gw_proc_load_dec(srv, host, proc);
299 
300     if (debug) {
301         log_error_write(srv, __FILE__, __LINE__, "ssdsbsd",
302                         "released proc:",
303                         "pid:", proc->pid,
304                         "socket:", proc->connection_name,
305                         "load:", proc->load);
306     }
307 }
308 
309 static void gw_proc_check_enable(server *srv, gw_host *host, gw_proc *proc) {
310     if (srv->cur_ts <= proc->disabled_until) return;
311     if (proc->state != PROC_STATE_OVERLOADED) return;
312 
313     gw_proc_set_state(host, proc, PROC_STATE_RUNNING);
314 
315     log_error_write(srv, __FILE__, __LINE__,  "sbbdb",
316                     "gw-server re-enabled:", proc->connection_name,
317                     host->host, host->port, host->unixsocket);
318 }
319 
320 static void gw_proc_waitpid_log(server *srv, gw_host *host, gw_proc *proc, int status) {
321     UNUSED(host);
322     if (WIFEXITED(status)) {
323         if (proc->state != PROC_STATE_KILLED) {
324             log_error_write(srv, __FILE__, __LINE__, "sdb",
325                             "child exited:",
326                             WEXITSTATUS(status), proc->connection_name);
327         }
328     } else if (WIFSIGNALED(status)) {
329         if (WTERMSIG(status) != SIGTERM && WTERMSIG(status) != SIGINT
330             && WTERMSIG(status) != host->kill_signal) {
331             log_error_write(srv, __FILE__, __LINE__, "sd",
332                             "child signalled:", WTERMSIG(status));
333         }
334     } else {
335         log_error_write(srv, __FILE__, __LINE__, "sd",
336                         "child died somehow:", status);
337     }
338 }
339 
340 static int gw_proc_waitpid(server *srv, gw_host *host, gw_proc *proc) {
341     int rc, status;
342 
343     if (!proc->is_local) return 0;
344     if (proc->pid <= 0) return 0;
345 
346     do {
347         rc = waitpid(proc->pid, &status, WNOHANG);
348     } while (-1 == rc && errno == EINTR);
349     if (0 == rc) return 0; /* child still running */
350 
351     /* child terminated */
352     if (-1 == rc) {
353         /* EINVAL or ECHILD no child processes */
354         /* should not happen; someone else has cleaned up for us */
355         log_error_write(srv, __FILE__, __LINE__, "sddss",
356                         "pid ", proc->pid, proc->state,
357                         "not found:", strerror(errno));
358     }
359     else {
360         gw_proc_waitpid_log(srv, host, proc, status);
361     }
362 
363     proc->pid = 0;
364     if (proc->state != PROC_STATE_KILLED)
365         proc->disabled_until = srv->cur_ts;
366     gw_proc_set_state(host, proc, PROC_STATE_DIED);
367     return 1;
368 }
369 
370 static int gw_proc_sockaddr_init(server *srv, gw_host *host, gw_proc *proc) {
371     sock_addr addr;
372     socklen_t addrlen;
373 
374     if (!buffer_string_is_empty(proc->unixsocket)) {
375         if (1 != sock_addr_from_str_hints(srv, &addr, &addrlen,
376                                           proc->unixsocket->ptr, AF_UNIX, 0)) {
377             errno = EINVAL;
378             return -1;
379         }
380         buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("unix:"));
381         buffer_append_string_buffer(proc->connection_name, proc->unixsocket);
382     }
383     else {
384         /*(note: name resolution here is *blocking* if IP string not supplied)*/
385         if (1 != sock_addr_from_str_hints(srv, &addr, &addrlen,
386                                           host->host->ptr, 0, proc->port)) {
387             errno = EINVAL;
388             return -1;
389         }
390         else {
391             /* overwrite host->host buffer with IP addr string so that
392              * any further use of gw_host does not block on DNS lookup */
393             sock_addr_inet_ntop_copy_buffer(host->host, &addr);
394             host->family = sock_addr_get_family(&addr);
395         }
396         buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("tcp:"));
397         buffer_append_string_buffer(proc->connection_name, host->host);
398         buffer_append_string_len(proc->connection_name, CONST_STR_LEN(":"));
399         buffer_append_int(proc->connection_name, proc->port);
400     }
401 
402     if (NULL != proc->saddr && proc->saddrlen < addrlen) {
403         free(proc->saddr);
404         proc->saddr = NULL;
405     }
406     if (NULL == proc->saddr) {
407         proc->saddr = (struct sockaddr *)malloc(addrlen);
408         force_assert(proc->saddr);
409     }
410     proc->saddrlen = addrlen;
411     memcpy(proc->saddr, &addr, addrlen);
412     return 0;
413 }
414 
415 static int env_add(char_array *env, const char *key, size_t key_len, const char *val, size_t val_len) {
416     char *dst;
417 
418     if (!key || !val) return -1;
419 
420     dst = malloc(key_len + val_len + 3);
421     force_assert(dst);
422     memcpy(dst, key, key_len);
423     dst[key_len] = '=';
424     memcpy(dst + key_len + 1, val, val_len + 1); /* add the \0 from the value */
425 
426     for (size_t i = 0; i < env->used; ++i) {
427         if (0 == strncmp(dst, env->ptr[i], key_len + 1)) {
428             free(env->ptr[i]);
429             env->ptr[i] = dst;
430             return 0;
431         }
432     }
433 
434     if (env->size <= env->used + 1) {
435         env->size += 16;
436         env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
437         force_assert(env->ptr);
438     }
439 
440     env->ptr[env->used++] = dst;
441 
442     return 0;
443 }
444 
445 static int gw_spawn_connection(server *srv, gw_host *host, gw_proc *proc, int debug) {
446     int gw_fd;
447     int status;
448     struct timeval tv = { 0, 10 * 1000 };
449 
450     if (debug) {
451         log_error_write(srv, __FILE__, __LINE__, "sdb",
452                         "new proc, socket:", proc->port, proc->unixsocket);
453     }
454 
455     gw_fd = fdevent_socket_cloexec(proc->saddr->sa_family, SOCK_STREAM, 0);
456     if (-1 == gw_fd) {
457         log_error_write(srv, __FILE__, __LINE__, "ss",
458                         "failed:", strerror(errno));
459         return -1;
460     }
461 
462     do {
463         status = connect(gw_fd, proc->saddr, proc->saddrlen);
464     } while (-1 == status && errno == EINTR);
465 
466     if (-1 == status && errno != ENOENT
467         && !buffer_string_is_empty(proc->unixsocket)) {
468         log_error_write(srv, __FILE__, __LINE__, "sbss",
469                         "unlink", proc->unixsocket,
470                         "after connect failed:", strerror(errno));
471         unlink(proc->unixsocket->ptr);
472     }
473 
474     close(gw_fd);
475 
476     if (-1 == status) {
477         /* server is not up, spawn it  */
478         char_array env;
479         size_t i;
480         int dfd = -1;
481 
482         /* reopen socket */
483         gw_fd = fdevent_socket_cloexec(proc->saddr->sa_family, SOCK_STREAM, 0);
484         if (-1 == gw_fd) {
485             log_error_write(srv, __FILE__, __LINE__, "ss",
486                             "socket failed:", strerror(errno));
487             return -1;
488         }
489 
490         if (fdevent_set_so_reuseaddr(gw_fd, 1) < 0) {
491             log_error_write(srv, __FILE__, __LINE__, "ss",
492                             "socketsockopt failed:", strerror(errno));
493             close(gw_fd);
494             return -1;
495         }
496 
497         /* create socket */
498         if (-1 == bind(gw_fd, proc->saddr, proc->saddrlen)) {
499             log_error_write(srv, __FILE__, __LINE__, "sbs",
500                             "bind failed for:",
501                             proc->connection_name,
502                             strerror(errno));
503             close(gw_fd);
504             return -1;
505         }
506 
507         if (-1 == listen(gw_fd, host->listen_backlog)) {
508             log_error_write(srv, __FILE__, __LINE__, "ss",
509                             "listen failed:", strerror(errno));
510             close(gw_fd);
511             return -1;
512         }
513 
514         {
515             /* create environment */
516             env.ptr = NULL;
517             env.size = 0;
518             env.used = 0;
519 
520             /* build clean environment */
521             if (host->bin_env_copy->used) {
522                 for (i = 0; i < host->bin_env_copy->used; ++i) {
523                     data_string *ds=(data_string *)host->bin_env_copy->data[i];
524                     char *ge;
525 
526                     if (NULL != (ge = getenv(ds->value->ptr))) {
527                         env_add(&env, CONST_BUF_LEN(ds->value), ge, strlen(ge));
528                     }
529                 }
530             } else {
531                 char ** const e = fdevent_environ();
532                 for (i = 0; e[i]; ++i) {
533                     char *eq;
534 
535                     if (NULL != (eq = strchr(e[i], '='))) {
536                         env_add(&env, e[i], eq - e[i], eq+1, strlen(eq+1));
537                     }
538                 }
539             }
540 
541             /* create environment */
542             for (i = 0; i < host->bin_env->used; ++i) {
543                 data_string *ds = (data_string *)host->bin_env->data[i];
544 
545                 env_add(&env, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value));
546             }
547 
548             for (i = 0; i < env.used; ++i) {
549                 /* search for PHP_FCGI_CHILDREN */
550                 if (0 == strncmp(env.ptr[i], "PHP_FCGI_CHILDREN=",
551                                       sizeof("PHP_FCGI_CHILDREN=")-1)) {
552                     break;
553                 }
554             }
555 
556             /* not found, add a default */
557             if (i == env.used) {
558                 env_add(&env, CONST_STR_LEN("PHP_FCGI_CHILDREN"),
559                               CONST_STR_LEN("1"));
560             }
561 
562             env.ptr[env.used] = NULL;
563         }
564 
565         dfd = fdevent_open_dirname(host->args.ptr[0], 1); /* permit symlinks */
566         if (-1 == dfd) {
567             log_error_write(srv, __FILE__, __LINE__, "sss",
568                             "open dirname failed:", strerror(errno),
569                             host->args.ptr[0]);
570         }
571 
572         /*(FCGI_LISTENSOCK_FILENO == STDIN_FILENO == 0)*/
573         proc->pid = (dfd >= 0)
574           ? fdevent_fork_execve(host->args.ptr[0], host->args.ptr,
575                                 env.ptr, gw_fd, -1, -1, dfd)
576           : -1;
577 
578         for (i = 0; i < env.used; ++i) free(env.ptr[i]);
579         free(env.ptr);
580         if (-1 != dfd) close(dfd);
581         close(gw_fd);
582 
583         if (-1 == proc->pid) {
584             log_error_write(srv, __FILE__, __LINE__, "sb",
585                             "gw-backend failed to start:", host->bin_path);
586             proc->pid = 0;
587             proc->disabled_until = srv->cur_ts;
588             return -1;
589         }
590 
591         /* register process */
592         proc->last_used = srv->cur_ts;
593         proc->is_local = 1;
594 
595         /* wait */
596         select(0, NULL, NULL, NULL, &tv);
597 
598         if (0 != gw_proc_waitpid(srv, host, proc)) {
599             log_error_write(srv, __FILE__, __LINE__, "sb",
600                             "gw-backend failed to start:", host->bin_path);
601             log_error_write(srv, __FILE__, __LINE__, "s",
602               "If you're trying to run your app as a FastCGI backend, make "
603               "sure you're using the FastCGI-enabled version.  If this is PHP "
604               "on Gentoo, add 'fastcgi' to the USE flags.  If this is PHP, try "
605               "removing the bytecode caches for now and try again.");
606             return -1;
607         }
608     } else {
609         proc->is_local = 0;
610         proc->pid = 0;
611 
612         if (debug) {
613             log_error_write(srv, __FILE__, __LINE__, "sb",
614                             "(debug) socket is already used; won't spawn:",
615                             proc->connection_name);
616         }
617     }
618 
619     gw_proc_set_state(host, proc, PROC_STATE_RUNNING);
620     return 0;
621 }
622 
623 static void gw_proc_spawn(server *srv, gw_host *host, int debug) {
624     gw_proc *proc;
625     for (proc = host->unused_procs; proc; proc = proc->next) {
626         /* (proc->pid <= 0 indicates PROC_STATE_DIED, not PROC_STATE_KILLED) */
627         if (proc->pid > 0) continue;
628         /* (do not attempt to spawn another proc if a proc just exited) */
629         if (proc->disabled_until >= srv->cur_ts) return;
630         break;
631     }
632     if (proc) {
633         if (proc == host->unused_procs)
634             host->unused_procs = proc->next;
635         else
636             proc->prev->next = proc->next;
637 
638         if (proc->next) {
639             proc->next->prev = proc->prev;
640             proc->next = NULL;
641         }
642 
643         proc->prev = NULL;
644     } else {
645         proc = gw_proc_init();
646         proc->id = host->max_id++;
647     }
648 
649     ++host->num_procs;
650 
651     if (buffer_string_is_empty(host->unixsocket)) {
652         proc->port = host->port + proc->id;
653     } else {
654         buffer_copy_buffer(proc->unixsocket, host->unixsocket);
655         buffer_append_string_len(proc->unixsocket, CONST_STR_LEN("-"));
656         buffer_append_int(proc->unixsocket, proc->id);
657     }
658 
659     if (0 != gw_proc_sockaddr_init(srv, host, proc)) {
660         /*(should not happen if host->host validated at startup,
661          * and translated from name to IP address at startup)*/
662         log_error_write(srv, __FILE__, __LINE__, "s",
663                         "ERROR: spawning backend failed.");
664         --host->num_procs;
665         if (proc->id == host->max_id-1) --host->max_id;
666         gw_proc_free(proc);
667     } else if (gw_spawn_connection(srv, host, proc, debug)) {
668         log_error_write(srv, __FILE__, __LINE__, "s",
669                         "ERROR: spawning backend failed.");
670         proc->next = host->unused_procs;
671         if (host->unused_procs)
672             host->unused_procs->prev = proc;
673         host->unused_procs = proc;
674     } else {
675         proc->next = host->first;
676         if (host->first)
677             host->first->prev = proc;
678         host->first = proc;
679     }
680 }
681 
682 static void gw_proc_kill(server *srv, gw_host *host, gw_proc *proc) {
683     UNUSED(srv);
684     if (proc->next) proc->next->prev = proc->prev;
685     if (proc->prev) proc->prev->next = proc->next;
686 
687     if (proc->prev == NULL) host->first = proc->next;
688 
689     proc->prev = NULL;
690     proc->next = host->unused_procs;
691     proc->disabled_until = 0;
692 
693     if (host->unused_procs)
694         host->unused_procs->prev = proc;
695     host->unused_procs = proc;
696 
697     kill(proc->pid, host->kill_signal);
698 
699     gw_proc_set_state(host, proc, PROC_STATE_KILLED);
700 
701     --host->num_procs;
702 }
703 
704 static gw_host * unixsocket_is_dup(gw_plugin_data *p, size_t used, buffer *unixsocket) {
705     for (size_t i = 0; i < used; ++i) {
706         gw_exts *exts = p->config_storage[i]->exts;
707         if (NULL == exts) continue;
708         for (size_t j = 0; j < exts->used; ++j) {
709             gw_extension *ex = exts->exts[j];
710             for (size_t n = 0; n < ex->used; ++n) {
711                 gw_host *host = ex->hosts[n];
712                 if (!buffer_string_is_empty(host->unixsocket)
713                     && buffer_is_equal(host->unixsocket, unixsocket)
714                     && !buffer_string_is_empty(host->bin_path))
715                     return host;
716             }
717         }
718     }
719 
720     return NULL;
721 }
722 
723 static int parse_binpath(char_array *env, buffer *b) {
724     char *start = b->ptr;
725     char c;
726     /* search for spaces */
727     for (size_t i = 0; i < buffer_string_length(b); ++i) {
728         switch(b->ptr[i]) {
729         case ' ':
730         case '\t':
731             /* a WS, stop here and copy the argument */
732 
733             if (env->size == env->used) {
734                 env->size += 16;
735                 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
736             }
737 
738             c = b->ptr[i];
739             b->ptr[i] = '\0';
740             env->ptr[env->used++] = strdup(start);
741             b->ptr[i] = c;
742 
743             start = b->ptr + i + 1;
744             break;
745         default:
746             break;
747         }
748     }
749 
750     if (env->size == env->used) { /*need one extra for terminating NULL*/
751         env->size += 16;
752         env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
753     }
754 
755     /* the rest */
756     env->ptr[env->used++] = strdup(start);
757 
758     if (env->size == env->used) { /*need one extra for terminating NULL*/
759         env->size += 16;
760         env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
761     }
762 
763     /* terminate */
764     env->ptr[env->used++] = NULL;
765 
766     return 0;
767 }
768 
769 enum {
770   GW_BALANCE_LEAST_CONNECTION,
771   GW_BALANCE_RR,
772   GW_BALANCE_HASH,
773   GW_BALANCE_STICKY
774 };
775 
776 static gw_host * gw_host_get(server *srv, connection *con, gw_extension *extension, int balance, int debug) {
777     gw_host *host;
778     unsigned long last_max = ULONG_MAX;
779     int max_usage = INT_MAX;
780     int ndx = -1;
781     size_t k;
782 
783     if (extension->used <= 1) {
784         if (1 == extension->used && extension->hosts[0]->active_procs > 0) {
785             ndx = 0;
786         }
787     } else switch(balance) {
788     case GW_BALANCE_HASH:
789         /* hash balancing */
790 
791         if (debug) {
792             log_error_write(srv, __FILE__, __LINE__,  "sd",
793                             "proxy - used hash balancing, hosts:",
794                             extension->used);
795         }
796 
797         for (k = 0, ndx = -1, last_max = ULONG_MAX; k < extension->used; ++k) {
798             unsigned long cur_max;
799             host = extension->hosts[k];
800             if (0 == host->active_procs) continue;
801 
802             cur_max = generate_crc32c(CONST_BUF_LEN(con->uri.path))
803                     + generate_crc32c(CONST_BUF_LEN(host->host)) /* cachable */
804                     + generate_crc32c(CONST_BUF_LEN(con->uri.authority));
805 
806             if (debug) {
807                 log_error_write(srv, __FILE__, __LINE__,  "sbbbd",
808                                 "proxy - election:", con->uri.path,
809                                 host->host, con->uri.authority, cur_max);
810             }
811 
812             if (last_max < cur_max || last_max == ULONG_MAX) {
813                 last_max = cur_max;
814                 ndx = k;
815             }
816         }
817 
818         break;
819     case GW_BALANCE_LEAST_CONNECTION:
820         /* fair balancing */
821         if (debug) {
822             log_error_write(srv, __FILE__, __LINE__,  "s",
823                             "proxy - used least connection");
824         }
825 
826         for (k = 0, ndx = -1, max_usage = INT_MAX; k < extension->used; ++k) {
827             host = extension->hosts[k];
828             if (0 == host->active_procs) continue;
829 
830             if (host->load < max_usage) {
831                 max_usage = host->load;
832                 ndx = k;
833             }
834         }
835 
836         break;
837     case GW_BALANCE_RR:
838         /* round robin */
839         if (debug) {
840             log_error_write(srv, __FILE__, __LINE__,  "s",
841                             "proxy - used round-robin balancing");
842         }
843 
844         /* just to be sure */
845         force_assert(extension->used < INT_MAX);
846 
847         host = extension->hosts[0];
848 
849         /* Use last_used_ndx from first host in list */
850         k = extension->last_used_ndx;
851         ndx = k + 1; /* use next host after the last one */
852         if (ndx < 0) ndx = 0;
853 
854         /* Search first active host after last_used_ndx */
855         while (ndx < (int) extension->used
856                && 0 == (host = extension->hosts[ndx])->active_procs) ++ndx;
857 
858         if (ndx >= (int) extension->used) {
859             /* didn't find a higher id, wrap to the start */
860             for (ndx = 0; ndx <= (int) k; ++ndx) {
861                 host = extension->hosts[ndx];
862                 if (0 != host->active_procs) break;
863             }
864 
865             /* No active host found */
866             if (0 == host->active_procs) ndx = -1;
867         }
868 
869         /* Save new index for next round */
870         extension->last_used_ndx = ndx;
871 
872         break;
873     case GW_BALANCE_STICKY:
874         /* source sticky balancing */
875 
876         if (debug) {
877             log_error_write(srv, __FILE__, __LINE__,  "sd",
878                             "proxy - used sticky balancing, hosts:",
879                             extension->used);
880         }
881 
882         for (k = 0, ndx = -1, last_max = ULONG_MAX; k < extension->used; ++k) {
883             unsigned long cur_max;
884             host = extension->hosts[k];
885 
886             if (0 == host->active_procs) continue;
887 
888             cur_max = generate_crc32c(CONST_BUF_LEN(con->dst_addr_buf))
889                     + generate_crc32c(CONST_BUF_LEN(host->host))
890                     + host->port;
891 
892             if (debug) {
893                 log_error_write(srv, __FILE__, __LINE__,  "sbbdd",
894                                 "proxy - election:", con->dst_addr_buf,
895                                 host->host, host->port, cur_max);
896             }
897 
898             if (last_max < cur_max || last_max == ULONG_MAX) {
899                 last_max = cur_max;
900                 ndx = k;
901             }
902         }
903 
904         break;
905     default:
906         break;
907     }
908 
909     if (-1 != ndx) {
910         /* found a server */
911         host = extension->hosts[ndx];
912 
913         if (debug) {
914             log_error_write(srv, __FILE__, __LINE__,  "sbd",
915                             "gw - found a host", host->host, host->port);
916         }
917 
918         return host;
919     } else if (0 == srv->srvconf.max_worker) {
920         /* special-case adaptive spawning and 0 == host->min_procs */
921         for (k = 0; k < extension->used; ++k) {
922             host = extension->hosts[k];
923             if (0 == host->min_procs && 0 == host->num_procs
924                 && !buffer_string_is_empty(host->bin_path)) {
925                 gw_proc_spawn(srv, host, debug);
926                 if (host->num_procs) return host;
927             }
928         }
929     }
930 
931     /* all hosts are down */
932     /* sorry, we don't have a server alive for this ext */
933     con->http_status = 503; /* Service Unavailable */
934     con->mode = DIRECT;
935 
936     /* only send the 'no handler' once */
937     if (!extension->note_is_sent) {
938         extension->note_is_sent = 1;
939         log_error_write(srv, __FILE__, __LINE__, "sBSbsbs",
940                         "all handlers for", con->uri.path, "?",
941                         con->uri.query, "on", extension->key, "are down.");
942     }
943 
944     return NULL;
945 }
946 
947 static int gw_establish_connection(server *srv, gw_host *host, gw_proc *proc, pid_t pid, int gw_fd, int debug) {
948     if (-1 == connect(gw_fd, proc->saddr, proc->saddrlen)) {
949         if (errno == EINPROGRESS ||
950             errno == EALREADY ||
951             errno == EINTR) {
952             if (debug > 2) {
953                 log_error_write(srv, __FILE__, __LINE__, "sb",
954                                 "connect delayed; will continue later:",
955                                 proc->connection_name);
956             }
957 
958             return 1;
959         } else {
960             gw_proc_connect_error(srv, host, proc, pid, errno, debug);
961             return -1;
962         }
963     }
964 
965     if (debug > 1) {
966         log_error_write(srv, __FILE__, __LINE__, "sd",
967                         "connect succeeded: ", gw_fd);
968     }
969 
970     return 0;
971 }
972 
973 static void gw_restart_dead_procs(server *srv, gw_host *host, int debug, int trigger) {
974     for (gw_proc *proc = host->first; proc; proc = proc->next) {
975         if (debug > 2) {
976             log_error_write(srv, __FILE__, __LINE__,  "sbdddd",
977                             "proc:", proc->connection_name, proc->state,
978                             proc->is_local, proc->load, proc->pid);
979         }
980 
981         switch (proc->state) {
982         case PROC_STATE_RUNNING:
983             break;
984         case PROC_STATE_OVERLOADED:
985             gw_proc_check_enable(srv, host, proc);
986             break;
987         case PROC_STATE_KILLED:
988             if (trigger && ++proc->disabled_until > 4) {
989                 int sig = (proc->disabled_until <= 8)
990                   ? host->kill_signal
991                   : proc->disabled_until <= 16 ? SIGTERM : SIGKILL;
992                 kill(proc->pid, sig);
993             }
994             break;
995         case PROC_STATE_DIED_WAIT_FOR_PID:
996             /*(state should not happen in workers if server.max-worker > 0)*/
997             /*(if PROC_STATE_DIED_WAIT_FOR_PID is used in future, might want
998              * to save proc->disabled_until before gw_proc_waitpid() since
999              * gw_proc_waitpid will set proc->disabled_until to srv->cur_ts,
1000              * and so process will not be restarted below until one sec later)*/
1001             if (0 == gw_proc_waitpid(srv, host, proc)) {
1002                 gw_proc_check_enable(srv, host, proc);
1003             }
1004 
1005             if (proc->state != PROC_STATE_DIED) break;
1006             /* fall through *//*(we have a dead proc now)*/
1007 
1008         case PROC_STATE_DIED:
1009             /* local procs get restarted by us,
1010              * remote ones hopefully by the admin */
1011 
1012             if (!buffer_string_is_empty(host->bin_path)) {
1013                 /* we still have connections bound to this proc,
1014                  * let them terminate first */
1015                 if (proc->load != 0) break;
1016 
1017                 /* avoid spinning if child exits too quickly */
1018                 if (proc->disabled_until >= srv->cur_ts) break;
1019 
1020                 /* restart the child */
1021 
1022                 if (debug) {
1023                     log_error_write(srv, __FILE__, __LINE__, "ssbsdsd",
1024                                     "--- gw spawning",
1025                                     "\n\tsocket", proc->connection_name,
1026                                     "\n\tcurrent:", 1, "/", host->max_procs);
1027                 }
1028 
1029                 if (gw_spawn_connection(srv, host, proc, debug)) {
1030                     log_error_write(srv, __FILE__, __LINE__, "s",
1031                                     "ERROR: spawning gw failed.");
1032                 }
1033             } else {
1034                 gw_proc_check_enable(srv, host, proc);
1035             }
1036             break;
1037         }
1038     }
1039 }
1040 
1041 
1042 
1043 
1044 #include "base.h"
1045 #include "connections.h"
1046 #include "joblist.h"
1047 #include "response.h"
1048 
1049 
1050 /* ok, we need a prototype */
1051 static handler_t gw_handle_fdevent(server *srv, void *ctx, int revents);
1052 
1053 
1054 static gw_handler_ctx * handler_ctx_init(size_t sz) {
1055     gw_handler_ctx *hctx = calloc(1, 0 == sz ? sizeof(*hctx) : sz);
1056     force_assert(hctx);
1057 
1058     /*hctx->response = chunk_buffer_acquire();*//*(allocated when needed)*/
1059 
1060     hctx->request_id = 0;
1061     hctx->gw_mode = GW_RESPONDER;
1062     hctx->state = GW_STATE_INIT;
1063     hctx->proc = NULL;
1064 
1065     hctx->fd = -1;
1066 
1067     hctx->reconnects = 0;
1068     hctx->send_content_body = 1;
1069 
1070     /*hctx->rb = chunkqueue_init();*//*(allocated when needed)*/
1071     hctx->wb = chunkqueue_init();
1072     hctx->wb_reqlen = 0;
1073 
1074     return hctx;
1075 }
1076 
1077 static void handler_ctx_free(gw_handler_ctx *hctx) {
1078     /* caller MUST have called gw_backend_close(srv, hctx) if necessary */
1079     if (hctx->handler_ctx_free) hctx->handler_ctx_free(hctx);
1080     chunk_buffer_release(hctx->response);
1081 
1082     chunkqueue_free(hctx->rb);
1083     chunkqueue_free(hctx->wb);
1084 
1085     free(hctx);
1086 }
1087 
1088 static void handler_ctx_clear(gw_handler_ctx *hctx) {
1089     /* caller MUST have called gw_backend_close(srv, hctx) if necessary */
1090 
1091     hctx->proc = NULL;
1092     hctx->host = NULL;
1093     hctx->ext  = NULL;
1094     /*hctx->ext_auth is intentionally preserved to flag prior authorizer*/
1095 
1096     hctx->gw_mode = GW_RESPONDER;
1097     hctx->state = GW_STATE_INIT;
1098     /*hctx->state_timestamp = 0;*//*(unused; left as-is)*/
1099 
1100     if (hctx->rb) chunkqueue_reset(hctx->rb);
1101     if (hctx->wb) chunkqueue_reset(hctx->wb);
1102     hctx->wb_reqlen = 0;
1103 
1104     if (hctx->response) buffer_clear(hctx->response);
1105 
1106     hctx->fd = -1;
1107     hctx->reconnects = 0;
1108     hctx->request_id = 0;
1109     hctx->send_content_body = 1;
1110 
1111     /*plugin_config conf;*//*(no need to reset for same request)*/
1112 
1113     /*hctx->remote_conn = NULL;*//*(no need to reset for same request)*/
1114     /*hctx->plugin_data = NULL;*//*(no need to reset for same request)*/
1115 }
1116 
1117 
1118 void * gw_init(void) {
1119     return calloc(1, sizeof(gw_plugin_data));
1120 }
1121 
1122 
1123 void gw_plugin_config_free(gw_plugin_config *s) {
1124     gw_exts *exts = s->exts;
1125     if (exts) {
1126         for (size_t j = 0; j < exts->used; ++j) {
1127             gw_extension *ex = exts->exts[j];
1128             for (size_t n = 0; n < ex->used; ++n) {
1129                 gw_proc *proc;
1130                 gw_host *host = ex->hosts[n];
1131 
1132                 for (proc = host->first; proc; proc = proc->next) {
1133                     if (proc->pid > 0) {
1134                         kill(proc->pid, host->kill_signal);
1135                     }
1136 
1137                     if (proc->is_local &&
1138                         !buffer_string_is_empty(proc->unixsocket)) {
1139                         unlink(proc->unixsocket->ptr);
1140                     }
1141                 }
1142 
1143                 for (proc = host->unused_procs; proc; proc = proc->next) {
1144                     if (proc->pid > 0) {
1145                         kill(proc->pid, host->kill_signal);
1146                     }
1147                     if (proc->is_local &&
1148                         !buffer_string_is_empty(proc->unixsocket)) {
1149                         unlink(proc->unixsocket->ptr);
1150                     }
1151                 }
1152             }
1153         }
1154 
1155         gw_extensions_free(s->exts);
1156         gw_extensions_free(s->exts_auth);
1157         gw_extensions_free(s->exts_resp);
1158     }
1159     array_free(s->ext_mapping);
1160     free(s);
1161 }
1162 
1163 handler_t gw_free(server *srv, void *p_d) {
1164     gw_plugin_data *p = p_d;
1165     if (p->config_storage) {
1166         for (size_t i = 0; i < srv->config_context->used; ++i) {
1167             gw_plugin_config *s = p->config_storage[i];
1168             if (NULL == s) continue;
1169             gw_plugin_config_free(s);
1170         }
1171         free(p->config_storage);
1172     }
1173     free(p);
1174     return HANDLER_GO_ON;
1175 }
1176 
1177 int gw_set_defaults_backend(server *srv, gw_plugin_data *p, data_unset *du, size_t i, int sh_exec) {
1178     /* per-module plugin_config MUST have common "base class" gw_plugin_config*/
1179     /* per-module plugin_data MUST have pointer-compatible common "base class"
1180      * with gw_plugin_data (stemming from gw_plugin_config compatibility) */
1181 
1182     data_array *da = (data_array *)du;
1183     gw_plugin_config *s = p->config_storage[i];
1184     buffer *gw_mode;
1185     gw_host *host = NULL;
1186 
1187     if (NULL == da) return 1;
1188 
1189     if (da->type != TYPE_ARRAY || !array_is_kvarray(da->value)) {
1190         log_error_write(srv, __FILE__, __LINE__, "s",
1191           "unexpected value for xxxxx.server; expected "
1192           "( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
1193         return 0;
1194     }
1195 
1196     p->srv_pid = srv->pid;
1197 
1198     gw_mode = buffer_init();
1199 
1200     s->exts      = gw_extensions_init();
1201     s->exts_auth = gw_extensions_init();
1202     s->exts_resp = gw_extensions_init();
1203     /*s->balance = GW_BALANCE_LEAST_CONNECTION;*//*(default)*/
1204 
1205     /*
1206      * gw.server = ( "<ext>" => ( ... ),
1207      *               "<ext>" => ( ... ) )
1208      */
1209 
1210     for (size_t j = 0; j < da->value->used; ++j) {
1211         data_array *da_ext = (data_array *)da->value->data[j];
1212 
1213         /*
1214          * da_ext->key == name of the extension
1215          */
1216 
1217         /*
1218          * gw.server = ( "<ext>" =>
1219          *                     ( "<host>" => ( ... ),
1220          *                       "<host>" => ( ... )
1221          *                     ),
1222          *               "<ext>" => ... )
1223          */
1224 
1225         for (size_t n = 0; n < da_ext->value->used; ++n) {
1226             data_array *da_host = (data_array *)da_ext->value->data[n];
1227 
1228             config_values_t fcv[] = {
1229                 { "host",              NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
1230                 { "docroot",           NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 1 */
1231                 { "mode",              NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 2 */
1232                 { "socket",            NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 3 */
1233                 { "bin-path",          NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 4 */
1234 
1235                 { "check-local",       NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },      /* 5 */
1236                 { "port",              NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },        /* 6 */
1237                 { "min-procs",         NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },        /* 7 */
1238                 { "max-procs",         NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },        /* 8 */
1239                 { "max-load-per-proc", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },        /* 9 */
1240                 { "idle-timeout",      NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },        /* 10 */
1241                 { "disable-time",      NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },        /* 11 */
1242 
1243                 { "bin-environment",   NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },        /* 12 */
1244                 { "bin-copy-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },     /* 13 */
1245 
1246                 { "broken-scriptfilename", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },  /* 14 */
1247                 { "allow-x-send-file",  NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },     /* 15 */
1248                 { "strip-request-uri",  NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },      /* 16 */
1249                 { "kill-signal",        NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },       /* 17 */
1250                 { "fix-root-scriptname",   NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },  /* 18 */
1251                 { "listen-backlog",    NULL, T_CONFIG_INT,   T_CONFIG_SCOPE_CONNECTION },        /* 19 */
1252                 { "x-sendfile",        NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },      /* 20 */
1253                 { "x-sendfile-docroot",NULL, T_CONFIG_ARRAY,  T_CONFIG_SCOPE_CONNECTION },       /* 21 */
1254                 { "tcp-fin-propagate", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },      /* 22 */
1255 
1256                 { NULL,                NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
1257             };
1258             unsigned short host_mode = GW_RESPONDER;
1259 
1260             if (da_host->type != TYPE_ARRAY || !array_is_kvany(da_host->value)){
1261                 log_error_write(srv, __FILE__, __LINE__, "SBS",
1262                   "unexpected value for gw.server near [",
1263                   da_host->key, "](string); expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))");
1264 
1265                 goto error;
1266             }
1267 
1268             host = gw_host_init();
1269             buffer_clear(gw_mode);
1270 
1271             buffer_copy_buffer(host->id, da_host->key);
1272 
1273             host->check_local  = 1;
1274             host->min_procs    = 4;
1275             host->max_procs    = 4;
1276             host->max_load_per_proc = 1;
1277             host->idle_timeout = 60;
1278             host->disable_time = 1;
1279             host->break_scriptfilename_for_php = 0;
1280             host->kill_signal = SIGTERM;
1281             host->fix_root_path_name = 0;
1282             host->listen_backlog = 1024;
1283             host->xsendfile_allow = 0;
1284             host->refcount = 0;
1285 
1286             fcv[0].destination = host->host;
1287             fcv[1].destination = host->docroot;
1288             fcv[2].destination = gw_mode;
1289             fcv[3].destination = host->unixsocket;
1290             fcv[4].destination = host->bin_path;
1291 
1292             fcv[5].destination = &(host->check_local);
1293             fcv[6].destination = &(host->port);
1294             fcv[7].destination = &(host->min_procs);
1295             fcv[8].destination = &(host->max_procs);
1296             fcv[9].destination = &(host->max_load_per_proc);
1297             fcv[10].destination = &(host->idle_timeout);
1298             fcv[11].destination = &(host->disable_time);
1299 
1300             fcv[12].destination = host->bin_env;
1301             fcv[13].destination = host->bin_env_copy;
1302             fcv[14].destination = &(host->break_scriptfilename_for_php);
1303             fcv[15].destination = &(host->xsendfile_allow);
1304             fcv[16].destination = host->strip_request_uri;
1305             fcv[17].destination = &(host->kill_signal);
1306             fcv[18].destination = &(host->fix_root_path_name);
1307             fcv[19].destination = &(host->listen_backlog);
1308             fcv[20].destination = &(host->xsendfile_allow);
1309             fcv[21].destination = host->xsendfile_docroot;
1310             fcv[22].destination = &(host->tcp_fin_propagate);
1311 
1312             if (0 != config_insert_values_internal(srv, da_host->value, fcv, T_CONFIG_SCOPE_CONNECTION)) {
1313                 goto error;
1314             }
1315 
1316             for (size_t m = 0; m < da_host->value->used; ++m) {
1317                 if (NULL != strchr(da_host->value->data[m]->key->ptr, '_')) {
1318                     log_error_write(srv, __FILE__, __LINE__, "sb",
1319                       "incorrect directive contains underscore ('_') instead of dash ('-'):",
1320                       da_host->value->data[m]->key);
1321                 }
1322             }
1323 
1324             if ((!buffer_string_is_empty(host->host) || host->port)
1325                 && !buffer_string_is_empty(host->unixsocket)) {
1326                 log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",
1327                   "either host/port or socket have to be set in:",
1328                   da->key, "= (",
1329                   da_ext->key, " => (",
1330                   da_host->key, " ( ...");
1331 
1332                 goto error;
1333             }
1334 
1335             if (!buffer_string_is_empty(host->host) && *host->host->ptr == '/'
1336                 && buffer_string_is_empty(host->unixsocket)) {
1337                 buffer_copy_buffer(host->unixsocket, host->host);
1338             }
1339 
1340             if (!buffer_string_is_empty(host->unixsocket)) {
1341                 /* unix domain socket */
1342                 struct sockaddr_un un;
1343 
1344                 if (buffer_string_length(host->unixsocket) + 1 > sizeof(un.sun_path) - 2) {
1345                     log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",
1346                             "unixsocket is too long in:",
1347                             da->key, "= (",
1348                             da_ext->key, " => (",
1349                             da_host->key, " ( ...");
1350 
1351                     goto error;
1352                 }
1353 
1354                 if (!buffer_string_is_empty(host->bin_path)) {
1355                     gw_host *duplicate = unixsocket_is_dup(p, i+1, host->unixsocket);
1356                     if (NULL != duplicate) {
1357                         if (!buffer_is_equal(host->bin_path, duplicate->bin_path)) {
1358                             log_error_write(srv, __FILE__, __LINE__, "sb",
1359                                 "duplicate unixsocket path:",
1360                                 host->unixsocket);
1361                             goto error;
1362                         }
1363                         gw_host_free(host);
1364                         host = duplicate;
1365                         ++host->refcount;
1366                     }
1367                 }
1368 
1369                 host->family = AF_UNIX;
1370             } else {
1371                 /* tcp/ip */
1372 
1373                 if (buffer_string_is_empty(host->host) &&
1374                     buffer_string_is_empty(host->bin_path)) {
1375                     log_error_write(srv, __FILE__, __LINE__, "sbsbsbs",
1376                             "host or bin-path have to be set in:",
1377                             da->key, "= (",
1378                             da_ext->key, " => (",
1379                             da_host->key, " ( ...");
1380 
1381                     goto error;
1382                 } else if (0 == host->port) {
1383                     host->port = 80;
1384                 }
1385 
1386                 if (buffer_string_is_empty(host->host)) {
1387                     buffer_copy_string_len(host->host,
1388                                            CONST_STR_LEN("127.0.0.1"));
1389                 }
1390 
1391                 host->family = (NULL != strchr(host->host->ptr, ':'))
1392                   ? AF_INET6
1393                   : AF_INET;
1394             }
1395 
1396             if (host->refcount) {
1397                 /* already init'd; skip spawning */
1398             } else if (!buffer_string_is_empty(host->bin_path)) {
1399                 /* a local socket + self spawning */
1400                 struct stat st;
1401                 parse_binpath(&host->args, host->bin_path);
1402                 if (0 != stat(host->args.ptr[0], &st) || !S_ISREG(st.st_mode)
1403                     || !(st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
1404                     log_error_write(srv, __FILE__, __LINE__, "SSs",
1405                       "invalid \"bin-path\" => \"", host->bin_path->ptr,
1406                       "\" (check that file exists, is regular file, "
1407                       "and is executable by lighttpd)");
1408                 }
1409 
1410                 if (sh_exec) {
1411                     /*(preserve prior behavior for SCGI exec of command)*/
1412                     /*(admin should really prefer to put
1413                      * any complex command into a script)*/
1414                     for (size_t m = 0; m < host->args.used; ++m)
1415                         free(host->args.ptr[m]);
1416                     free(host->args.ptr);
1417 
1418                     host->args.ptr = calloc(4, sizeof(char *));
1419                     force_assert(host->args.ptr);
1420                     host->args.used = 3;
1421                     host->args.size = 4;
1422                     host->args.ptr[0] = malloc(sizeof("/bin/sh"));
1423                     force_assert(host->args.ptr[0]);
1424                     memcpy(host->args.ptr[0], "/bin/sh", sizeof("/bin/sh"));
1425                     host->args.ptr[1] = malloc(sizeof("-c"));
1426                     force_assert(host->args.ptr[1]);
1427                     memcpy(host->args.ptr[1], "-c", sizeof("-c"));
1428                     host->args.ptr[2] =
1429                       malloc(sizeof("exec ")-1
1430                              + buffer_string_length(host->bin_path) + 1);
1431                     force_assert(host->args.ptr[2]);
1432                     memcpy(host->args.ptr[2], "exec ", sizeof("exec ")-1);
1433                     memcpy(host->args.ptr[2]+sizeof("exec ")-1,
1434                            host->bin_path->ptr,
1435                            buffer_string_length(host->bin_path)+1);
1436                     host->args.ptr[3] = NULL;
1437                 }
1438 
1439                 if (host->min_procs > host->max_procs)
1440                     host->min_procs = host->max_procs;
1441                 if (host->min_procs!= host->max_procs
1442                     && 0 != srv->srvconf.max_worker) {
1443                     host->min_procs = host->max_procs;
1444                     log_error_write(srv, __FILE__, __LINE__, "s",
1445                                     "adaptive backend spawning disabled "
1446                                     "(server.max_worker is non-zero)");
1447                 }
1448                 if (host->max_load_per_proc < 1)
1449                     host->max_load_per_proc = 0;
1450 
1451                 if (s->debug) {
1452                     log_error_write(srv, __FILE__, __LINE__, "ssbsdsbsdsd",
1453                                     "--- gw spawning local",
1454                                     "\n\tproc:", host->bin_path,
1455                                     "\n\tport:", host->port,
1456                                     "\n\tsocket", host->unixsocket,
1457                                     "\n\tmin-procs:", host->min_procs,
1458                                     "\n\tmax-procs:", host->max_procs);
1459                 }
1460 
1461                 for (size_t pno = 0; pno < host->min_procs; ++pno) {
1462                     gw_proc *proc = gw_proc_init();
1463                     proc->id = host->num_procs++;
1464                     host->max_id++;
1465 
1466                     if (buffer_string_is_empty(host->unixsocket)) {
1467                         proc->port = host->port + pno;
1468                     } else {
1469                         buffer_copy_buffer(proc->unixsocket, host->unixsocket);
1470                         buffer_append_string_len(proc->unixsocket,
1471                                                  CONST_STR_LEN("-"));
1472                         buffer_append_int(proc->unixsocket, pno);
1473                     }
1474 
1475                     if (s->debug) {
1476                         log_error_write(srv, __FILE__, __LINE__, "ssdsbsdsd",
1477                           "--- gw spawning",
1478                           "\n\tport:", host->port,
1479                           "\n\tsocket", host->unixsocket,
1480                           "\n\tcurrent:", pno, "/", host->max_procs);
1481                     }
1482 
1483                     if (0 != gw_proc_sockaddr_init(srv, host, proc)) {
1484                         gw_proc_free(proc);
1485                         goto error;
1486                     }
1487 
1488                     if (!srv->srvconf.preflight_check
1489                         && gw_spawn_connection(srv, host, proc, s->debug)) {
1490                         log_error_write(srv, __FILE__, __LINE__, "s",
1491                                         "[ERROR]: spawning gw failed.");
1492                         gw_proc_free(proc);
1493                         goto error;
1494                     }
1495 
1496                     gw_status_init(srv, host, proc);
1497 
1498                     proc->next = host->first;
1499                     if (host->first) host->first->prev = proc;
1500 
1501                     host->first = proc;
1502                 }
1503             } else {
1504                 gw_proc *proc;
1505 
1506                 proc = gw_proc_init();
1507                 proc->id = host->num_procs++;
1508                 host->max_id++;
1509                 gw_proc_set_state(host, proc, PROC_STATE_RUNNING);
1510 
1511                 if (buffer_string_is_empty(host->unixsocket)) {
1512                     proc->port = host->port;
1513                 } else {
1514                     buffer_copy_buffer(proc->unixsocket, host->unixsocket);
1515                 }
1516 
1517                 gw_status_init(srv, host, proc);
1518 
1519                 host->first = proc;
1520 
1521                 host->min_procs = 1;
1522                 host->max_procs = 1;
1523 
1524                 if (0 != gw_proc_sockaddr_init(srv, host, proc)) goto error;
1525             }
1526 
1527             if (!buffer_string_is_empty(gw_mode)) {
1528                 if (strcmp(gw_mode->ptr, "responder") == 0) {
1529                     host_mode = GW_RESPONDER;
1530                 } else if (strcmp(gw_mode->ptr, "authorizer") == 0) {
1531                     host_mode = GW_AUTHORIZER;
1532                 } else {
1533                     log_error_write(srv, __FILE__, __LINE__, "sbs",
1534                                     "WARNING: unknown gw mode:",
1535                                     gw_mode,"(ignored, mode set to responder)");
1536                 }
1537             }
1538 
1539             if (host->xsendfile_docroot->used) {
1540                 size_t k;
1541                 for (k = 0; k < host->xsendfile_docroot->used; ++k) {
1542                     data_string *ds = (data_string *)host->xsendfile_docroot->data[k];
1543                     if (ds->type != TYPE_STRING) {
1544                         log_error_write(srv, __FILE__, __LINE__, "s",
1545                           "unexpected type for x-sendfile-docroot; expected: \"x-sendfile-docroot\" => ( \"/allowed/path\", ... )");
1546                         goto error;
1547                     }
1548                     if (ds->value->ptr[0] != '/') {
1549                         log_error_write(srv, __FILE__, __LINE__, "SBs",
1550                           "x-sendfile-docroot paths must begin with '/'; invalid: \"", ds->value, "\"");
1551                         goto error;
1552                     }
1553                     buffer_path_simplify(ds->value, ds->value);
1554                     buffer_append_slash(ds->value);
1555                 }
1556             }
1557 
1558             /* s->exts is list of exts -> hosts
1559              * s->exts now used as combined list
1560              *   of authorizer and responder hosts (for backend maintenance)
1561              * s->exts_auth is list of exts -> authorizer hosts
1562              * s->exts_resp is list of exts -> responder hosts
1563              * For each path/extension:
1564              * there may be an independent GW_AUTHORIZER and GW_RESPONDER
1565              * (The GW_AUTHORIZER and GW_RESPONDER could be handled by the same
1566              *  host, and an admin might want to do that for large uploads,
1567              *  since GW_AUTHORIZER runs prior to receiving (potentially large)
1568              *  request body from client and can authorizer or deny request
1569              *  prior to receiving the full upload)
1570              */
1571             gw_extension_insert(s->exts, da_ext->key, host);
1572 
1573             if (host_mode == GW_AUTHORIZER) {
1574                 ++host->refcount;
1575                 gw_extension_insert(s->exts_auth, da_ext->key, host);
1576             } else if (host_mode == GW_RESPONDER) {
1577                 ++host->refcount;
1578                 gw_extension_insert(s->exts_resp, da_ext->key, host);
1579             } /*(else should have been rejected above)*/
1580 
1581             host = NULL;
1582         }
1583     }
1584 
1585     buffer_free(gw_mode);
1586     return 1;
1587 
1588 error:
1589     if (NULL != host) gw_host_free(host);
1590     buffer_free(gw_mode);
1591     return 0;
1592 }
1593 
1594 int gw_set_defaults_balance(server *srv, gw_plugin_config *s, data_unset *du) {
1595     buffer *b;
1596     if (NULL == du) {
1597         b = NULL;
1598     } else if (du->type == TYPE_STRING) {
1599         b = ((data_string *)du)->value;
1600     } else {
1601         log_error_write(srv, __FILE__, __LINE__, "s",
1602                         "unexpected type for xxxxx.balance; expected string");
1603         return 0;
1604     }
1605     if (buffer_string_is_empty(b)) {
1606         s->balance = GW_BALANCE_LEAST_CONNECTION;
1607     } else if (buffer_is_equal_string(b, CONST_STR_LEN("fair"))) {
1608         s->balance = GW_BALANCE_LEAST_CONNECTION;
1609     } else if (buffer_is_equal_string(b, CONST_STR_LEN("least-connection"))) {
1610         s->balance = GW_BALANCE_LEAST_CONNECTION;
1611     } else if (buffer_is_equal_string(b, CONST_STR_LEN("round-robin"))) {
1612         s->balance = GW_BALANCE_RR;
1613     } else if (buffer_is_equal_string(b, CONST_STR_LEN("hash"))) {
1614         s->balance = GW_BALANCE_HASH;
1615     } else if (buffer_is_equal_string(b, CONST_STR_LEN("sticky"))) {
1616         s->balance = GW_BALANCE_STICKY;
1617     } else {
1618         log_error_write(srv, __FILE__, __LINE__, "sb",
1619                         "xxxxx.balance has to be one of: "
1620                         "least-connection, round-robin, hash, sticky, but not:",
1621                         b);
1622         return 0;
1623     }
1624     return 1;
1625 }
1626 
1627 static void gw_set_state(server *srv, gw_handler_ctx *hctx, gw_connection_state_t state) {
1628     hctx->state = state;
1629     hctx->state_timestamp = srv->cur_ts;
1630 }
1631 
1632 
1633 void gw_set_transparent(server *srv, gw_handler_ctx *hctx) {
1634     if (AF_UNIX != hctx->host->family) {
1635         if (-1 == fdevent_set_tcp_nodelay(hctx->fd, 1)) {
1636             /*(error, but not critical)*/
1637         }
1638     }
1639     hctx->wb_reqlen = -1;
1640     gw_set_state(srv, hctx, GW_STATE_WRITE);
1641 }
1642 
1643 
1644 static void gw_backend_close(server *srv, gw_handler_ctx *hctx) {
1645     if (hctx->fd >= 0) {
1646         fdevent_fdnode_event_del(srv->ev, hctx->fdn);
1647         /*fdevent_unregister(srv->ev, hctx->fd);*//*(handled below)*/
1648         fdevent_sched_close(srv->ev, hctx->fd, 1);
1649         hctx->fdn = NULL;
1650         hctx->fd = -1;
1651     }
1652 
1653     if (hctx->host) {
1654         if (hctx->proc) {
1655             gw_proc_release(srv, hctx->host, hctx->proc, hctx->conf.debug);
1656             hctx->proc = NULL;
1657         }
1658 
1659         gw_host_reset(srv, hctx->host);
1660         hctx->host = NULL;
1661     }
1662 }
1663 
1664 static void gw_connection_close(server *srv, gw_handler_ctx *hctx) {
1665     gw_plugin_data *p = hctx->plugin_data;
1666     connection *con = hctx->remote_conn;
1667 
1668     gw_backend_close(srv, hctx);
1669     handler_ctx_free(hctx);
1670     con->plugin_ctx[p->id] = NULL;
1671 
1672     if (con->mode == p->id) {
1673         http_response_backend_done(srv, con);
1674     }
1675 }
1676 
1677 static handler_t gw_reconnect(server *srv, gw_handler_ctx *hctx) {
1678     gw_backend_close(srv, hctx);
1679 
1680     hctx->host = gw_host_get(srv, hctx->remote_conn, hctx->ext,
1681                              hctx->conf.balance, hctx->conf.debug);
1682     if (NULL == hctx->host) return HANDLER_FINISHED;
1683 
1684     gw_host_assign(srv, hctx->host);
1685     hctx->request_id = 0;
1686     hctx->opts.xsendfile_allow = hctx->host->xsendfile_allow;
1687     hctx->opts.xsendfile_docroot = hctx->host->xsendfile_docroot;
1688     gw_set_state(srv, hctx, GW_STATE_INIT);
1689     return HANDLER_COMEBACK;
1690 }
1691 
1692 
1693 handler_t gw_connection_reset(server *srv, connection *con, void *p_d) {
1694     gw_plugin_data *p = p_d;
1695     gw_handler_ctx *hctx = con->plugin_ctx[p->id];
1696     if (hctx) gw_connection_close(srv, hctx);
1697 
1698     return HANDLER_GO_ON;
1699 }
1700 
1701 
1702 static void gw_conditional_tcp_fin(server *srv, gw_handler_ctx *hctx) {
1703     connection *con = hctx->remote_conn;
1704     /*assert(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_TCP_FIN);*/
1705     if (!chunkqueue_is_empty(hctx->wb)) return;
1706     if (!hctx->host->tcp_fin_propagate) return;
1707     if (hctx->gw_mode == GW_AUTHORIZER) return;
1708     if (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BACKEND_SHUT_WR)
1709         return;
1710 
1711     /* propagate shutdown SHUT_WR to backend if TCP half-close on con->fd */
1712     con->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_BACKEND_SHUT_WR;
1713     con->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN;
1714     con->is_readable = 0;
1715     shutdown(hctx->fd, SHUT_WR);
1716     fdevent_fdnode_event_clr(srv->ev, hctx->fdn, FDEVENT_OUT);
1717 }
1718 
1719 static handler_t gw_write_request(server *srv, gw_handler_ctx *hctx) {
1720     switch(hctx->state) {
1721     case GW_STATE_INIT:
1722         /* do we have a running process for this host (max-procs) ? */
1723         hctx->proc = NULL;
1724 
1725         for (gw_proc *proc = hctx->host->first; proc; proc = proc->next) {
1726              if (proc->state == PROC_STATE_RUNNING) {
1727                  hctx->proc = proc;
1728                  break;
1729              }
1730         }
1731 
1732         /* all children are dead */
1733         if (hctx->proc == NULL) {
1734             return HANDLER_ERROR;
1735         }
1736 
1737         /* check the other procs if they have a lower load */
1738         for (gw_proc *proc = hctx->proc->next; proc; proc = proc->next) {
1739             if (proc->state != PROC_STATE_RUNNING) continue;
1740             if (proc->load < hctx->proc->load) hctx->proc = proc;
1741         }
1742 
1743         gw_proc_load_inc(srv, hctx->host, hctx->proc);
1744 
1745         hctx->fd = fdevent_socket_nb_cloexec(hctx->host->family,SOCK_STREAM,0);
1746         if (-1 == hctx->fd) {
1747             if (errno == EMFILE || errno == EINTR) {
1748                 log_error_write(srv, __FILE__, __LINE__, "sd",
1749                                 "wait for fd at connection:",
1750                                 hctx->remote_conn->fd);
1751                 return HANDLER_WAIT_FOR_FD;
1752             }
1753 
1754             log_error_write(srv, __FILE__, __LINE__, "ssdd",
1755                             "socket failed:", strerror(errno),
1756                             srv->cur_fds, srv->max_fds);
1757             return HANDLER_ERROR;
1758         }
1759 
1760         srv->cur_fds++;
1761 
1762         hctx->fdn = fdevent_register(srv->ev,hctx->fd,gw_handle_fdevent,hctx);
1763 
1764         if (hctx->proc->is_local) {
1765             hctx->pid = hctx->proc->pid;
1766         }
1767 
1768         switch (gw_establish_connection(srv, hctx->host, hctx->proc, hctx->pid,
1769                                         hctx->fd, hctx->conf.debug)) {
1770         case 1: /* connection is in progress */
1771             fdevent_fdnode_event_set(srv->ev, hctx->fdn, FDEVENT_OUT);
1772             gw_set_state(srv, hctx, GW_STATE_CONNECT_DELAYED);
1773             return HANDLER_WAIT_FOR_EVENT;
1774         case -1:/* connection error */
1775             return HANDLER_ERROR;
1776         case 0: /* everything is ok, go on */
1777             hctx->reconnects = 0;
1778             break;
1779         }
1780         /* fall through */
1781     case GW_STATE_CONNECT_DELAYED:
1782         if (hctx->state == GW_STATE_CONNECT_DELAYED) { /*(not GW_STATE_INIT)*/
1783             int socket_error = fdevent_connect_status(hctx->fd);
1784             if (socket_error != 0) {
1785                 gw_proc_connect_error(srv, hctx->host, hctx->proc, hctx->pid,
1786                                       socket_error, hctx->conf.debug);
1787                 return HANDLER_ERROR;
1788             }
1789             /* go on with preparing the request */
1790         }
1791 
1792         gw_proc_connect_success(srv, hctx->host, hctx->proc, hctx->conf.debug);
1793 
1794         gw_set_state(srv, hctx, GW_STATE_PREPARE_WRITE);
1795         /* fall through */
1796     case GW_STATE_PREPARE_WRITE:
1797         /* ok, we have the connection */
1798 
1799         {
1800             handler_t rc = hctx->create_env(srv, hctx);
1801             if (HANDLER_GO_ON != rc) {
1802                 if (HANDLER_FINISHED != rc && HANDLER_ERROR != rc)
1803                     fdevent_fdnode_event_clr(srv->ev, hctx->fdn, FDEVENT_OUT);
1804                 return rc;
1805             }
1806         }
1807 
1808         /*(disable Nagle algorithm if streaming and content-length unknown)*/
1809         if (AF_UNIX != hctx->host->family) {
1810             connection *con = hctx->remote_conn;
1811             if (con->request.content_length < 0) {
1812                 if (-1 == fdevent_set_tcp_nodelay(hctx->fd, 1)) {
1813                     /*(error, but not critical)*/
1814                 }
1815             }
1816         }
1817 
1818         fdevent_fdnode_event_add(srv->ev, hctx->fdn, FDEVENT_IN|FDEVENT_RDHUP);
1819         gw_set_state(srv, hctx, GW_STATE_WRITE);
1820         /* fall through */
1821     case GW_STATE_WRITE:
1822         if (!chunkqueue_is_empty(hctx->wb)) {
1823             int ret;
1824           #if 0
1825             if (hctx->conf.debug > 1) {
1826                 log_error_write(srv, __FILE__, __LINE__, "sdsx",
1827                                 "send data to backend ( fd =", hctx->fd,
1828                                 "), size =", chunkqueue_length(hctx->wb));
1829             }
1830           #endif
1831             ret = srv->network_backend_write(srv, hctx->fd, hctx->wb,
1832                                              MAX_WRITE_LIMIT);
1833 
1834             chunkqueue_remove_finished_chunks(hctx->wb);
1835 
1836             if (ret < 0) {
1837                 switch(errno) {
1838                 case EPIPE:
1839                 case ENOTCONN:
1840                 case ECONNRESET:
1841                     /* the connection got dropped after accept()
1842                      * we don't care about that --
1843                      * if you accept() it, you have to handle it.
1844                      */
1845                     log_error_write(srv, __FILE__, __LINE__, "ssosb",
1846                                     "connection was dropped after accept() "
1847                                     "(perhaps the gw process died),",
1848                                     "write-offset:", hctx->wb->bytes_out,
1849                                     "socket:", hctx->proc->connection_name);
1850                     return HANDLER_ERROR;
1851                 default:
1852                     log_error_write(srv, __FILE__, __LINE__, "ssd",
1853                                     "write failed:", strerror(errno), errno);
1854                     return HANDLER_ERROR;
1855                 }
1856             }
1857         }
1858 
1859         if (hctx->wb->bytes_out == hctx->wb_reqlen) {
1860             fdevent_fdnode_event_clr(srv->ev, hctx->fdn, FDEVENT_OUT);
1861             gw_set_state(srv, hctx, GW_STATE_READ);
1862         } else {
1863             off_t wblen = hctx->wb->bytes_in - hctx->wb->bytes_out;
1864             if ((hctx->wb->bytes_in < hctx->wb_reqlen || hctx->wb_reqlen < 0)
1865                 && wblen < 65536 - 16384) {
1866                 connection *con = hctx->remote_conn;
1867                 /*(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/
1868                 if (!(con->conf.stream_request_body
1869                       & FDEVENT_STREAM_REQUEST_POLLIN)) {
1870                     con->conf.stream_request_body |=
1871                         FDEVENT_STREAM_REQUEST_POLLIN;
1872                     con->is_readable = 1;/*trigger optimistic read from client*/
1873                 }
1874             }
1875             if (0 == wblen) {
1876                 fdevent_fdnode_event_clr(srv->ev, hctx->fdn, FDEVENT_OUT);
1877             } else {
1878                 fdevent_fdnode_event_add(srv->ev, hctx->fdn, FDEVENT_OUT);
1879             }
1880         }
1881 
1882         if (hctx->remote_conn->conf.stream_request_body
1883             & FDEVENT_STREAM_REQUEST_TCP_FIN)
1884             gw_conditional_tcp_fin(srv, hctx);
1885 
1886         return HANDLER_WAIT_FOR_EVENT;
1887     case GW_STATE_READ:
1888         /* waiting for a response */
1889         return HANDLER_WAIT_FOR_EVENT;
1890     default:
1891         log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state");
1892         return HANDLER_ERROR;
1893     }
1894 }
1895 
1896 static handler_t gw_write_error(server *srv, gw_handler_ctx *hctx) {
1897     connection *con = hctx->remote_conn;
1898     int status = con->http_status;
1899 
1900     if (hctx->state == GW_STATE_INIT ||
1901         hctx->state == GW_STATE_CONNECT_DELAYED) {
1902 
1903         /* (optimization to detect backend process exit while processing a
1904          *  large number of ready events; (this block could be removed)) */
1905         if (0 == srv->srvconf.max_worker)
1906             gw_restart_dead_procs(srv, hctx->host, hctx->conf.debug, 0);
1907 
1908         /* cleanup this request and let request handler start request again */
1909         if (hctx->reconnects++ < 5) return gw_reconnect(srv, hctx);
1910     }
1911 
1912     if (hctx->backend_error) hctx->backend_error(hctx);
1913     gw_connection_close(srv, hctx);
1914     con->http_status = (status == 400) ? 400 : 503;
1915     return HANDLER_FINISHED;
1916 }
1917 
1918 static handler_t gw_send_request(server *srv, gw_handler_ctx *hctx) {
1919     handler_t rc = gw_write_request(srv, hctx);
1920     return (HANDLER_ERROR != rc) ? rc : gw_write_error(srv, hctx);
1921 }
1922 
1923 
1924 static handler_t gw_recv_response(server *srv, gw_handler_ctx *hctx);
1925 
1926 
1927 handler_t gw_handle_subrequest(server *srv, connection *con, void *p_d) {
1928     gw_plugin_data *p = p_d;
1929     gw_handler_ctx *hctx = con->plugin_ctx[p->id];
1930     if (NULL == hctx) return HANDLER_GO_ON;
1931     if (con->mode != p->id) return HANDLER_GO_ON; /* not my job */
1932 
1933     if ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN)
1934         && con->file_started) {
1935         if (chunkqueue_length(con->write_queue) > 65536 - 4096) {
1936             fdevent_fdnode_event_clr(srv->ev, hctx->fdn, FDEVENT_IN);
1937         }
1938         else if (!(fdevent_fdnode_interest(hctx->fdn) & FDEVENT_IN)) {
1939             /* optimistic read from backend */
1940             handler_t rc;
1941             rc = gw_recv_response(srv, hctx);        /*(might invalidate hctx)*/
1942             if (rc != HANDLER_GO_ON) return rc;      /*(unless HANDLER_GO_ON)*/
1943             fdevent_fdnode_event_add(srv->ev, hctx->fdn, FDEVENT_IN);
1944         }
1945     }
1946 
1947     /* (do not receive request body before GW_AUTHORIZER has run or else
1948      *  the request body is discarded with handler_ctx_clear() after running
1949      *  the FastCGI Authorizer) */
1950 
1951     if (hctx->gw_mode != GW_AUTHORIZER
1952         && (0 == hctx->wb->bytes_in
1953             ? (con->state == CON_STATE_READ_POST || -1 == hctx->wb_reqlen)
1954             : (hctx->wb->bytes_in < hctx->wb_reqlen || hctx->wb_reqlen < 0))) {
1955         /* leave excess data in con->request_content_queue, which is
1956          * buffered to disk if too large and backend can not keep up */
1957         /*(64k - 4k to attempt to avoid temporary files
1958          * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/
1959         if (hctx->wb->bytes_in - hctx->wb->bytes_out > 65536 - 4096) {
1960             if (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN) {
1961                 con->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN;
1962             }
1963             if (0 != hctx->wb->bytes_in) return HANDLER_WAIT_FOR_EVENT;
1964         }
1965         else {
1966             handler_t r = connection_handle_read_post_state(srv, con);
1967             chunkqueue *req_cq = con->request_content_queue;
1968           #if 0 /*(not reached since we send 411 Length Required below)*/
1969             if (hctx->wb_reqlen < -1 && con->request.content_length >= 0) {
1970                 /* (completed receiving Transfer-Encoding: chunked) */
1971                 hctx->wb_reqlen= -hctx->wb_reqlen + con->request.content_length;
1972                 if (hctx->stdin_append) {
1973                     handler_t rc = hctx->stdin_append(srv, hctx);
1974                     if (HANDLER_GO_ON != rc) return rc;
1975                 }
1976             }
1977           #endif
1978             if ((0 != hctx->wb->bytes_in || -1 == hctx->wb_reqlen)
1979                 && !chunkqueue_is_empty(req_cq)) {
1980                 if (hctx->stdin_append) {
1981                     handler_t rc = hctx->stdin_append(srv, hctx);
1982                     if (HANDLER_GO_ON != rc) return rc;
1983                 }
1984                 else
1985                     chunkqueue_append_chunkqueue(hctx->wb, req_cq);
1986                 if (fdevent_fdnode_interest(hctx->fdn) & FDEVENT_OUT) {
1987                     return (r == HANDLER_GO_ON) ? HANDLER_WAIT_FOR_EVENT : r;
1988                 }
1989             }
1990             if (r != HANDLER_GO_ON) return r;
1991 
1992 
1993             /* XXX: create configurable flag */
1994             /* CGI environment requires that Content-Length be set.
1995              * Send 411 Length Required if Content-Length missing.
1996              * (occurs here if client sends Transfer-Encoding: chunked
1997              *  and module is flagged to stream request body to backend) */
1998             /* proxy currently sends HTTP/1.0 request and ideally should send
1999              * Content-Length with request if request body is present, so
2000              * send 411 Length Required if Content-Length missing. */
2001             if (-1 == con->request.content_length) {
2002                 return connection_handle_read_post_error(srv, con, 411);
2003             }
2004         }
2005     }
2006 
2007     {
2008         handler_t rc =((0==hctx->wb->bytes_in || !chunkqueue_is_empty(hctx->wb))
2009                        && hctx->state != GW_STATE_CONNECT_DELAYED)
2010           ? gw_send_request(srv, hctx)
2011           : HANDLER_WAIT_FOR_EVENT;
2012         if (HANDLER_WAIT_FOR_EVENT != rc) return rc;
2013     }
2014 
2015     if (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_TCP_FIN)
2016         gw_conditional_tcp_fin(srv, hctx);
2017 
2018     return HANDLER_WAIT_FOR_EVENT;
2019 }
2020 
2021 
2022 static handler_t gw_recv_response(server *srv, gw_handler_ctx *hctx) {
2023     connection *con = hctx->remote_conn;
2024     gw_proc *proc = hctx->proc;
2025     gw_host *host = hctx->host;
2026     /*(XXX: make this a configurable flag for other protocols)*/
2027     buffer *b = hctx->opts.backend == BACKEND_FASTCGI
2028       ? chunk_buffer_acquire()
2029       : hctx->response;
2030 
2031     handler_t rc =
2032       http_response_read(srv, hctx->remote_conn, &hctx->opts, b, hctx->fdn);
2033 
2034     if (b != hctx->response) chunk_buffer_release(b);
2035 
2036     switch (rc) {
2037     default:
2038         return HANDLER_GO_ON;
2039     case HANDLER_FINISHED:
2040         if (hctx->gw_mode == GW_AUTHORIZER
2041             && (200 == con->http_status || 0 == con->http_status)) {
2042             /*
2043              * If we are here in AUTHORIZER mode then a request for authorizer
2044              * was processed already, and status 200 has been returned. We need
2045              * now to handle authorized request.
2046              */
2047             buffer *physpath = NULL;
2048 
2049             if (!buffer_string_is_empty(host->docroot)) {
2050                 buffer_copy_buffer(con->physical.doc_root, host->docroot);
2051                 buffer_copy_buffer(con->physical.basedir, host->docroot);
2052 
2053                 buffer_copy_buffer(con->physical.path, host->docroot);
2054                 buffer_append_string_buffer(con->physical.path, con->uri.path);
2055                 physpath = con->physical.path;
2056             }
2057 
2058             proc->last_used = srv->cur_ts;
2059             gw_backend_close(srv, hctx);
2060             handler_ctx_clear(hctx);
2061 
2062             /* don't do more than 6 loops here; normally shouldn't happen */
2063             if (++con->loops_per_request > 5) {
2064                 log_error_write(srv, __FILE__, __LINE__, "sb",
2065                                 "too many loops while processing request:",
2066                                 con->request.orig_uri);
2067                 con->http_status = 500; /* Internal Server Error */
2068                 con->mode = DIRECT;
2069                 return HANDLER_FINISHED;
2070             }
2071 
2072             /* restart the request so other handlers can process it */
2073 
2074             if (physpath) con->physical.path = NULL;
2075             connection_response_reset(srv,con);/*(includes con->http_status=0)*/
2076             /* preserve con->physical.path with modified docroot */
2077             if (physpath) con->physical.path = physpath;
2078 
2079             /*(FYI: if multiple FastCGI authorizers were to be supported,
2080              * next one could be started here instead of restarting request)*/
2081 
2082             con->mode = DIRECT;
2083             return HANDLER_COMEBACK;
2084         } else {
2085             /* we are done */
2086             gw_connection_close(srv, hctx);
2087         }
2088 
2089         return HANDLER_FINISHED;
2090     case HANDLER_COMEBACK: /*(not expected; treat as error)*/
2091     case HANDLER_ERROR:
2092         /* (optimization to detect backend process exit while processing a
2093          *  large number of ready events; (this block could be removed)) */
2094         if (proc->is_local && 1 == proc->load && proc->pid == hctx->pid
2095             && proc->state != PROC_STATE_DIED && 0 == srv->srvconf.max_worker) {
2096             /* intentionally check proc->disabed_until before gw_proc_waitpid */
2097             if (proc->disabled_until < srv->cur_ts
2098                 && 0 != gw_proc_waitpid(srv, host, proc)) {
2099                 if (hctx->conf.debug) {
2100                     log_error_write(srv, __FILE__, __LINE__, "ssbsdsd",
2101                                     "--- gw spawning",
2102                                     "\n\tsocket", proc->connection_name,
2103                                     "\n\tcurrent:", 1, "/", host->num_procs);
2104                 }
2105 
2106                 if (gw_spawn_connection(srv, host, proc, hctx->conf.debug)) {
2107                     log_error_write(srv, __FILE__, __LINE__, "s",
2108                                     "respawning failed, will retry later");
2109                 }
2110             }
2111         }
2112 
2113         if (con->file_started == 0) {
2114             /* nothing has been sent out yet, try to use another child */
2115 
2116             if (hctx->wb->bytes_out == 0 &&
2117                 hctx->reconnects++ < 5) {
2118 
2119                 log_error_write(srv, __FILE__, __LINE__, "ssbsBSBs",
2120                   "response not received, request not sent",
2121                   "on socket:", proc->connection_name,
2122                   "for", con->uri.path, "?", con->uri.query, ", reconnecting");
2123 
2124                 return gw_reconnect(srv, hctx);
2125             }
2126 
2127             log_error_write(srv, __FILE__, __LINE__, "sosbsBSBs",
2128               "response not received, request sent:", hctx->wb->bytes_out,
2129               "on socket:", proc->connection_name, "for",
2130               con->uri.path, "?", con->uri.query, ", closing connection");
2131         } else {
2132             log_error_write(srv, __FILE__, __LINE__, "ssbsBSBs",
2133               "response already sent out, but backend returned error",
2134               "on socket:", proc->connection_name, "for",
2135               con->uri.path, "?", con->uri.query, ", terminating connection");
2136         }
2137 
2138         if (hctx->backend_error) hctx->backend_error(hctx);
2139         http_response_backend_error(srv, con);
2140         gw_connection_close(srv, hctx);
2141         return HANDLER_FINISHED;
2142     }
2143 }
2144 
2145 
2146 static handler_t gw_handle_fdevent(server *srv, void *ctx, int revents) {
2147     gw_handler_ctx *hctx = ctx;
2148     connection *con = hctx->remote_conn;
2149 
2150     joblist_append(srv, con);
2151 
2152     if (revents & FDEVENT_IN) {
2153         handler_t rc = gw_recv_response(srv, hctx); /*(might invalidate hctx)*/
2154         if (rc != HANDLER_GO_ON) return rc;         /*(unless HANDLER_GO_ON)*/
2155     }
2156 
2157     if (revents & FDEVENT_OUT) {
2158         return gw_send_request(srv, hctx); /*(might invalidate hctx)*/
2159     }
2160 
2161     /* perhaps this issue is already handled */
2162     if (revents & (FDEVENT_HUP|FDEVENT_RDHUP)) {
2163         if (hctx->state == GW_STATE_CONNECT_DELAYED) {
2164             /* getoptsock will catch this one (right ?)
2165              *
2166              * if we are in connect we might get an EINPROGRESS
2167              * in the first call and an FDEVENT_HUP in the
2168              * second round
2169              *
2170              * FIXME: as it is a bit ugly.
2171              *
2172              */
2173             gw_send_request(srv, hctx);
2174         } else if (con->file_started) {
2175             /* drain any remaining data from kernel pipe buffers
2176              * even if (con->conf.stream_response_body
2177              *          & FDEVENT_STREAM_RESPONSE_BUFMIN)
2178              * since event loop will spin on fd FDEVENT_HUP event
2179              * until unregistered. */
2180             handler_t rc;
2181             const unsigned short flags = con->conf.stream_response_body;
2182             con->conf.stream_response_body &= ~FDEVENT_STREAM_RESPONSE_BUFMIN;
2183             con->conf.stream_response_body |= FDEVENT_STREAM_RESPONSE_POLLRDHUP;
2184             do {
2185                 rc = gw_recv_response(srv,hctx); /*(might invalidate hctx)*/
2186             } while (rc == HANDLER_GO_ON);       /*(unless HANDLER_GO_ON)*/
2187             con->conf.stream_response_body = flags;
2188             return rc; /* HANDLER_FINISHED or HANDLER_ERROR */
2189         } else {
2190             gw_proc *proc = hctx->proc;
2191             log_error_write(srv, __FILE__, __LINE__, "sBSbsbsd",
2192               "error: unexpected close of gw connection for",
2193               con->uri.path, "?", con->uri.query,
2194               "(no gw process on socket:", proc->connection_name, "?)",
2195               hctx->state);
2196 
2197             gw_connection_close(srv, hctx);
2198         }
2199     } else if (revents & FDEVENT_ERR) {
2200         log_error_write(srv, __FILE__, __LINE__, "s",
2201                         "gw: got a FDEVENT_ERR. Don't know why.");
2202 
2203         if (hctx->backend_error) hctx->backend_error(hctx);
2204         http_response_backend_error(srv, con);
2205         gw_connection_close(srv, hctx);
2206     }
2207 
2208     return HANDLER_FINISHED;
2209 }
2210 
2211 handler_t gw_check_extension(server *srv, connection *con, gw_plugin_data *p, int uri_path_handler, size_t hctx_sz) {
2212   #if 0 /*(caller must handle)*/
2213     if (con->mode != DIRECT) return HANDLER_GO_ON;
2214     gw_patch_connection(srv, con, p);
2215     if (NULL == p->conf.exts) return HANDLER_GO_ON;
2216   #endif
2217 
2218     buffer *fn = uri_path_handler ? con->uri.path : con->physical.path;
2219     size_t s_len = buffer_string_length(fn);
2220     gw_extension *extension = NULL;
2221     gw_host *host = NULL;
2222     gw_handler_ctx *hctx;
2223     unsigned short gw_mode;
2224 
2225     if (0 == s_len) return HANDLER_GO_ON; /*(not expected)*/
2226 
2227     /* check p->conf.exts_auth list and then p->conf.ext_resp list
2228      * (skip p->conf.exts_auth if array is empty
2229      *  or if GW_AUTHORIZER already ran in this request) */
2230     hctx = con->plugin_ctx[p->id];
2231     /*(hctx not NULL if GW_AUTHORIZER ran; hctx->ext_auth check is redundant)*/
2232     gw_mode = (NULL == hctx || NULL == hctx->ext_auth)
2233       ? 0              /*GW_AUTHORIZER p->conf.exts_auth will be searched next*/
2234       : GW_AUTHORIZER; /*GW_RESPONDER p->conf.exts_resp will be searched next*/
2235 
2236     do {
2237 
2238         gw_exts *exts;
2239         if (0 == gw_mode) {
2240             gw_mode = GW_AUTHORIZER;
2241             exts = p->conf.exts_auth;
2242         } else {
2243             gw_mode = GW_RESPONDER;
2244             exts = p->conf.exts_resp;
2245         }
2246 
2247         if (0 == exts->used) continue;
2248 
2249         /* gw.map-extensions maps extensions to existing gw.server entries
2250          *
2251          * gw.map-extensions = ( ".php3" => ".php" )
2252          *
2253          * gw.server = ( ".php" => ... )
2254          *
2255          * */
2256 
2257         /* check if extension-mapping matches */
2258         if (p->conf.ext_mapping) {
2259             data_string *ds =
2260               (data_string *)array_match_key_suffix(p->conf.ext_mapping, fn);
2261             if (NULL != ds) {
2262                 /* found a mapping */
2263                     /* check if we know the extension */
2264                     size_t k;
2265                     for (k = 0; k < exts->used; ++k) {
2266                         extension = exts->exts[k];
2267 
2268                         if (buffer_is_equal(ds->value, extension->key)) {
2269                             break;
2270                         }
2271                     }
2272 
2273                     if (k == exts->used) {
2274                         /* found nothing */
2275                         extension = NULL;
2276                     }
2277             }
2278         }
2279 
2280         if (extension == NULL) {
2281             size_t uri_path_len = buffer_string_length(con->uri.path);
2282 
2283             /* check if extension matches */
2284             for (size_t k = 0; k < exts->used; ++k) {
2285                 gw_extension *ext = exts->exts[k];
2286                 size_t ct_len = buffer_string_length(ext->key);
2287 
2288                 /* check _url_ in the form "/gw_pattern" */
2289                 if (ext->key->ptr[0] == '/') {
2290                     if (ct_len <= uri_path_len
2291                         && 0==memcmp(con->uri.path->ptr,ext->key->ptr,ct_len)){
2292                         extension = ext;
2293                         break;
2294                     }
2295                 } else if (ct_len <= s_len
2296                            && 0 == memcmp(fn->ptr + s_len - ct_len,
2297                                           ext->key->ptr, ct_len)) {
2298                     /* check extension in the form ".fcg" */
2299                     extension = ext;
2300                     break;
2301                 }
2302             }
2303         }
2304 
2305     } while (NULL == extension && gw_mode != GW_RESPONDER);
2306 
2307     /* extension doesn't match */
2308     if (NULL == extension) {
2309         return HANDLER_GO_ON;
2310     }
2311 
2312     /* check if we have at least one server for this extension up and running */
2313     host = gw_host_get(srv, con, extension, p->conf.balance, p->conf.debug);
2314     if (NULL == host) {
2315         return HANDLER_FINISHED;
2316     }
2317 
2318     /* a note about no handler is not sent yet */
2319     extension->note_is_sent = 0;
2320 
2321     /*
2322      * if check-local is disabled, use the uri.path handler
2323      *
2324      */
2325 
2326     /* init handler-context */
2327     if (uri_path_handler) {
2328         if (host->check_local != 0) {
2329             return HANDLER_GO_ON;
2330         } else {
2331             /* do not split path info for authorizer */
2332             if (gw_mode != GW_AUTHORIZER) {
2333                 /* the prefix is the SCRIPT_NAME,
2334                 * everything from start to the next slash
2335                 * this is important for check-local = "disable"
2336                 *
2337                 * if prefix = /admin.gw
2338                 *
2339                 * /admin.gw/foo/bar
2340                 *
2341                 * SCRIPT_NAME = /admin.gw
2342                 * PATH_INFO   = /foo/bar
2343                 *
2344                 * if prefix = /cgi-bin/
2345                 *
2346                 * /cgi-bin/foo/bar
2347                 *
2348                 * SCRIPT_NAME = /cgi-bin/foo
2349                 * PATH_INFO   = /bar
2350                 *
2351                 * if prefix = /, and fix-root-path-name is enable
2352                 *
2353                 * /cgi-bin/foo/bar
2354                 *
2355                 * SCRIPT_NAME = /cgi-bin/foo
2356                 * PATH_INFO   = /bar
2357                 *
2358                 */
2359                 char *pathinfo;
2360 
2361                 /* the rewrite is only done for /prefix/? matches */
2362                 if (host->fix_root_path_name && extension->key->ptr[0] == '/'
2363                                              && extension->key->ptr[1] == '\0'){
2364                     buffer_copy_buffer(con->request.pathinfo, con->uri.path);
2365                     buffer_clear(con->uri.path);
2366                 } else if (extension->key->ptr[0] == '/'
2367                            && buffer_string_length(con->uri.path)
2368                               > buffer_string_length(extension->key)
2369                            && (pathinfo =
2370                                  strchr(con->uri.path->ptr
2371                                         + buffer_string_length(extension->key),
2372                                         '/')) != NULL) {
2373                     /* rewrite uri.path and pathinfo */
2374 
2375                     buffer_copy_string(con->request.pathinfo, pathinfo);
2376                     buffer_string_set_length(
2377                       con->uri.path,
2378                       buffer_string_length(con->uri.path)
2379                       - buffer_string_length(con->request.pathinfo));
2380                 }
2381             }
2382         }
2383     }
2384 
2385     if (!hctx) hctx = handler_ctx_init(hctx_sz);
2386 
2387     hctx->remote_conn      = con;
2388     hctx->plugin_data      = p;
2389     hctx->host             = host;
2390     hctx->proc             = NULL;
2391     hctx->ext              = extension;
2392     gw_host_assign(srv, host);
2393 
2394     hctx->gw_mode = gw_mode;
2395     if (gw_mode == GW_AUTHORIZER) {
2396         hctx->ext_auth = hctx->ext;
2397     }
2398 
2399     /*hctx->conf.exts        = p->conf.exts;*/
2400     /*hctx->conf.exts_auth   = p->conf.exts_auth;*/
2401     /*hctx->conf.exts_resp   = p->conf.exts_resp;*/
2402     /*hctx->conf.ext_mapping = p->conf.ext_mapping;*/
2403     hctx->conf.balance     = p->conf.balance;
2404     hctx->conf.proto       = p->conf.proto;
2405     hctx->conf.debug       = p->conf.debug;
2406 
2407     hctx->opts.fdfmt = S_IFSOCK;
2408     hctx->opts.authorizer = (gw_mode == GW_AUTHORIZER);
2409     hctx->opts.local_redir = 0;
2410     hctx->opts.xsendfile_allow = host->xsendfile_allow;
2411     hctx->opts.xsendfile_docroot = host->xsendfile_docroot;
2412 
2413     con->plugin_ctx[p->id] = hctx;
2414 
2415     con->mode = p->id;
2416 
2417     if (con->conf.log_request_handling) {
2418         log_error_write(srv, __FILE__, __LINE__, "s", "handling it in mod_gw");
2419     }
2420 
2421     return HANDLER_GO_ON;
2422 }
2423 
2424 static void gw_handle_trigger_host(server *srv, gw_host *host, int debug) {
2425     /*
2426      * TODO:
2427      *
2428      * - add timeout for a connect to a non-gw process
2429      *   (use state_timestamp + state)
2430      *
2431      * perhaps we should kill a connect attempt after 10-15 seconds
2432      *
2433      * currently we wait for the TCP timeout which is 180 seconds on Linux
2434      */
2435 
2436     /* check each child proc to detect if proc exited */
2437 
2438     gw_proc *proc;
2439     time_t idle_timestamp;
2440     int overload = 1;
2441 
2442     for (proc = host->first; proc; proc = proc->next) {
2443         gw_proc_waitpid(srv, host, proc);
2444     }
2445 
2446     gw_restart_dead_procs(srv, host, debug, 1);
2447 
2448     /* check if adaptive spawning enabled */
2449     if (host->min_procs == host->max_procs) return;
2450     if (buffer_string_is_empty(host->bin_path)) return;
2451 
2452     for (proc = host->first; proc; proc = proc->next) {
2453         if (proc->load <= host->max_load_per_proc) {
2454             overload = 0;
2455             break;
2456         }
2457     }
2458 
2459     if (overload && host->num_procs && host->num_procs < host->max_procs) {
2460         /* overload, spawn new child */
2461         if (debug) {
2462             log_error_write(srv, __FILE__, __LINE__, "s",
2463                             "overload detected, spawning a new child");
2464         }
2465 
2466         gw_proc_spawn(srv, host, debug);
2467     }
2468 
2469     idle_timestamp = srv->cur_ts - host->idle_timeout;
2470     for (proc = host->first; proc; proc = proc->next) {
2471         if (host->num_procs <= host->min_procs) break;
2472         if (0 != proc->load) continue;
2473         if (proc->pid <= 0) continue;
2474         if (proc->last_used >= idle_timestamp) continue;
2475 
2476         /* terminate proc that has been idling for a long time */
2477         if (debug) {
2478             log_error_write(srv, __FILE__, __LINE__, "ssbsd",
2479                             "idle-timeout reached, terminating child:",
2480                             "socket:", proc->unixsocket, "pid", proc->pid);
2481         }
2482 
2483         gw_proc_kill(srv, host, proc);
2484 
2485         /* proc is now in unused, let next second handle next process */
2486         break;
2487     }
2488 
2489     for (proc = host->unused_procs; proc; proc = proc->next) {
2490         gw_proc_waitpid(srv, host, proc);
2491     }
2492 }
2493 
2494 static void gw_handle_trigger_exts(server *srv, gw_exts *exts, int debug) {
2495     for (size_t j = 0; j < exts->used; ++j) {
2496         gw_extension *ex = exts->exts[j];
2497         for (size_t n = 0; n < ex->used; ++n) {
2498             gw_handle_trigger_host(srv, ex->hosts[n], debug);
2499         }
2500     }
2501 }
2502 
2503 static void gw_handle_trigger_exts_wkr(server *srv, gw_exts *exts) {
2504     for (size_t j = 0; j < exts->used; ++j) {
2505         gw_extension * const ex = exts->exts[j];
2506         for (size_t n = 0; n < ex->used; ++n) {
2507             gw_host * const host = ex->hosts[n];
2508             for (gw_proc *proc = host->first; proc; proc = proc->next) {
2509                 if (proc->state == PROC_STATE_OVERLOADED)
2510                     gw_proc_check_enable(srv, host, proc);
2511             }
2512         }
2513     }
2514 }
2515 
2516 handler_t gw_handle_trigger(server *srv, void *p_d) {
2517     gw_plugin_data *p = p_d;
2518     int wkr = (0 != srv->srvconf.max_worker && p->srv_pid != srv->pid);
2519 
2520     for (size_t i = 0; i < srv->config_context->used; i++) {
2521         gw_plugin_config *conf = p->config_storage[i];
2522         gw_exts *exts = conf->exts;
2523         int debug = conf->debug ? conf->debug : p->config_storage[0]->debug;
2524         if (NULL == exts) continue;
2525         wkr
2526           ? gw_handle_trigger_exts_wkr(srv, exts)
2527           : gw_handle_trigger_exts(srv, exts, debug);
2528     }
2529 
2530     return HANDLER_GO_ON;
2531 }
2532 
2533 handler_t gw_handle_waitpid_cb(server *srv, void *p_d, pid_t pid, int status) {
2534     gw_plugin_data *p = p_d;
2535     if (0 != srv->srvconf.max_worker && p->srv_pid != srv->pid)
2536         return HANDLER_GO_ON;
2537 
2538     for (size_t i = 0; i < srv->config_context->used; ++i) {
2539         gw_plugin_config *conf = p->config_storage[i];
2540         gw_exts *exts = conf->exts;
2541         int debug = conf->debug ? conf->debug : p->config_storage[0]->debug;
2542         if (NULL == exts) continue;
2543         for (size_t j = 0; j < exts->used; ++j) {
2544             gw_extension *ex = exts->exts[j];
2545             for (size_t n = 0; n < ex->used; ++n) {
2546                 gw_host *host = ex->hosts[n];
2547                 gw_proc *proc;
2548                 for (proc = host->first; proc; proc = proc->next) {
2549                     if (!proc->is_local || proc->pid != pid) continue;
2550 
2551                     gw_proc_waitpid_log(srv, host, proc, status);
2552                     gw_proc_set_state(host, proc, PROC_STATE_DIED);
2553                     proc->pid = 0;
2554 
2555                     /* restart, but avoid spinning if child exits too quickly */
2556                     if (proc->disabled_until < srv->cur_ts) {
2557                         if (proc->state != PROC_STATE_KILLED)
2558                             proc->disabled_until = srv->cur_ts;
2559                         if (gw_spawn_connection(srv, host, proc, debug)) {
2560                             log_error_write(srv, __FILE__, __LINE__, "s",
2561                                             "ERROR: spawning gw failed.");
2562                         }
2563                     }
2564 
2565                     return HANDLER_FINISHED;
2566                 }
2567                 for (proc = host->unused_procs; proc; proc = proc->next) {
2568                     if (!proc->is_local || proc->pid != pid) continue;
2569 
2570                     gw_proc_waitpid_log(srv, host, proc, status);
2571                     if (proc->state != PROC_STATE_KILLED)
2572                         proc->disabled_until = srv->cur_ts;
2573                     gw_proc_set_state(host, proc, PROC_STATE_DIED);
2574                     proc->pid = 0;
2575                     return HANDLER_FINISHED;
2576                 }
2577             }
2578         }
2579     }
2580 
2581     return HANDLER_GO_ON;
2582 }
2583