ackpfd.c
上传用户:ladybrid91
上传日期:2007-01-04
资源大小:287k
文件大小:10k
源码类别:

Web服务器

开发平台:

Unix_Linux

  1. /*
  2. ** ackpfd.c         "Accepting Child Keepalive Protocol Forking Daemon"
  3. **
  4. ** Copyright (c) 1995 Peter Eriksson <pen@signum.se>
  5. **
  6. ** Special thanks go to Per Cederqvist <ceder@signum.se> for coming up
  7. ** with what "ackpfd" stands for.
  8. **
  9. ** This program is free software; you can redistribute it and/or modify
  10. ** it under the terms of the GNU General Public License as published by
  11. ** the Free Software Foundation; either version 2 of the License, or
  12. ** (at your option) any later version.
  13. **
  14. ** This program is distributed in the hope that it will be useful,
  15. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17. ** GNU General Public License for more details.
  18. ** You should have received a copy of the GNU General Public License
  19. ** along with this program; if not, write to the Free Software
  20. ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21. */
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <unistd.h>
  25. #include <ctype.h>
  26. #include <time.h>
  27. #include <syslog.h>
  28. #include <signal.h>
  29. #include <errno.h>
  30. #include <pwd.h>
  31. #include <grp.h>
  32. #include <setjmp.h>
  33. #include <sys/time.h>
  34. #include <sys/resource.h>
  35. #include <sys/wait.h>
  36. #include <netdb.h>
  37. #include <sys/socket.h>
  38. #include <netinet/in.h>
  39. #include <arpa/inet.h>
  40. #include <sys/types.h>
  41. #include <fcntl.h>
  42. #include <signal.h>
  43. int n_processes = 1;
  44. int backlog = 1024;
  45. int port = 80;
  46. int debug = 0;
  47. unsigned long address = INADDR_ANY;
  48. struct passwd *pwp = NULL;
  49. int uid = -1;
  50. int gid = -1;
  51. char *root_dir = NULL;
  52. int subservers = 0;
  53. pid_t pid;
  54. void xsleep(int timeout)
  55. {
  56.     time_t start, now;
  57.     int len;
  58.     
  59.     
  60.     time(&start);
  61.     len = timeout;
  62.     
  63.     while (len > 0)
  64.     {
  65. sleep(len);
  66. time(&now);
  67. len = timeout - (now - start);
  68.     }
  69. }
  70. void help(void)
  71. {
  72.     fprintf(stderr,
  73.     "Syntax: ackpfd [<ackpfd options>] <program path> [<program options>]n");
  74.     fprintf(stderr, "nWhere <ackpfd options> may be:nn");
  75.     fprintf(stderr, "t-d ................ Enable debuggingn");
  76.     fprintf(stderr, "t-b<backlog> ....... Change the listen() backlogn");
  77.     fprintf(stderr, "t-a<address> ....... Bind to this address onlyn");
  78.     fprintf(stderr, "t-p<port> .......... Bind to this portn");
  79.     fprintf(stderr, "t-u<uid> ........... Run program as this usern");
  80.     fprintf(stderr, "t-g<gid> ........... Run program as this groupn");
  81.     fprintf(stderr, "t-r<rootdir> ....... Chroot to this directoryn");
  82.     fprintf(stderr, "nThe defaults are:nn");
  83.     fprintf(stderr, "tBacklog ........... %dn", backlog);
  84.     fprintf(stderr, "tAddress ........... %sn",
  85.     address == INADDR_ANY ? "*" :
  86.     inet_ntoa(* (struct in_addr *) &address));
  87.     fprintf(stderr, "tPort .............. %dn", port);
  88. }
  89. int parse_options(int argc,
  90.   char *argv[])
  91. {
  92.     int i;
  93.  
  94.     for (i = 1; i < argc && argv[i][0] == '-'; i++)
  95. switch (argv[i][1])
  96. {
  97.   case 'd':
  98.     debug = 1;
  99.     break;
  100.   case 'b':
  101.     backlog = atoi(argv[i]+2);
  102.     break;
  103.   case 'a':
  104.     if (isdigit(argv[i][2]))
  105. address = inet_addr(argv[i]+2);
  106.     else
  107.     {
  108. struct hostent *hp;
  109. hp = gethostbyname(argv[i]+2);
  110. if (hp == NULL)
  111. {
  112.     syslog(LOG_ERR, "gethostbyname("%s") failed", argv[i]+2);
  113.     exit(1);
  114. }
  115. address = * (unsigned long *) (hp->h_addr_list[0]);
  116.     }
  117.     
  118.     break;
  119.   case 'P':
  120.     n_processes = atoi(argv[i]+2);
  121.     break;
  122.     
  123.   case 'p':
  124.     port = atoi(argv[i]+2);
  125.     break;
  126.     
  127.           case 'u':
  128.             if (isdigit(argv[i][2]))
  129.                 uid = atoi(argv[i]+2);
  130.             else
  131.             {
  132.                 pwp = getpwnam(argv[i]+2);
  133.                 if (pwp == NULL)
  134.                 {
  135.                     syslog(LOG_ERR, "getpwnam("%s") failed", argv[i]+2);
  136.                     exit(1);
  137.                 }
  138.  
  139.                 uid = pwp->pw_uid;
  140.                 gid = pwp->pw_gid;
  141.             }
  142.             break;
  143.  
  144.   case 'g':
  145.             if (isdigit(argv[i][2]))
  146.                 gid = atoi(argv[i]+2);
  147.             else
  148.             {
  149.                 struct group *grp;
  150.  
  151.                 grp = getgrnam(argv[i]+2);
  152.                 if (grp == NULL)
  153.                 {
  154.                     syslog(LOG_ERR, "getgrnam("%s") failed", argv[i]+2);
  155.                     exit(1);
  156.                 }
  157.  
  158.                 gid = grp->gr_gid;
  159.             }
  160.             break;
  161.  
  162.   case 'r':
  163.     root_dir = argv[i];
  164.     break;
  165.   case 'h':
  166.   case '?':
  167.     help();
  168.     exit(0);
  169.     
  170.   default:
  171.     syslog(LOG_ERR, "unknown command line switch: %sn", argv[i]);
  172.     exit(1);
  173. }
  174.     
  175.     return i;
  176. }
  177. void become_daemon(void)
  178. {
  179.     pid_t pid;
  180.     int fd, i;
  181.     struct rlimit rlb;
  182.     
  183.     pid = fork();
  184.     if (pid < 0)
  185.     {
  186. syslog(LOG_ERR, "fork() failed: %m");
  187. exit(1);
  188.     }
  189.     else if (pid > 0)
  190. exit(0);
  191.     if (getrlimit(RLIMIT_NOFILE, &rlb) < 0)
  192.     {
  193. syslog(LOG_ERR, "getrlimit() failed: %m");
  194. perror("ackpfd: getrlimit()");
  195. exit(1);
  196.     }
  197.     
  198.     rlb.rlim_cur = rlb.rlim_max;
  199.     
  200.     if (setrlimit(RLIMIT_NOFILE, &rlb) < 0)
  201.     {
  202. syslog(LOG_ERR, "setrlimit() failed: %m");
  203. perror("ackpfd: setrlimit()");
  204. exit(1);
  205.     }
  206.     
  207.     for (i = 0; i < rlb.rlim_cur; i++)
  208.     {
  209. if (!(debug && i == 2))
  210.     while (close(i) < 0 && errno == EINTR)
  211. ;
  212.     }
  213.     while ((fd = open("/dev/null", O_RDWR)) < 0 && errno == EINTR)
  214. ;
  215.     
  216.     if (fd < 0)
  217.     {
  218. syslog(LOG_ERR, "open("/dev/null") failed: %m");
  219. perror("open("/dev/null")");
  220. exit(1);
  221.     }
  222.     dup2(fd, 1);
  223.     if (!debug)
  224. dup2(fd, 2);
  225.     
  226.     setsid();
  227. }
  228. int listen_socket(unsigned long address,
  229.   int port,
  230.   int backlog)
  231. {
  232.     int sock_fd, val;
  233.     struct sockaddr_in sin;
  234.     
  235.     
  236.     sock_fd = socket(AF_INET, SOCK_STREAM, 0);
  237.     if (sock_fd < 0)
  238.     {
  239. syslog(LOG_ERR, "socket() failed: %m");
  240. exit(1);
  241.     }
  242.     val = 1;
  243.     if (setsockopt(sock_fd,
  244.    SOL_SOCKET,
  245.    SO_REUSEADDR,
  246.    (void *) &val,
  247.    sizeof(val)) < 0)
  248. syslog(LOG_WARNING, "setsockopt(SO_REUSEADDR) failed: %m");
  249.     
  250.     sin.sin_family = AF_INET;
  251.     sin.sin_addr.s_addr = address;
  252.     sin.sin_port = htons(port);
  253.     
  254.     if (bind(sock_fd, (struct sockaddr *) &sin, sizeof(sin)))
  255.     {
  256. syslog(LOG_ERR, "bind(%s, %d) failed: %m",
  257.        (address == INADDR_ANY ? "*" :
  258. inet_ntoa(* (struct in_addr *) &address)),
  259.        port);
  260. exit(1);
  261.     }
  262.     if (listen(sock_fd, backlog))
  263. syslog(LOG_WARNING, "listen(..., %d) failed: %m", backlog);
  264.     return sock_fd;
  265. }
  266. int child_process(int sock_fd, char **argv)
  267. {
  268.     int status;
  269.     
  270.     if (sock_fd != 0)
  271.     {
  272. if (dup2(sock_fd, 0) < 0)
  273. {
  274.     syslog(LOG_ERR, "dup2(%d, 0) failed: %m", sock_fd);
  275.     return -errno;
  276. }
  277. while (close(sock_fd) < 0 && errno == EINTR)
  278.     ;
  279.     }
  280.     
  281.     if (pwp != NULL)
  282.     {
  283. if (initgroups(pwp->pw_name, pwp->pw_gid) < 0)
  284. {
  285.     syslog(LOG_ERR, "initgroups("%s", %d) failed: %m",
  286.    pwp->pw_name, pwp->pw_gid);
  287.     return -errno;
  288. }
  289.     }
  290.     
  291.     if (gid != -1)
  292. if (setgid(gid) < 0)
  293. {
  294.     syslog(LOG_ERR, "setgid(%d) failed: %m", gid);
  295.     return -errno;
  296. }
  297.     if (uid != -1)
  298. if (setuid(uid) < 0)
  299. {
  300.     syslog(LOG_ERR, "setuid(%d) failed: %m", uid);
  301.     return -errno;
  302. }
  303.     
  304.     if (root_dir)
  305.     {
  306. while ((status = chroot(root_dir)) < 0 && errno == EINTR)
  307.     ;
  308. if (status < 0)
  309. {
  310.     syslog(LOG_ERR, "chroot("%s") failed: %m", root_dir);
  311.     return -errno;
  312. }
  313. while ((status = chdir("/")) < 0 && errno == EINTR)
  314.     ;
  315. if (status < 0)
  316. {
  317.     syslog(LOG_ERR, "chdir("/") failed: %m");
  318.     return -errno;
  319. }
  320.     }
  321.     syslog(LOG_NOTICE, "starting: %s", argv[0]);
  322.     
  323.     execv(argv[0], argv);
  324.     
  325.     syslog(LOG_ERR, "execv("%s") failed: %m", argv[0]);
  326.     return -errno;
  327. }
  328. static void sigterm_handler()
  329. {
  330.     syslog(LOG_NOTICE, "sigterm received, exiting");
  331.     if (pid > 2)
  332. kill(pid, SIGTERM);
  333.     exit(1);
  334. }
  335. int main(int argc,
  336.  char *argv[])
  337. {
  338.     int i, sock_fd, status;
  339.     struct sigaction actb;
  340.     time_t start_time, cur_time;
  341.     
  342.     openlog("ackpfd", LOG_PID, LOG_DAEMON);
  343.     
  344.     i = parse_options(argc, argv);
  345.     if (i >= argc)
  346.     {
  347. syslog(LOG_ERR, "missing program path");
  348. exit(1);
  349.     }
  350.     
  351.     become_daemon();
  352.     actb.sa_handler = sigterm_handler;
  353.     actb.sa_flags = 0;
  354.     sigemptyset(&actb.sa_mask);
  355.     sigaction(SIGTERM, &actb, NULL);
  356.     sock_fd = listen_socket(address, port, backlog);
  357.   Loop:
  358.     time(&start_time);
  359.     subservers++;
  360.     pid = fork();
  361.     if (pid < 0)
  362. syslog(LOG_ERR, "fork() failed: %m");
  363.     
  364.     else if (pid == 0)
  365. _exit(child_process(sock_fd, argv+i));
  366.     
  367.     /* Parent process */
  368.     actb.sa_handler = SIG_IGN;
  369.     actb.sa_flags = 0;
  370.     sigemptyset(&actb.sa_mask);
  371.     sigaction(SIGCHLD, &actb, NULL);
  372.   Wait:
  373.     status = 0;
  374.     while  ((pid = waitpid(-1, &status, 0)) < 0 && errno == EINTR)
  375. ;
  376.     
  377.     time(&cur_time);
  378.     
  379.     if (pid < 0)
  380. syslog(LOG_ERR, "waitpid() failed: %m");
  381.     else
  382.     {
  383. if (WIFEXITED(status))
  384. {
  385.     status = WEXITSTATUS(status);
  386.     if (status != 0)
  387. syslog(LOG_ERR,
  388.        "process %d exited with code #%d",
  389.        pid, status);
  390.     subservers--;
  391. }
  392. else if (WIFSIGNALED(status))
  393. {
  394.     status = WTERMSIG(status);
  395.     syslog(LOG_ERR,
  396.    "process %d exited with signal #%d",
  397.    pid, status);
  398.     subservers--;
  399. }
  400. else if (WIFSTOPPED(status))
  401. {
  402.     status = WSTOPSIG(status);
  403.     syslog(LOG_ERR,
  404.    "process %d stopped due to signal #%d",
  405.    pid, status);
  406.     goto Wait;
  407. }
  408. else if (WIFCONTINUED(status))
  409. {
  410.     syslog(LOG_ERR, "process %d continued",
  411.    pid);
  412.     goto Wait;
  413. }
  414. else
  415. {
  416.     syslog(LOG_ERR, "strange process event notification: %dn",
  417.    status);
  418.     goto Wait;
  419. }
  420.     }
  421.     if (subservers > 0)
  422. goto Wait;
  423.     
  424.     if (cur_time == start_time)
  425.     {
  426. syslog(LOG_NOTICE, "process looping - sleeping one minute");
  427. xsleep(60);
  428.     }
  429.     
  430.     goto Loop;
  431. }