• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2001-2004 Brandon Long
3  * All Rights Reserved.
4  *
5  * ClearSilver Templating System
6  *
7  * This code is made available under the terms of the ClearSilver License.
8  * http://www.clearsilver.net/license.hdf
9  *
10  */
11 
12 /* Initial version based on multi-proc based server (like apache 1.x)
13  *
14  * Parts are:
15  *   1) server Init
16  *   2) sub-proc start
17  *   3)   sub-proc init
18  *   4)   sub-proc process request
19  *   5)   sub-proc cleanup
20  *   6) server cleanup
21  *
22  * Parts 1 & 6 aren't part of the framework, and at this point, I don't
23  * think I need to worry about 3 & 5 either, but maybe in the future.
24  */
25 
26 #include "cs_config.h"
27 
28 #include <unistd.h>
29 #include <sys/types.h>
30 #include <sys/wait.h>
31 #include <limits.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <signal.h>
35 
36 #include "neo_misc.h"
37 #include "neo_err.h"
38 #include "neo_net.h"
39 #include "ulocks.h"
40 #include "neo_server.h"
41 
nserver_child_loop(NSERVER * server,int num)42 static NEOERR *nserver_child_loop(NSERVER *server, int num)
43 {
44   NEOERR *err = STATUS_OK, *clean_err;
45   int loop = 0;
46   NSOCK *child_sock;
47 
48   if (server->init_cb)
49   {
50     err = server->init_cb(server->data, num);
51     if (err) return nerr_pass(err);
52   }
53 
54   while (loop++ < server->num_requests)
55   {
56     err = fLock(server->accept_lock);
57     if (err) break;
58     err = ne_net_accept(&child_sock, server->server_fd, server->data_timeout);
59     fUnlock(server->accept_lock);
60     if (err) break;
61     err = server->req_cb(server->data, num, child_sock);
62     if (err)
63     {
64       ne_net_close(&child_sock);
65     }
66     else
67     {
68       err = ne_net_close(&child_sock);
69     }
70     nerr_log_error(err);
71     nerr_ignore(&err);
72   }
73   ne_warn("nserver child loop handled %d connections", loop-1);
74 
75   if (server->clean_cb)
76   {
77     clean_err = server->clean_cb(server->data, num);
78     if (clean_err)
79     {
80       nerr_log_error(clean_err);
81       nerr_ignore(&clean_err);
82     }
83   }
84 
85   return nerr_pass(err);
86 }
87 
ignore_pipe(void)88 static void ignore_pipe(void)
89 {
90   struct sigaction sa;
91 
92 
93   memset(&sa, 0, sizeof(struct sigaction));
94 
95   sa.sa_handler = SIG_IGN;
96   sigemptyset(&sa.sa_mask);
97   sa.sa_flags = SA_RESTART;
98   sigaction(SIGPIPE, &sa, NULL);
99 }
100 
101 /* Handle shutdown by accepting a TERM signal and then passing it to our
102  * program group */
103 static int ShutdownPending = 0;
104 
sig_term(int sig)105 static void sig_term(int sig)
106 {
107   ShutdownPending = 1;
108   ne_net_shutdown();
109 }
110 
setup_term(void)111 static void setup_term(void)
112 {
113   struct sigaction sa;
114 
115 
116   memset(&sa, 0, sizeof(struct sigaction));
117 
118   sa.sa_handler = sig_term;
119   sigemptyset(&sa.sa_mask);
120   sa.sa_flags = 0;
121   sigaction(SIGTERM, &sa, NULL);
122 }
123 
nserver_proc_start(NSERVER * server,BOOL debug)124 NEOERR *nserver_proc_start(NSERVER *server, BOOL debug)
125 {
126   NEOERR *err;
127 
128   if (server->req_cb == NULL)
129     return nerr_raise(NERR_ASSERT, "nserver requires a request callback");
130 
131   ignore_pipe();
132 
133   setup_term();
134 
135   ShutdownPending = 0;
136 
137   err = fFind(&(server->accept_lock), server->lockfile);
138   if (err && nerr_handle(&err, NERR_NOT_FOUND))
139   {
140     err = fCreate(&(server->accept_lock), server->lockfile);
141   }
142   if (err) return nerr_pass(err);
143 
144   do
145   {
146     err = ne_net_listen(server->port, &(server->server_fd));
147     if (err) break;
148 
149     if (debug == TRUE)
150     {
151       err = nserver_child_loop(server, 0);
152       break;
153     }
154     else
155     {
156       /* create children and restart them as necessary */
157       pid_t child;
158       int count, status;
159 
160       for (count = 0; count < server->num_children; count++)
161       {
162 	child = fork();
163 	if (child == -1)
164 	{
165 	  err = nerr_raise_errno(NERR_SYSTEM, "Unable to fork child");
166 	  break;
167 	}
168 	if (!child)
169 	{
170 	  err = nserver_child_loop(server, count);
171 	  if (err) exit(-1);
172 	  exit(0);
173 	}
174 	ne_warn("Starting child pid %d", child);
175       }
176       if (count < server->num_children) break;
177       while (!ShutdownPending)
178       {
179 	child = wait3(&status, 0, NULL);
180 	if (child == -1)
181 	{
182 	   ne_warn("wait3 failed [%d] %s", errno, strerror(errno));
183 	   continue;
184 	}
185 	if (WIFSTOPPED(status))
186 	{
187 	  ne_warn("pid %d stopped on signal %d", child, WSTOPSIG(status));
188 	  continue;
189 	}
190 	if (WIFEXITED(status))
191 	{
192 	  /* at some point, we might do something here with the
193 	   * particular exit value */
194 	  ne_warn("pid %d exited, returned %d", child, WEXITSTATUS(status));
195 	}
196 	else if (WIFSIGNALED(status))
197 	{
198 	  ne_warn("pid %d exited on signal %d", child, WTERMSIG(status));
199 	}
200 	count++;
201 
202 	child = fork();
203 	if (child == -1)
204 	{
205 	  err = nerr_raise_errno(NERR_SYSTEM, "Unable to fork child");
206 	  break;
207 	}
208 	if (!child)
209 	{
210 	  err = nserver_child_loop(server, count);
211 	  if (err) exit(-1);
212 	  exit(0);
213 	}
214 	ne_warn("Starting child pid %d", child);
215       }
216       /* At some point, we might want to actually maintain information
217        * on our children, and then we can be more specific here in terms
218        * of making sure they all shutdown... for now, fergitaboutit */
219       if (ShutdownPending)
220       {
221 	killpg(0, SIGTERM);
222       }
223     }
224   }
225   while (0);
226 
227   fDestroy(server->accept_lock);
228   return nerr_pass(err);
229 }
230