]> mj.ucw.cz Git - misc.git/blob - prefork.c
Merge with git+ssh://git.ucw.cz/home/mj/GIT/misc.git
[misc.git] / prefork.c
1 /*
2  *      A simple preforking daemon, inspired by the logic in Apache 1.3
3  *      Martin Mares, September 2007
4  */
5
6 #include <stdio.h>
7 #include <stdarg.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <sys/wait.h>
11 #include <sys/socket.h>
12 #include <netinet/in.h>
13
14 #define NUM_CHILDREN 10
15
16 static int listening_sk;
17
18 /*** Logging functions ***/
19
20 static void
21 vmsg(char *msg, va_list args)
22 {
23   /*
24    * This is a little bit tricky, because we need to be sure that logging
25    * is atomic with respect to other processes. Fortunately, it is sufficient
26    * to prepare everything in a buffer and flush it with a single write().
27    */
28   char buf[1024];
29   int cnt = vsnprintf(buf, sizeof(buf)-1, msg, args);
30   buf[cnt++] = '\n';
31   write(2, buf, cnt);
32 }
33
34 static void
35 msg(char *msg, ...)
36 {
37   va_list args;
38   va_start(args, msg);
39   vmsg(msg, args);
40   va_end(args);
41 }
42
43 static void
44 die(char *msg, ...)
45 {
46   va_list args;
47   va_start(args, msg);
48   vmsg(msg, args);
49   va_end(args);
50   exit(1);
51 }
52
53 /*** Child process ***/
54
55 static void
56 child(void)
57 {
58   int count = 0;
59   for (;;)
60     {
61       struct sockaddr_in sa;
62       socklen_t len = sizeof(sa);
63       int sk = accept(listening_sk, (struct sockaddr *) &sa, &len);
64       if (sk < 0)
65         die("Accept failed: %m");
66
67       /* Handle the connection somehow */
68       write(sk, "Hello, world!\n", 14);
69       close(sk);
70
71       /* After processing a certain number of requests, exit. This makes small memory leaks harmless. */
72       if (count++ >= 100)
73         exit(0);
74     }
75 }
76
77 /*** Master process ***/
78
79 int
80 main(void)
81 {
82   /* Set up the listening socket */
83   int one = 1;
84   listening_sk = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
85   if (listening_sk < 0)
86     die("Cannot create listening socket: %m");
87   if (setsockopt(listening_sk, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
88     die("Cannot set SO_REUSEADDR: %m");
89   struct sockaddr_in sa = { .sin_family = AF_INET, .sin_port = htons(9999) };
90   if (bind(listening_sk, (struct sockaddr *) &sa, sizeof(sa)) < 0)
91     die("Cannot bind: %m");
92   if (listen(listening_sk, 256) < 0)
93     die("Cannot listen: %m");
94
95   /* Main loop */
96   pid_t pid;
97   int children = 0;
98   int status;
99   for (;;)
100     {
101       if (children < NUM_CHILDREN)
102         {
103           pid = fork();
104           if (pid < 0)
105             {
106               /* Temporary resource shortage, better sleep for a while */
107               msg("Fork failed: %m");
108               sleep(1);
109             }
110           else if (pid)
111             children++;
112           else
113             child();
114           continue;
115         }
116       pid = wait(&status);
117       if (pid < 0)
118         die("Wait failed: %m");
119       if (!(WIFEXITED(status) && !WEXITSTATUS(status)))
120         msg("Child %d exited with status %x", (int)pid, status);
121       children--;
122     }
123 }