Main Page | Modules | Data Structures | Directories | File List | Data Fields | Globals

server.c

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

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