server.c

00001 /*
00002  * Copyright (c) 2005-2012 by KoanLogic s.r.l. <http://www.koanlogic.com>
00003  * All rights reserved.
00004  *
00005  * This file is part of KLone, and as such it is subject to the license stated
00006  * in the LICENSE file which you have received as part of this distribution.
00007  *
00008  * $Id: server.c,v 1.70 2009/11/26 09:11:33 stewy Exp $
00009  */
00010 
00011 #include "klone_conf.h"
00012 #include <sys/types.h>
00013 #include <sys/stat.h>
00014 #ifdef HAVE_SYS_WAIT
00015 #include <sys/wait.h>
00016 #endif
00017 #include <u/libu.h>
00018 #include <stdlib.h>
00019 #include <unistd.h>
00020 #include <errno.h>
00021 #include <fcntl.h>
00022 #include <klone/server.h>
00023 #include <klone/backend.h>
00024 #include <klone/os.h>
00025 #include <klone/timer.h>
00026 #include <klone/context.h>
00027 #include <klone/ppc.h>
00028 #include <klone/ppc_cmd.h>
00029 #include <klone/utils.h>
00030 #include <klone/klog.h>
00031 #include <klone/hook.h>
00032 #include <klone/hookprv.h>
00033 #include <klone/server_ppc_cmd.h>
00034 #include <klone/emb.h>
00035 #include "server_s.h"
00036 #include "child.h"
00037 
00038 #define SERVER_MAX_BACKENDS 8
00039 
00040 enum watch_fd_e
00041 {
00042     WATCH_FD_READ   = 1 << 1,
00043     WATCH_FD_WRITE  = 1 << 2,
00044     WATCH_FD_EXCP   = 1 << 3
00045 };
00046 
00047 static void server_watch_fd(server_t *s, int fd, unsigned int mode);
00048 static void server_clear_fd(server_t *s, int fd, unsigned int mode);
00049 static void server_close_fd(server_t *s, int fd);
00050 
00051 static int server_be_listen(backend_t *be)
00052 {
00053     dbg_return_if (be == NULL, ~0);
00054     dbg_return_if (be->na == NULL, ~0);
00055 
00056     /* Return a socket descriptor bound to the configured endpoint. */
00057     warn_return_if ((be->ld = u_net_sd_by_addr(be->na)) == -1, ~0);
00058 
00059     return 0;
00060 }
00061 
00062 
00063 #ifdef OS_UNIX
00064 /* remove a child process whose pid is 'pid' to children list */
00065 static int server_reap_child(server_t *s, pid_t pid)
00066 {
00067     child_t *child;
00068     backend_t *be;
00069 
00070     dbg_err_if (s == NULL);
00071     
00072     /* get the child object */
00073     dbg_err_if(children_get_by_pid(s->children, pid, &child));
00074 
00075     /* remove the child from the list */
00076     dbg_err_if(children_del(s->children, child));
00077     be = child->be;
00078 
00079     /* check that the minimum number of process are active */
00080     be->nchild--;
00081     if(be->nchild < be->start_child)
00082         be->fork_child = be->start_child - be->nchild;
00083 
00084     U_FREE(child);
00085 
00086     return 0;
00087 err:
00088     return ~0;
00089 }
00090 
00091 /* add a child to the list */
00092 static int server_add_child(server_t *s, pid_t pid, backend_t *be)
00093 {
00094     child_t *child = NULL;
00095 
00096     dbg_err_if (s == NULL);
00097     dbg_err_if (be == NULL);
00098 
00099     dbg_err_if(child_create(pid, be, &child));
00100 
00101     dbg_err_if(children_add(s->children, child));
00102 
00103     be->nchild++;
00104 
00105     return 0;
00106 err:
00107     return ~0;
00108 }
00109 
00110 /* send 'sig' signal to all children process */
00111 static int server_signal_children(server_t *s, int sig)
00112 {
00113     child_t *child;
00114     ssize_t i;
00115 
00116     dbg_return_if (s == NULL, ~0);
00117     
00118     for(i = children_count(s->children) - 1; i >= 0; --i)
00119     {
00120         if(!children_getn(s->children, i, &child))
00121             dbg_err_if(kill(child->pid, sig) < 0);
00122     }
00123 
00124     return 0;
00125 err:
00126     dbg_strerror(errno);
00127     return ~0;
00128 }
00129 #endif
00130 
00131 static void server_term_children(server_t *s)
00132 {
00133     dbg_ifb(s == NULL) return;
00134 #ifdef OS_UNIX
00135     server_signal_children(s, SIGTERM);
00136 #endif
00137     return;
00138 }
00139 
00140 static void server_kill_children(server_t *s)
00141 {
00142     dbg_ifb(s == NULL) return;
00143 #ifdef OS_UNIX
00144     server_signal_children(s, SIGKILL);
00145 #endif
00146     return;
00147 }
00148 
00149 static void server_sigint(int sig)
00150 {
00151     u_unused_args(sig);
00152 
00153     u_warn("SIGINT");
00154 
00155     if(ctx && ctx->server)
00156         server_stop(ctx->server);
00157     
00158     emb_term();
00159 }
00160 
00161 static void server_sigterm(int sig)
00162 {
00163     u_unused_args(sig);
00164 
00165     /* child process die immediately.
00166      * note: don't call debug functions because the parent process could be
00167      * already dead if the user used the "killall kloned" command */
00168     if(ctx->pipc)
00169         _exit(0); 
00170 
00171     u_warn("SIGTERM");
00172 
00173     if(ctx && ctx->server)
00174         server_stop(ctx->server);
00175 }
00176 
00177 #ifdef OS_UNIX
00178 static void server_sigchld(int sig)
00179 {
00180     server_t *s = ctx->server;
00181 
00182     u_unused_args(sig);
00183 
00184     s->reap_children = 1;
00185 }
00186 
00187 static void server_waitpid(server_t *s)
00188 {
00189     pid_t pid = -1;
00190     int status;
00191 
00192     dbg_ifb(s == NULL) return;
00193     
00194     u_sig_block(SIGCHLD);
00195 
00196     /* detach from child processes */
00197     while((pid = waitpid(-1, &status, WNOHANG)) > 0) 
00198     {
00199         if(WIFEXITED(status) && WEXITSTATUS(status) != EXIT_SUCCESS)
00200             u_warn("pid [%u], exit code [%d]", pid, WEXITSTATUS(status));
00201 
00202         if(WIFSIGNALED(status))
00203             u_warn("pid [%u], signal [%d]", pid, WTERMSIG(status));
00204 
00205         /* decrement child count */
00206         server_reap_child(s, pid);
00207     }
00208 
00209     s->reap_children = 0;
00210 
00211     u_sig_unblock(SIGCHLD);
00212 }
00213 #endif
00214 
00215 static void server_recalc_hfd(server_t *s)
00216 {
00217     register int i;
00218     fd_set *prdfds, *pwrfds, *pexfds;
00219 
00220     dbg_ifb(s == NULL) return;
00221     
00222     prdfds = &s->rdfds;
00223     pwrfds = &s->wrfds;
00224     pexfds = &s->exfds;
00225 
00226     /* set s->hfd to highest value */
00227     for(i = s->hfd, s->hfd = 0; i > 0; --i)
00228     {
00229         if(FD_ISSET(i, prdfds) || FD_ISSET(i, pwrfds) || FD_ISSET(i, pexfds))
00230         {
00231             s->hfd = i;
00232             break;
00233         }
00234     }
00235 }
00236 
00237 static void server_clear_fd(server_t *s, int fd, unsigned int mode)
00238 {
00239     dbg_ifb(s == NULL) return;
00240 
00241     if(mode & WATCH_FD_READ)
00242         FD_CLR(fd, &s->rdfds);
00243 
00244     if(mode & WATCH_FD_WRITE)
00245         FD_CLR(fd, &s->wrfds);
00246 
00247     if(mode & WATCH_FD_EXCP)
00248         FD_CLR(fd, &s->exfds);
00249 
00250     server_recalc_hfd(s);
00251 }
00252 
00253 static void server_watch_fd(server_t *s, int fd, unsigned int mode)
00254 {
00255     dbg_ifb(s == NULL) return;
00256     dbg_ifb(fd < 0) return;
00257 
00258     if(mode & WATCH_FD_READ)
00259         FD_SET(fd, &s->rdfds);
00260 
00261     if(mode & WATCH_FD_WRITE)
00262         FD_SET(fd, &s->wrfds);
00263 
00264     if(mode & WATCH_FD_EXCP)
00265         FD_SET(fd, &s->exfds);
00266 
00267     s->hfd = U_MAX(s->hfd, fd);
00268 }
00269 
00270 static void server_close_fd(server_t *s, int fd)
00271 {
00272     dbg_ifb(s == NULL) return;
00273     dbg_ifb(fd < 0) return;
00274 
00275     server_clear_fd(s, fd, WATCH_FD_READ | WATCH_FD_WRITE | WATCH_FD_EXCP);
00276     close(fd);
00277 }
00278 
00279 static int server_be_accept(server_t *s, backend_t *be, int *pfd)
00280 {
00281     struct sockaddr sa;
00282     int sa_len = sizeof(struct sockaddr);
00283     int ad;
00284 
00285     u_unused_args(s);
00286 
00287     dbg_return_if (be == NULL, ~0);
00288     dbg_return_if (pfd == NULL, ~0);
00289 
00290 again:
00291     ad = accept(be->ld, &sa, &sa_len);
00292     if(ad == -1 && errno == EINTR)
00293         goto again; /* interrupted */
00294     dbg_err_if(ad == -1); /* accept error */
00295 
00296     *pfd = ad;
00297 
00298     return 0;
00299 err:
00300     if(ad < 0)
00301         warn_strerror(errno);
00302     return ~0;
00303 }
00304 
00305 static int server_backend_detach(server_t *s, backend_t *be)
00306 {
00307     s->nbackend--;
00308 
00309     dbg_return_if (s == NULL, ~0);
00310     dbg_return_if (be == NULL, ~0);
00311 
00312     u_net_addr_free(be->na);
00313     be->server = NULL;
00314     be->na = NULL;
00315     be->config = NULL;
00316 
00317     close(be->ld);
00318     be->ld = -1;
00319 
00320     backend_free(be);
00321 
00322     return 0;
00323 }
00324 
00325 #ifdef OS_UNIX
00326 static int server_chroot_to(server_t *s, const char *dir)
00327 {
00328     dbg_return_if (s == NULL, ~0);
00329     dbg_return_if (dir == NULL, ~0);
00330 
00331     u_unused_args(s);
00332 
00333     dbg_err_if(chroot(dir));
00334 
00335     dbg_err_if(chdir("/"));
00336 
00337     u_info("chroot'd: %s", dir);
00338 
00339     return 0;
00340 err:
00341     warn_strerror(errno);
00342     return ~0;
00343 }
00344 
00345 static int server_foreach_cb(struct dirent *d, const char *path, void *arg)
00346 {
00347     int *pfound = (int*)arg;
00348 
00349     u_unused_args(d, path);
00350 
00351     *pfound = 1;
00352 
00353     return ~0;
00354 }
00355 
00356 static int server_chroot_blind(server_t *s)
00357 {
00358 #ifdef HAVE_FORK
00359     enum { BLIND_DIR_MODE = 0100 }; /* blind dir mode must be 0100 */
00360     char dir[U_PATH_MAX];
00361     struct stat st;
00362     int fd_dir = -1, found;
00363     pid_t child;
00364     unsigned int mask;
00365 
00366     dbg_err_if (s == NULL);
00367     dbg_err_if (s->chroot == NULL);
00368 
00369     dbg_err_if(u_path_snprintf(dir, U_PATH_MAX, U_PATH_SEPARATOR,
00370         "%s/kloned_blind_chroot_%d.dir", s->chroot, getpid()));
00371 
00372     /* create the blind dir (0100 mode) */
00373     dbg_err_if(mkdir(dir, BLIND_DIR_MODE ));
00374 
00375     /* get the fd of the dir */
00376     dbg_err_if((fd_dir = open(dir, O_RDONLY, 0)) < 0);
00377 
00378     dbg_err_if((child = fork()) < 0);
00379 
00380     if(child == 0)
00381     {   /* child */
00382 
00383         /* delete the chroot dir and exit */
00384         sleep(1); // FIXME use a lock here
00385         u_dbg("[child] removing dir: %s\n", dir);
00386         rmdir(dir);
00387         _exit(0);
00388     }
00389     /* parent */
00390 
00391     /* do chroot */
00392     dbg_err_if(server_chroot_to(s, dir));
00393 
00394     /* do some dir sanity checks */
00395 
00396     /* get stat values */
00397     dbg_err_if(fstat(fd_dir, &st));
00398 
00399     /* the dir owned must be root */
00400     dbg_err_if(st.st_gid || st.st_uid);
00401 
00402     /* the dir mode must be 0100 */
00403     dbg_err_if((st.st_mode & 07777) != BLIND_DIR_MODE);
00404 
00405     /* the dir must be empty */
00406     found = 0;
00407     mask = S_IFIFO | S_IFCHR | S_IFDIR | S_IFBLK | S_IFREG | S_IFLNK | S_IFSOCK;
00408     dbg_err_if(u_foreach_dir_item("/", mask, server_foreach_cb, &found));
00409 
00410     /* bail out if the dir is not empty */
00411     dbg_err_if(found);
00412 
00413     close(fd_dir);
00414 
00415     return 0;
00416 err:
00417     if(fd_dir >= 0)
00418         close(fd_dir);
00419     warn_strerror(errno);
00420     return ~0;
00421 #else   /* !HAVE_FORK */
00422     u_unused_args(s);
00423     err("Blind chroot could not be honoured since fork(2) unavailable");
00424     return ~0;
00425 #endif  /* HAVE_FORK */
00426 }
00427 
00428 static int server_chroot(server_t *s)
00429 {
00430     dbg_return_if (s == NULL, ~0);
00431 
00432     if(s->blind_chroot)
00433         return server_chroot_blind(s);
00434     else
00435         return server_chroot_to(s, s->chroot);
00436 }
00437 
00438 static int server_drop_privileges(server_t *s)
00439 {
00440     uid_t uid;
00441     gid_t gid;
00442 
00443     dbg_return_if (s == NULL, ~0);
00444 
00445     if(s->gid > 0)
00446     {
00447         gid = (gid_t)s->gid;
00448 
00449         /* remove all groups except gid */
00450         dbg_err_if(setgroups(1, &gid));
00451 
00452         /* set gid */
00453         dbg_err_if(setgid(gid));
00454         dbg_err_if(setegid(gid));
00455 
00456         /* verify */
00457         dbg_err_if(getgid() != gid || getegid() != gid);
00458     }
00459 
00460     if(s->uid > 0)
00461     {
00462         uid = (uid_t)s->uid;
00463 
00464         /* set uid */
00465         dbg_err_if(setuid(uid));
00466         dbg_err_if(seteuid(uid));
00467 
00468         /* verify */
00469         dbg_err_if(getuid() != uid || geteuid() != uid);
00470     }
00471     
00472     return 0;
00473 err:
00474     warn_strerror(errno);
00475     return ~0;
00476 }
00477 
00478 static int server_fork_child(server_t *s, backend_t *be)
00479 {
00480 #ifdef HAVE_FORK
00481     backend_t *obe; /* other backed */
00482     pid_t child;
00483     int socks[2];
00484 
00485     dbg_return_if (s == NULL, -1);
00486     dbg_return_if (be == NULL, -1);
00487     /* exit on too much children */
00488     dbg_return_if (children_count(s->children) == s->max_child, -1);
00489     dbg_return_if (be->nchild == be->max_child, -1);
00490 
00491     /* create a parent<->child IPC channel */
00492     dbg_err_if(socketpair(AF_UNIX, SOCK_STREAM, 0, socks) < 0);
00493 
00494     if((child = fork()) == 0)
00495     {   /* child */
00496 
00497         /* never flush, the parent process will */
00498         s->klog_flush = 0;
00499 
00500         /* reseed the PRNG */
00501         srand(rand() + getpid() + time(0));
00502 
00503         /* close one end of the channel */
00504         close(socks[0]);
00505 
00506         /* save parent PPC socket and close the other */
00507         ctx->pipc = socks[1];
00508         ctx->backend = be;
00509 
00510         /* close listening sockets of other backends */
00511         LIST_FOREACH(obe, &s->bes, np)
00512         {
00513             if(obe == be)
00514                 continue;
00515             close(obe->ld);
00516             obe->ld = -1;
00517         }
00518 
00519         /* clear child copy of children list */
00520         dbg_err_if(children_clear(s->children));
00521 
00522     } else if(child > 0) {
00523         /* parent */
00524 
00525         /* save child pid and increment child count */
00526         server_add_child(s, child, be);
00527 
00528         /* close one end of the channel */
00529         close(socks[1]);
00530 
00531         /* watch the PPC socket connected to the child */
00532         server_watch_fd(s, socks[0], WATCH_FD_READ);
00533     } else {
00534         warn_err("fork error");
00535     }
00536 
00537     return child;
00538 err:
00539     warn_strerror(errno);
00540     return -1;
00541 #else   /* !HAVE_FORK */
00542     u_unused_args(s, be);
00543     u_warn("Only iterative mode is enabled (fork(2) unsupported by target OS)");
00544     return -1;
00545 #endif  /* HAVE_FORK */
00546 }
00547 
00548 static int server_child_serve(server_t *s, backend_t *be, int ad)
00549 {
00550     pid_t child;
00551 
00552     dbg_return_if (s == NULL, ~0);
00553     dbg_return_if (be == NULL, ~0);
00554 
00555     dbg_err_if((child = server_fork_child(s, be)) < 0);
00556 
00557     if(child == 0)
00558     {   /* child */
00559 
00560         /* close this be listening descriptor */
00561         close(be->ld);
00562 
00563         hook_call(child_init);
00564 
00565         /* serve the page */
00566         dbg_if(backend_serve(be, ad));
00567 
00568         hook_call(child_term);
00569 
00570         /* close client socket and die */
00571         close(ad);
00572         server_stop(be->server); 
00573     }
00574     /* parent */
00575 
00576     return 0;
00577 err:
00578     warn_strerror(errno);
00579     return ~0;
00580 }
00581 
00582 static int server_cb_spawn_child(talarm_t *al, void *arg)
00583 {
00584     server_t *s = (server_t*)arg;
00585 
00586     u_unused_args(al);
00587 
00588     dbg_err_if (s == NULL);
00589 
00590     /* must be called by a child process */
00591     dbg_err_if(ctx->backend == NULL || ctx->pipc == 0);
00592 
00593     /* ask the parent to create a new worker child process */
00594     dbg_err_if(server_ppc_cmd_fork_child(s, ctx->backend));
00595 
00596     /* mark the current child process so it will die when finishes 
00597        serving this page */
00598     server_stop(s);
00599 
00600     return 0;
00601 err:
00602     return ~0;
00603 }
00604 #endif /* ifdef OS_UNIX */
00605 
00606 static int server_be_serve(server_t *s, backend_t *be, int ad)
00607 {
00608     talarm_t *al = NULL;
00609 
00610     dbg_err_if (s == NULL);
00611     dbg_err_if (be == NULL);
00612     
00613     switch(be->model)
00614     {
00615 #if defined(OS_UNIX) && defined(HAVE_FORK)
00616     case SERVER_MODEL_FORK:
00617         /* spawn a child to handle the request */
00618         dbg_err_if(server_child_serve(s, be, ad));
00619         break;
00620 
00621     case SERVER_MODEL_PREFORK: 
00622         /* FIXME lower timeout value may be needed */
00623         /* if _serve takes more then 1 second spawn a new worker process */
00624         dbg_err_if(timerm_add(1, server_cb_spawn_child, (void*)s, &al));
00625 
00626         /* serve the page */
00627         dbg_if(backend_serve(be, ad));
00628 
00629         /* remove and free the alarm */
00630         timerm_del(al); /* prefork */
00631         break;
00632 #endif  /* OS_UNIX && HAVE_FORK */
00633 
00634     case SERVER_MODEL_ITERATIVE:
00635         /* serve the page */
00636         dbg_if(backend_serve(be, ad));
00637         break;
00638 
00639     default:
00640         warn_err_if("server model not supported");
00641     }
00642 
00643     /* close the accepted (already served) socket */
00644     close(ad);
00645 
00646     return 0;
00647 err:
00648     close(ad);
00649     return ~0;
00650 }
00651 
00652 int server_stop(server_t *s)
00653 {
00654     dbg_err_if (s == NULL);
00655     
00656     if(ctx->pipc)
00657     {   /* child process */
00658 
00659         dbg_err_if(ctx->backend == NULL);
00660 
00661         /* close child listening sockets to force accept(2) to exit */
00662         close(ctx->backend->ld);
00663     }
00664 
00665     /* stop the parent process */
00666     s->stop = 1;
00667 
00668     return 0;
00669 err:
00670     return ~0;
00671 }
00672 
00673 static int server_listen(server_t *s)
00674 {
00675     backend_t *be;
00676 
00677     dbg_err_if (s == NULL);
00678     
00679     LIST_FOREACH(be, &s->bes, np)
00680     {
00681         dbg_err_if(server_be_listen(be));
00682 
00683         /* watch the listening socket */
00684         if(be->model != SERVER_MODEL_PREFORK)
00685             server_watch_fd(s, be->ld, WATCH_FD_READ);
00686     }
00687 
00688     return 0;
00689 err:
00690     return ~0;
00691 }
00692 
00693 int server_cgi(server_t *s)
00694 {
00695     backend_t *be;
00696 
00697     dbg_err_if (s == NULL);
00698 
00699     /* use the first http backend as the CGI backend */
00700     LIST_FOREACH(be, &s->bes, np)
00701     {
00702         if(strcasecmp(be->proto, "http") == 0)
00703         {
00704             hook_call(server_init);
00705 
00706             dbg_if(backend_serve(be, 0));
00707 
00708             hook_call(server_term);
00709 
00710             return 0;
00711         }
00712     }
00713 
00714 err: /* fall through if search loop exhausted */
00715     return ~0;
00716 }
00717 
00718 ppc_t* server_get_ppc(server_t *s)
00719 {
00720     dbg_return_if (s == NULL, NULL);
00721 
00722     return s->ppc;
00723 }
00724 
00725 static int server_process_ppc(server_t *s, int fd)
00726 {
00727     unsigned char cmd;
00728     char data[PPC_MAX_DATA_SIZE];
00729     ssize_t n;
00730 
00731     dbg_err_if (s == NULL);
00732     dbg_err_if (fd < 0);
00733 
00734     /* get a ppc request */
00735     n = ppc_read(s->ppc, fd, &cmd, data, PPC_MAX_DATA_SIZE); 
00736     if(n > 0)
00737     {   
00738         /* process a ppc (parent procedure call) request */
00739         dbg_err_if(ppc_dispatch(s->ppc, fd, cmd, data, n));
00740     } else if(n == 0) {
00741         /* child has exit or closed the channel. close our side of the sock 
00742            and remove it from the watch list */
00743         server_close_fd(s, fd);
00744     } else {
00745         /* ppc error. close fd and remove it from the watch list */
00746         server_close_fd(s, fd);
00747     }
00748 
00749     return 0;
00750 err:
00751     return ~0;
00752 }
00753 
00754 static int server_dispatch(server_t *s, int fd)
00755 {
00756     backend_t *be;
00757     int ad = -1; 
00758 
00759     dbg_err_if (s == NULL);
00760 
00761     /* find the backend that listen on fd */
00762     LIST_FOREACH(be, &s->bes, np)
00763         if(be->ld == fd)
00764             break;
00765 
00766     if(be == NULL) /* a child is ppc-calling */
00767         return server_process_ppc(s, fd);
00768 
00769     /* accept the pending connection */
00770     dbg_err_if(server_be_accept(s, be, &ad));
00771 
00772     /* Disable Nagle on the socket. Note that this may fail if the socket is 
00773      * in the UNIX family or it is using the SCTP transport instead of TCP. */
00774     (void) u_net_nagle_off(ad);
00775 
00776     /* serve the page */
00777     dbg_err_if(server_be_serve(s, be, ad));
00778 
00779     return 0;
00780 err:
00781     U_CLOSE(ad);
00782     return ~0;
00783 }
00784 
00785 int server_cb_klog_flush(talarm_t *a, void *arg)
00786 {
00787     server_t *s = (server_t*)arg;
00788 
00789     u_unused_args(a);
00790 
00791     dbg_return_if (s == NULL, ~0);
00792 
00793     /* set a flag to flush the klog object in server_loop */
00794     s->klog_flush++;
00795 
00796     return 0;
00797 }
00798 
00799 #ifdef OS_UNIX
00800 int server_spawn_child(server_t *s, backend_t *be)
00801 {
00802     size_t c;
00803     int rc;
00804 
00805     dbg_err_if (s == NULL);
00806     dbg_err_if (be == NULL);
00807 
00808     dbg_err_if((rc = server_fork_child(s, be)) < 0);
00809     if(rc > 0)
00810         return 0; /* parent */
00811 
00812     /* call the hook that runs the on-child user code */
00813     hook_call(child_init);
00814 
00815     /* child main loop: 
00816        close on s->stop or if max # of request limit has reached (the 
00817        server will respawn a new process if needed) */
00818     for(c = 0; !s->stop && c < be->max_rq_xchild; ++c)
00819     {
00820         /* wait for a new client (will block on accept(2)) */
00821         dbg_err_if(server_dispatch(s, be->ld));
00822     }
00823 
00824     /* before child shutdowns call the term hook */
00825     hook_call(child_term);
00826 
00827     server_stop(s);
00828 
00829     return 0;
00830 err:
00831     return ~0;
00832 }
00833 
00834 /* spawn pre-fork child processes */
00835 static int server_spawn_children(server_t *s)
00836 {
00837     backend_t *be;
00838     register size_t i;
00839 
00840     dbg_err_if (s == NULL);
00841 
00842     /* spawn N child process that will sleep asap into accept(2) */
00843     LIST_FOREACH (be, &s->bes, np)
00844     {
00845         if(be->model != SERVER_MODEL_PREFORK || be->fork_child == 0)
00846             continue;
00847 
00848         /* spawn be->fork_child child processes */
00849         for(i = 0; i < be->fork_child; ++i)
00850         {
00851             dbg_err_if(server_spawn_child(s, be));
00852             /* child context? */
00853             if(ctx->pipc)
00854                 break; /* the child previously spawned is dying, exit */
00855             be->fork_child--;
00856         }
00857     }
00858 
00859     return 0;
00860 err:
00861     return ~0;
00862 }
00863 #endif
00864 
00865 int server_loop(server_t *s)
00866 {
00867     struct timeval tv;
00868     int rc, fd;
00869     fd_set rdfds, wrfds, exfds;
00870 
00871     dbg_err_if (s == NULL);
00872     dbg_err_if (s->config == NULL);
00873 
00874     dbg_err_if(server_listen(s));
00875 
00876 #ifdef OS_UNIX
00877     /* if it's configured chroot to the dst dir */
00878     if(s->chroot)
00879         dbg_err_if(server_chroot(s));
00880 
00881     /* set uid/gid to non-root user */
00882     warn_err_sifm(server_drop_privileges(s), "unable to drop priviledges");
00883 
00884     /* if allow_root is not set check that we're not running as root */
00885     if(!s->allow_root)
00886         warn_err_ifm(!getuid() || !geteuid() || !getgid() || !getegid(),
00887             "you must set the allow_root config option to run kloned as root");
00888 
00889 #elif OS_WIN
00890     if(s->chroot)
00891         dbg_err_if(SetCurrentDirectory(s->chroot) == 0);
00892 
00893 #endif
00894 
00895     /* server startup hook */
00896     hook_call(server_init);
00897 
00898     for(; !s->stop; )
00899     {
00900 #ifdef OS_UNIX
00901         /* spawn new child if needed (may fail on resource limits) */
00902         dbg_if(server_spawn_children(s));
00903 #endif
00904 
00905         /* children in pre-fork mode exit here */
00906         if(ctx->pipc)
00907             break;
00908 
00909         memcpy(&rdfds, &s->rdfds, sizeof(fd_set));
00910         memcpy(&wrfds, &s->wrfds, sizeof(fd_set));
00911         memcpy(&exfds, &s->exfds, sizeof(fd_set));
00912 
00913     again:
00914         /* wake up every second */
00915         tv.tv_sec = 1; tv.tv_usec = 0;
00916 
00917         rc = select(1 + s->hfd, &rdfds, &wrfds, &exfds, &tv); 
00918         if(rc == -1 && errno == EINTR)
00919             goto again; /* interrupted */
00920         dbg_err_if(rc == -1); /* select error */
00921 
00922 #ifdef OS_UNIX
00923         if(s->reap_children)
00924             server_waitpid(s);
00925 #endif
00926 
00927         /* parent only */
00928         if(ctx->pipc == 0)
00929         {
00930             /* call klog_flush if flush timeout has expired and select() timeouts */
00931             if(s->klog_flush)
00932             {
00933                 /* flush the log buffer */
00934                 klog_flush(s->klog);
00935 
00936                 /* reset the flag */
00937                 s->klog_flush = 0;
00938 
00939                 U_FREE(s->al_klog_flush);
00940 
00941                 /* re-set the timer */
00942                 dbg_err_if(timerm_add(SERVER_LOG_FLUSH_TIMEOUT,
00943                     server_cb_klog_flush, s, &s->al_klog_flush));
00944             }
00945 
00946             /* server loop hook - trigger only upon timeout and not upon client request */
00947             if(rc == 0)
00948                 hook_call(server_loop);
00949         }
00950 
00951         /* for each signaled listening descriptor */
00952         for(fd = 0; rc && fd < 1 + s->hfd; ++fd)
00953         { 
00954             if(FD_ISSET(fd, &rdfds))
00955             {
00956                 --rc;
00957                 /* dispatch the request to the right backend */
00958                 dbg_if(server_dispatch(s, fd));
00959             } 
00960         } /* for each ready fd */
00961 
00962     } /* !s->stop */
00963 
00964     /* children in fork mode exit here */
00965     if(ctx->pipc)
00966         return 0;
00967 
00968     /* server shutdown hook */
00969     hook_call(server_term);
00970 
00971     /* shutdown all children */
00972     server_term_children(s);
00973 
00974     sleep(1);
00975 
00976     /* brute kill children process */
00977     if(s->nchild)
00978         server_kill_children(s);
00979 
00980     return 0;
00981 err:
00982     return ~0;
00983 }
00984 
00985 int server_free(server_t *s)
00986 {
00987     backend_t *be;
00988 
00989     dbg_err_if (s == NULL);
00990 
00991     /* remove the hook (that needs the server_t object) */
00992     u_log_set_hook(NULL, NULL, NULL, NULL);
00993 
00994     /* remove klog flushing alarm */
00995     if(s->al_klog_flush)
00996     {
00997         timerm_del(s->al_klog_flush);
00998         s->al_klog_flush = NULL;
00999     }
01000 
01001     if(s->klog)
01002     {
01003         /* child processes must not close klog when in 'file' mode, because 
01004            klog_file_t will flush data that the parent already flushed 
01005            (children inherit a "used" FILE* that will usually contain, on close,
01006            not-empty buffer that fclose (called by exit()) flushes). same 
01007            thing may happens with different log devices when buffers are used.
01008          */
01009         if(ctx->pipc == 0)
01010             klog_close(s->klog);
01011         s->klog = NULL;
01012     }
01013 
01014     while((be = LIST_FIRST(&s->bes)) != NULL)
01015     {
01016         LIST_REMOVE(be, np);
01017         server_backend_detach(s, be);
01018     }
01019 
01020     dbg_if(ppc_free(s->ppc));
01021 
01022     dbg_if(children_free(s->children));
01023 
01024 #ifdef OS_WIN
01025     WSACleanup();
01026 #endif
01027 
01028     U_FREE(s);
01029     return 0;
01030 err:
01031     return ~0;
01032 }
01033 
01034 static int server_setup_backend(server_t *s, backend_t *be)
01035 {
01036     const char *a;
01037 
01038     dbg_return_if (s == NULL, ~0);
01039     dbg_return_if (be == NULL, ~0);
01040     
01041     /* server count */
01042     s->nbackend++;
01043 
01044     /* Get 'addr' value from config.  Expect it to be given in libu::net
01045      * URI format, e.g. something in between 'tcp4://192.168.0.1:80' and 
01046      * 'tcp6://[*]:8080'. */
01047     warn_err_ifm ((a = u_config_get_subkey_value(be->config, "addr")) == NULL,
01048         "missing or bad '<servname>.addr' value");
01049 
01050     /* Parse and internalize it. */
01051     warn_err_ifm (u_net_uri2addr(a, U_NET_SSOCK, &be->na), 
01052             "bad syntax for 'addr' value");
01053 
01054     return 0;
01055 err:
01056     u_warn("'addr' syntax has changed with klone 3: check libu URI format");
01057     return ~0;
01058 }
01059 
01060 static int server_log_hook(void *arg, int level, const char *str)
01061 {
01062     server_t *s = (server_t*)arg;
01063     u_log_hook_t old = NULL;
01064     void *old_arg = NULL;
01065 
01066     dbg_err_if (s == NULL);
01067     dbg_err_if (str == NULL);
01068  
01069     /* if both the server and the calling backend have no log then exit */
01070     if(s->klog == NULL && (ctx->backend == NULL || ctx->backend->klog == NULL))
01071         return 0; /* log is disabled */
01072 
01073     /* disable log hooking in the hook itself otherwise an infinite loop 
01074        may happen if a log function is called from inside the hook */
01075     u_log_set_hook(NULL, NULL, &old, &old_arg);
01076 
01077     /* syslog klog doesn't go through ppc */
01078     if(s->klog->type == KLOG_TYPE_SYSLOG || ctx->pipc == 0)
01079     {   /* syslog klog or parent context */
01080         if(s->klog)
01081             dbg_err_if(klog(s->klog, syslog_to_klog(level), "%s", str));
01082     } else {
01083         /* children context */
01084         dbg_err_if(server_ppc_cmd_log_add(s, level, str));
01085     }
01086 
01087     /* re-set the old hook */
01088     u_log_set_hook(old, old_arg, NULL, NULL);
01089 
01090     return 0;
01091 err:
01092     if(old)
01093         u_log_set_hook(old, old_arg, NULL, NULL);
01094     return ~0;
01095 }
01096 
01097 int server_get_logger(server_t *s, klog_t **pkl)
01098 {
01099     klog_t *kl = NULL;
01100 
01101     dbg_err_if (s == NULL);
01102     dbg_err_if (pkl == NULL);
01103  
01104     if(ctx->backend)
01105         kl = ctx->backend->klog; /* may be NULL */
01106 
01107     if(kl == NULL)
01108         kl = s->klog; /* may be NULL */
01109 
01110     *pkl = kl;
01111 
01112     return 0;
01113 err:
01114     return ~0;
01115 }
01116 
01117 static int server_get_klog_line(server_t *s, klog_t *kl, size_t i, char *line)
01118 {
01119     backend_t *be = ctx->backend;
01120 
01121     dbg_err_if(kl->type != KLOG_TYPE_MEM);
01122     dbg_err_if(be == NULL);
01123 
01124     /* we need ppc just in prefork mode */
01125     if(be->model != SERVER_MODEL_PREFORK)
01126     {
01127         dbg_err_if(klog_getln(kl, i, line));
01128         return 0;
01129     }
01130 
01131     /* send the ppc command and read back the response */
01132     nop_err_if(server_ppc_cmd_log_get(s, i, line));
01133 
01134     return 0;
01135 err:
01136     return ~0;
01137 }
01138 
01139 int server_foreach_memlog_line(server_t *s, 
01140     int (*cb)(const char*, void*), void *arg)
01141 {
01142     klog_t *kl = NULL;  
01143     size_t i;
01144     char line[KLOG_LN_SZ];
01145 
01146     /* get the configured klog object and check that's a in-memory klog */
01147     if(server_get_logger(s, &kl) || kl == NULL || kl->type != KLOG_TYPE_MEM)
01148     {
01149         cb("logging is not configured or is not a in-memory log", arg);
01150         return ~0;
01151     }
01152 
01153     /* for each log line call the user-given callback function */
01154     for(i = 1; server_get_klog_line(s, kl, i, line) == 0; ++i)
01155         cb(line, arg);
01156 
01157     return 0;
01158 }
01159 
01160 
01161 int server_get_backend_by_id(server_t *s, int id, backend_t **pbe)
01162 {
01163     backend_t *be;
01164 
01165     dbg_err_if (s == NULL);
01166     dbg_err_if (pbe == NULL);
01167     
01168     LIST_FOREACH(be, &s->bes, np)
01169     {
01170         if(be->id == id)
01171         {
01172             *pbe = be;
01173             return 0;
01174         }
01175     }
01176 
01177 err: /* fall through if search loop exhausted */
01178     return ~0;
01179 }
01180 
01181 int server_create(u_config_t *config, int foreground, server_t **ps)
01182 {
01183     server_t *s = NULL;
01184     u_config_t *bekey = NULL, *log_c = NULL;
01185     backend_t *be = NULL;
01186     const char *list, *type;
01187     char *n = NULL, *name = NULL;
01188     int i, id, iv;
01189 
01190     dbg_return_if (ps == NULL, ~0);
01191     dbg_return_if (config == NULL, ~0);
01192 
01193 #ifdef OS_WIN
01194     WORD ver;
01195     WSADATA wsadata;
01196 
01197     ver = MAKEWORD(1,1);
01198     dbg_err_if(WSAStartup(ver, &wsadata));
01199 #endif
01200 
01201     s = u_zalloc(sizeof(server_t));
01202     dbg_err_if(s == NULL);
01203 
01204     *ps = s; /* we need it before backend inits */
01205 
01206     s->config = config;
01207     s->model = SERVER_MODEL_FORK; /* default */
01208 
01209     dbg_err_if(children_create(&s->children));
01210 
01211     /* init fd_set */
01212     FD_ZERO(&s->rdfds);
01213     FD_ZERO(&s->wrfds);
01214     FD_ZERO(&s->exfds);
01215 
01216     /* init backend list */
01217     LIST_INIT(&s->bes);
01218 
01219     dbg_err_if(ppc_create(&s->ppc));
01220 
01221     /* create the log device if requested */
01222     if(!u_config_get_subkey(config, "log", &log_c))
01223     {
01224         dbg_if(klog_open_from_config(log_c, &s->klog));
01225         s->klog_flush = 1;
01226     }
01227 
01228     /* register the log ppc callbacks */
01229     dbg_err_if(ppc_register(s->ppc, PPC_CMD_NOP, server_ppc_cb_nop, s));
01230     dbg_err_if(ppc_register(s->ppc, PPC_CMD_LOG_ADD, server_ppc_cb_log_add, s));
01231     dbg_err_if(ppc_register(s->ppc, PPC_CMD_LOG_GET, server_ppc_cb_log_get, s));
01232 #ifdef OS_UNIX
01233     dbg_err_if(ppc_register(s->ppc, PPC_CMD_FORK_CHILD, 
01234         server_ppc_cb_fork_child, s));
01235 #endif
01236     dbg_err_if(ppc_register(s->ppc, PPC_CMD_ACCESS_LOG, 
01237                 server_ppc_cb_access_log, s));
01238 
01239     /* redirect logs to the server_log_hook function */
01240     dbg_err_if(u_log_set_hook(server_log_hook, s, NULL, NULL));
01241 
01242     /* parse server list and build s->bes */
01243     list = u_config_get_subkey_value(config, "server_list");
01244     warn_err_ifm(list == NULL, "bad or missing 'server_list' config param");
01245 
01246     /* chroot, uid and gid */
01247     s->chroot = u_config_get_subkey_value(config, "chroot");
01248     dbg_err_if(u_config_get_subkey_value_i(config, "uid", -1, &s->uid));
01249     dbg_err_if(u_config_get_subkey_value_i(config, "gid", -1, &s->gid));
01250     dbg_err_if(u_config_get_subkey_value_b(config, "allow_root", 0, 
01251         &s->allow_root));
01252     dbg_err_if(u_config_get_subkey_value_b(config, "blind_chroot", 0, 
01253         &s->blind_chroot));
01254 
01255     warn_err_ifm(!s->uid || !s->gid, 
01256         "you must set uid and gid config parameters");
01257 
01258     dbg_err_if(u_config_get_subkey_value_i(config, "max_child", 
01259         SERVER_MAX_CHILD, &iv));
01260     s->max_child = iv;
01261 
01262     name = n = u_zalloc(strlen(list) + 1);
01263     dbg_err_if(name == NULL);
01264     
01265     /* load config and init backend for each server in server.list */
01266     for(i = strlen(list), id = 0; 
01267         i > 0 && sscanf(list, "%[^ \t]", name); 
01268         i -= 1 + strlen(name), list += 1 + strlen(name), name[0] = 0)
01269     {
01270         u_dbg("configuring backend: %s", name);
01271 
01272         /* just SERVER_MAX_BACKENDS supported */
01273         warn_err_if(s->nbackend == SERVER_MAX_BACKENDS);
01274 
01275         /* get config tree of this backend */
01276         warn_err_ifm(u_config_get_subkey(config, name, &bekey),
01277             "missing [%s] backend configuration", name);
01278 
01279         type = u_config_get_subkey_value(bekey, "type");
01280         warn_err_ifm(type == NULL, "missing or bad '<servname>.type' value");
01281 
01282         /* create a new backend and push it into the 'be' list */
01283         warn_err_ifm(backend_create(type, bekey, &be),
01284             "backend \"%s\" startup error", type);
01285 
01286         be->server = s;
01287         be->config = bekey;
01288         be->id = id++;
01289         if(be->model == SERVER_MODEL_UNSET)
01290             be->model = s->model; /* inherit server model */
01291 
01292         if(foreground)
01293             be->model = SERVER_MODEL_ITERATIVE;
01294 
01295         /* create the log device (may fail if logging is not configured) */
01296         if(!u_config_get_subkey(bekey, "log", &log_c))
01297             dbg_if(klog_open_from_config(log_c, &be->klog));
01298 
01299 #ifdef OS_WIN
01300         if(be->model != SERVER_MODEL_ITERATIVE)
01301             warn_err("child-based server model is not "
01302                      "yet supported on Windows");
01303 #endif
01304 
01305         LIST_INSERT_HEAD(&s->bes, be, np);
01306 
01307         dbg_err_if(server_setup_backend(s, be));
01308     }
01309 
01310     U_FREE(n);
01311 
01312     /* init done, set signal handlers */
01313     dbg_err_if(u_signal(SIGINT, server_sigint));
01314     dbg_err_if(u_signal(SIGTERM, server_sigterm));
01315 #ifdef OS_UNIX 
01316     dbg_err_if(u_signal(SIGPIPE, SIG_IGN));
01317     dbg_err_if(u_signal(SIGCHLD, server_sigchld));
01318 #endif
01319 
01320     return 0;
01321 err:
01322     u_warn("server init error (config error?)");
01323     U_FREE(n);
01324     if(s)
01325     {
01326         server_free(s);
01327         *ps = NULL;
01328     }
01329     return ~0;
01330 }
01331 

←Products
Copyright © 2005-2012 - KoanLogic S.r.l. - All rights reserved