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