File: | src/mod/xml_int/mod_xml_rpc/../../../../libs/xmlrpc-c/lib/abyss/src/server.c |
Location: | line 464, column 5 |
Description: | Function call argument is an uninitialized value |
1 | /* Copyright information is at end of file */ | |||
2 | ||||
3 | #define _XOPEN_SOURCE700 600 /* Make sure strdup() is in <string.h> */ | |||
4 | #define _BSD_SOURCE1 /* Make sure setgroups()is in <grp.h> */ | |||
5 | #ifndef _DEFAULT_SOURCE1 | |||
6 | #define _DEFAULT_SOURCE1 | |||
7 | #endif | |||
8 | ||||
9 | #include <assert.h> | |||
10 | #include <stdio.h> | |||
11 | #include <stdlib.h> | |||
12 | #include <string.h> | |||
13 | #include <time.h> | |||
14 | #include <errno(*__errno_location ()).h> | |||
15 | #ifndef WIN32 | |||
16 | #include <grp.h> | |||
17 | #endif | |||
18 | ||||
19 | #include "xmlrpc_config.h" | |||
20 | #include "bool.h" | |||
21 | #include "girmath.h" | |||
22 | #include "mallocvar.h" | |||
23 | #include "xmlrpc-c/string_int.h" | |||
24 | #include "xmlrpc-c/sleep_int.h" | |||
25 | ||||
26 | #include "xmlrpc-c/abyss.h" | |||
27 | #include "trace.h" | |||
28 | #include "session.h" | |||
29 | #include "file.h" | |||
30 | #include "conn.h" | |||
31 | #include "chanswitch.h" | |||
32 | #include "channel.h" | |||
33 | #include "socket.h" | |||
34 | #ifdef WIN32 | |||
35 | #include "socket_win.h" | |||
36 | #else | |||
37 | #include "socket_unix.h" | |||
38 | #endif | |||
39 | #include "http.h" | |||
40 | #include "handler.h" | |||
41 | ||||
42 | #include "server.h" | |||
43 | ||||
44 | ||||
45 | struct uriHandler { | |||
46 | initHandlerFn init; | |||
47 | termHandlerFn term; | |||
48 | handleReq3Fn handleReq3; | |||
49 | handleReq2Fn handleReq2; | |||
50 | URIHandler handleReq1; | |||
51 | void * userdata; | |||
52 | }; | |||
53 | ||||
54 | ||||
55 | ||||
56 | void | |||
57 | ServerTerminate(TServer * const serverP) { | |||
58 | ||||
59 | struct _TServer * const srvP = serverP->srvP; | |||
60 | ||||
61 | srvP->terminationRequested = true; | |||
62 | ||||
63 | if (srvP->chanSwitchP) { | |||
64 | ChanSwitchInterrupt(srvP->chanSwitchP); | |||
65 | } | |||
66 | } | |||
67 | ||||
68 | ||||
69 | ||||
70 | void | |||
71 | ServerResetTerminate(TServer * const serverP) { | |||
72 | ||||
73 | struct _TServer * const srvP = serverP->srvP; | |||
74 | ||||
75 | srvP->terminationRequested = false; | |||
76 | } | |||
77 | ||||
78 | ||||
79 | ||||
80 | static void | |||
81 | initUnixStuff(struct _TServer * const srvP) { | |||
82 | #ifndef WIN32 | |||
83 | srvP->pidfileP = NULL((void*)0); | |||
84 | srvP->uid = srvP->gid = -1; | |||
85 | #endif | |||
86 | } | |||
87 | ||||
88 | ||||
89 | ||||
90 | static bool | |||
91 | logOpen(struct _TServer * const srvP) { | |||
92 | ||||
93 | bool success; | |||
94 | ||||
95 | success = FileOpenCreate(&srvP->logfileP, srvP->logfilename, | |||
96 | O_WRONLY01 | O_APPEND02000); | |||
97 | if (success) { | |||
98 | bool success; | |||
99 | success = MutexCreate(&srvP->logmutexP); | |||
100 | if (success) | |||
101 | srvP->logfileisopen = TRUE1; | |||
102 | else | |||
103 | TraceMsg("Can't create mutex for log file"); | |||
104 | ||||
105 | if (!success) | |||
106 | FileClose(srvP->logfileP); | |||
107 | } else | |||
108 | TraceMsg("Can't open log file '%s'", srvP->logfilename); | |||
109 | ||||
110 | return success; | |||
111 | } | |||
112 | ||||
113 | ||||
114 | ||||
115 | static void | |||
116 | logClose(struct _TServer * const srvP) { | |||
117 | ||||
118 | if (srvP->logfileisopen) { | |||
119 | FileClose(srvP->logfileP); | |||
120 | MutexDestroy(srvP->logmutexP); | |||
121 | srvP->logfileisopen = FALSE0; | |||
122 | } | |||
123 | } | |||
124 | ||||
125 | ||||
126 | ||||
127 | static void | |||
128 | initChanSwitchStuff(struct _TServer * const srvP, | |||
129 | bool const noAccept, | |||
130 | TChanSwitch * const chanSwitchP, | |||
131 | bool const userChanSwitch, | |||
132 | unsigned short const port, | |||
133 | const char ** const errorP) { | |||
134 | ||||
135 | ||||
136 | if (chanSwitchP) { | |||
137 | *errorP = NULL((void*)0); | |||
138 | srvP->serverAcceptsConnections = TRUE1; | |||
139 | srvP->chanSwitchP = chanSwitchP; | |||
140 | srvP->weCreatedChanSwitch = !userChanSwitch; | |||
141 | } else if (noAccept) { | |||
142 | *errorP = NULL((void*)0); | |||
143 | srvP->serverAcceptsConnections = FALSE0; | |||
144 | srvP->chanSwitchP = NULL((void*)0); | |||
145 | srvP->weCreatedChanSwitch = FALSE0; | |||
146 | } else { | |||
147 | *errorP = NULL((void*)0); | |||
148 | srvP->serverAcceptsConnections = TRUE1; | |||
149 | srvP->chanSwitchP = NULL((void*)0); | |||
150 | srvP->weCreatedChanSwitch = FALSE0; | |||
151 | srvP->port = port; | |||
152 | } | |||
153 | } | |||
154 | ||||
155 | ||||
156 | ||||
157 | static void | |||
158 | createServer(struct _TServer ** const srvPP, | |||
159 | bool const noAccept, | |||
160 | TChanSwitch * const chanSwitchP, | |||
161 | bool const userChanSwitch, | |||
162 | unsigned short const portNumber, | |||
163 | const char ** const errorP) { | |||
164 | ||||
165 | struct _TServer * srvP; | |||
166 | ||||
167 | MALLOCVAR(srvP)srvP = malloc(sizeof(*srvP)); | |||
168 | ||||
169 | if (srvP == NULL((void*)0)) { | |||
170 | xmlrpc_asprintf(errorP, | |||
171 | "Unable to allocate space for server descriptor"); | |||
172 | } else { | |||
173 | srvP->terminationRequested = false; | |||
174 | ||||
175 | initChanSwitchStuff(srvP, noAccept, chanSwitchP, userChanSwitch, | |||
176 | portNumber, errorP); | |||
177 | ||||
178 | if (!*errorP) { | |||
179 | srvP->builtinHandlerP = HandlerCreate(); | |||
180 | if (!srvP->builtinHandlerP) | |||
181 | xmlrpc_asprintf(errorP, "Unable to allocate space for " | |||
182 | "builtin handler descriptor"); | |||
183 | else { | |||
184 | srvP->defaultHandler = HandlerDefaultBuiltin; | |||
185 | srvP->defaultHandlerContext = srvP->builtinHandlerP; | |||
186 | ||||
187 | srvP->name = strdup("unnamed")(__extension__ (__builtin_constant_p ("unnamed") && ( (size_t)(const void *)(("unnamed") + 1) - (size_t)(const void *)("unnamed") == 1) ? (((const char *) ("unnamed"))[0] == '\0' ? (char *) calloc ((size_t) 1, (size_t) 1) : ({ size_t __len = strlen ("unnamed") + 1; char *__retval = (char *) malloc ( __len); if (__retval != ((void*)0)) __retval = (char *) memcpy (__retval, "unnamed", __len); __retval; })) : __strdup ("unnamed" ))); | |||
188 | srvP->logfilename = NULL((void*)0); | |||
189 | srvP->keepalivetimeout = 15; | |||
190 | srvP->keepalivemaxconn = 30; | |||
191 | srvP->timeout = 15; | |||
192 | srvP->advertise = TRUE1; | |||
193 | srvP->useSigchld = FALSE0; | |||
194 | srvP->uriHandlerStackSize = 0; | |||
195 | ||||
196 | initUnixStuff(srvP); | |||
197 | ||||
198 | ListInitAutoFree(&srvP->handlers); | |||
199 | ||||
200 | srvP->logfileisopen = FALSE0; | |||
201 | ||||
202 | *errorP = NULL((void*)0); | |||
203 | ||||
204 | if (*errorP) | |||
205 | HandlerDestroy(srvP->builtinHandlerP); | |||
206 | } | |||
207 | } | |||
208 | if (*errorP) | |||
209 | free(srvP); | |||
210 | } | |||
211 | *srvPP = srvP; | |||
212 | } | |||
213 | ||||
214 | ||||
215 | ||||
216 | static void | |||
217 | setNamePathLog(TServer * const serverP, | |||
218 | const char * const name, | |||
219 | const char * const filesPath, | |||
220 | const char * const logFileName) { | |||
221 | /*---------------------------------------------------------------------------- | |||
222 | This odd function exists to help with backward compatibility. | |||
223 | Today, we have the expandable model where you create a server with | |||
224 | default parameters, then use ServerSet... functions to choose | |||
225 | non-default parameters. But before, you specified these three | |||
226 | parameters right in the arguments of various create functions. | |||
227 | -----------------------------------------------------------------------------*/ | |||
228 | if (name) | |||
229 | ServerSetName(serverP, name); | |||
230 | if (filesPath) | |||
231 | ServerSetFilesPath(serverP, filesPath); | |||
232 | if (logFileName) | |||
233 | ServerSetLogFileName(serverP, logFileName); | |||
234 | } | |||
235 | ||||
236 | ||||
237 | ||||
238 | abyss_bool | |||
239 | ServerCreate(TServer * const serverP, | |||
240 | const char * const name, | |||
241 | xmlrpc_uint16_t const portNumber, | |||
242 | const char * const filesPath, | |||
243 | const char * const logFileName) { | |||
244 | ||||
245 | bool const noAcceptFalse = FALSE0; | |||
246 | bool const userChanSwitchFalse = FALSE0; | |||
247 | ||||
248 | bool success; | |||
249 | const char * error; | |||
250 | ||||
251 | createServer(&serverP->srvP, noAcceptFalse, | |||
252 | NULL((void*)0), userChanSwitchFalse, | |||
253 | portNumber, &error); | |||
254 | ||||
255 | if (error) { | |||
256 | TraceMsg(error); | |||
257 | xmlrpc_strfree(error); | |||
258 | success = FALSE0; | |||
259 | } else { | |||
260 | success = TRUE1; | |||
261 | ||||
262 | setNamePathLog(serverP, name, filesPath, logFileName); | |||
263 | } | |||
264 | ||||
265 | return success; | |||
266 | } | |||
267 | ||||
268 | ||||
269 | ||||
270 | static void | |||
271 | createSwitchFromOsSocket(TOsSocket const osSocket, | |||
272 | TChanSwitch ** const chanSwitchPP, | |||
273 | const char ** const errorP) { | |||
274 | ||||
275 | #ifdef WIN32 | |||
276 | ChanSwitchWinCreateWinsock(osSocket, chanSwitchPP, errorP); | |||
277 | #else | |||
278 | ChanSwitchUnixCreateFd(osSocket, chanSwitchPP, errorP); | |||
279 | #endif | |||
280 | } | |||
281 | ||||
282 | ||||
283 | ||||
284 | static void | |||
285 | createChannelFromOsSocket(TOsSocket const osSocket, | |||
286 | TChannel ** const channelPP, | |||
287 | void ** const channelInfoPP, | |||
288 | const char ** const errorP) { | |||
289 | ||||
290 | #ifdef WIN32 | |||
291 | ChannelWinCreateWinsock(osSocket, channelPP, | |||
292 | (struct abyss_win_chaninfo**)channelInfoPP, | |||
293 | errorP); | |||
294 | #else | |||
295 | ChannelUnixCreateFd(osSocket, channelPP, | |||
296 | (struct abyss_unix_chaninfo**)channelInfoPP, | |||
297 | errorP); | |||
298 | #endif | |||
299 | } | |||
300 | ||||
301 | ||||
302 | ||||
303 | abyss_bool | |||
304 | ServerCreateSocket(TServer * const serverP, | |||
305 | const char * const name, | |||
306 | TOsSocket const socketFd, | |||
307 | const char * const filesPath, | |||
308 | const char * const logFileName) { | |||
309 | ||||
310 | bool success; | |||
311 | TChanSwitch * chanSwitchP; | |||
312 | const char * error; | |||
313 | ||||
314 | createSwitchFromOsSocket(socketFd, &chanSwitchP, &error); | |||
315 | ||||
316 | if (error) { | |||
317 | TraceMsg(error); | |||
318 | success = FALSE0; | |||
319 | xmlrpc_strfree(error); | |||
320 | } else { | |||
321 | bool const noAcceptFalse = FALSE0; | |||
322 | bool const userChanSwitchFalse = FALSE0; | |||
323 | ||||
324 | const char * error; | |||
325 | ||||
326 | createServer(&serverP->srvP, noAcceptFalse, | |||
327 | chanSwitchP, userChanSwitchFalse, | |||
328 | 0, &error); | |||
329 | ||||
330 | if (error) { | |||
331 | TraceMsg(error); | |||
332 | success = FALSE0; | |||
333 | xmlrpc_strfree(error); | |||
334 | } else { | |||
335 | success = TRUE1; | |||
336 | ||||
337 | setNamePathLog(serverP, name, filesPath, logFileName); | |||
338 | } | |||
339 | if (!success) | |||
340 | ChanSwitchDestroy(chanSwitchP); | |||
341 | } | |||
342 | ||||
343 | return success; | |||
344 | } | |||
345 | ||||
346 | ||||
347 | ||||
348 | abyss_bool | |||
349 | ServerCreateNoAccept(TServer * const serverP, | |||
350 | const char * const name, | |||
351 | const char * const filesPath, | |||
352 | const char * const logFileName) { | |||
353 | ||||
354 | bool const noAcceptTrue = TRUE1; | |||
355 | bool const userChanSwitchFalse = FALSE0; | |||
356 | ||||
357 | bool success; | |||
358 | const char * error; | |||
359 | ||||
360 | createServer(&serverP->srvP, noAcceptTrue, | |||
| ||||
361 | NULL((void*)0), userChanSwitchFalse, | |||
362 | 0, &error); | |||
363 | ||||
364 | if (error) { | |||
365 | TraceMsg(error); | |||
366 | success = FALSE0; | |||
367 | xmlrpc_strfree(error); | |||
368 | } else { | |||
369 | success = TRUE1; | |||
370 | ||||
371 | setNamePathLog(serverP, name, filesPath, logFileName); | |||
372 | } | |||
373 | return success; | |||
374 | } | |||
375 | ||||
376 | ||||
377 | ||||
378 | void | |||
379 | ServerCreateSwitch(TServer * const serverP, | |||
380 | TChanSwitch * const chanSwitchP, | |||
381 | const char ** const errorP) { | |||
382 | ||||
383 | bool const noAcceptFalse = FALSE0; | |||
384 | bool const userChanSwitchTrue = TRUE1; | |||
385 | ||||
386 | assert(serverP)((serverP) ? (void) (0) : __assert_fail ("serverP", "../../../../libs/xmlrpc-c/lib/abyss/src/server.c" , 386, __PRETTY_FUNCTION__)); | |||
387 | assert(chanSwitchP)((chanSwitchP) ? (void) (0) : __assert_fail ("chanSwitchP", "../../../../libs/xmlrpc-c/lib/abyss/src/server.c" , 387, __PRETTY_FUNCTION__)); | |||
388 | ||||
389 | createServer(&serverP->srvP, noAcceptFalse, | |||
390 | chanSwitchP, userChanSwitchTrue, | |||
391 | 0, errorP); | |||
392 | } | |||
393 | ||||
394 | ||||
395 | ||||
396 | void | |||
397 | ServerCreateSocket2(TServer * const serverP, | |||
398 | TSocket * const socketP, | |||
399 | const char ** const errorP) { | |||
400 | ||||
401 | TChanSwitch * const chanSwitchP = SocketGetChanSwitch(socketP); | |||
402 | ||||
403 | assert(socketP)((socketP) ? (void) (0) : __assert_fail ("socketP", "../../../../libs/xmlrpc-c/lib/abyss/src/server.c" , 403, __PRETTY_FUNCTION__)); | |||
404 | ||||
405 | if (!chanSwitchP) | |||
406 | xmlrpc_asprintf( | |||
407 | errorP, "Socket is not a listening socket. " | |||
408 | "You should use ServerCreateSwitch() instead, anyway."); | |||
409 | else | |||
410 | ServerCreateSwitch(serverP, chanSwitchP, errorP); | |||
411 | } | |||
412 | ||||
413 | ||||
414 | ||||
415 | static void | |||
416 | terminateHandlers(TList * const handlersP) { | |||
417 | /*---------------------------------------------------------------------------- | |||
418 | Terminate all handlers in the list '*handlersP'. | |||
419 | ||||
420 | I.e. call each handler's terminate function. | |||
421 | -----------------------------------------------------------------------------*/ | |||
422 | if (handlersP->item) { | |||
423 | unsigned int i; | |||
424 | for (i = handlersP->size; i > 0; --i) { | |||
425 | struct uriHandler * const handlerP = handlersP->item[i-1]; | |||
426 | if (handlerP->term) | |||
427 | handlerP->term(handlerP->userdata); | |||
428 | } | |||
429 | } | |||
430 | } | |||
431 | ||||
432 | ||||
433 | ||||
434 | void | |||
435 | ServerFree(TServer * const serverP) { | |||
436 | ||||
437 | struct _TServer * const srvP = serverP->srvP; | |||
438 | ||||
439 | if (srvP->weCreatedChanSwitch && srvP->chanSwitchP) | |||
440 | ChanSwitchDestroy(srvP->chanSwitchP); | |||
441 | ||||
442 | xmlrpc_strfree(srvP->name); | |||
443 | ||||
444 | terminateHandlers(&srvP->handlers); | |||
445 | ||||
446 | ListFree(&srvP->handlers); | |||
447 | ||||
448 | HandlerDestroy(srvP->builtinHandlerP); | |||
449 | ||||
450 | logClose(srvP); | |||
451 | ||||
452 | if (srvP->logfilename) | |||
453 | xmlrpc_strfree(srvP->logfilename); | |||
454 | ||||
455 | free(srvP); | |||
456 | } | |||
457 | ||||
458 | ||||
459 | ||||
460 | void | |||
461 | ServerSetName(TServer * const serverP, | |||
462 | const char * const name) { | |||
463 | ||||
464 | xmlrpc_strfree(serverP->srvP->name); | |||
| ||||
465 | ||||
466 | serverP->srvP->name = strdup(name)(__extension__ (__builtin_constant_p (name) && ((size_t )(const void *)((name) + 1) - (size_t)(const void *)(name) == 1) ? (((const char *) (name))[0] == '\0' ? (char *) calloc ( (size_t) 1, (size_t) 1) : ({ size_t __len = strlen (name) + 1 ; char *__retval = (char *) malloc (__len); if (__retval != ( (void*)0)) __retval = (char *) memcpy (__retval, name, __len) ; __retval; })) : __strdup (name))); | |||
467 | } | |||
468 | ||||
469 | ||||
470 | ||||
471 | void | |||
472 | ServerSetFilesPath(TServer * const serverP, | |||
473 | const char * const filesPath) { | |||
474 | ||||
475 | HandlerSetFilesPath(serverP->srvP->builtinHandlerP, filesPath); | |||
476 | } | |||
477 | ||||
478 | ||||
479 | ||||
480 | void | |||
481 | ServerSetLogFileName(TServer * const serverP, | |||
482 | const char * const logFileName) { | |||
483 | ||||
484 | struct _TServer * const srvP = serverP->srvP; | |||
485 | ||||
486 | if (srvP->logfilename) | |||
487 | xmlrpc_strfree(srvP->logfilename); | |||
488 | ||||
489 | srvP->logfilename = strdup(logFileName)(__extension__ (__builtin_constant_p (logFileName) && ((size_t)(const void *)((logFileName) + 1) - (size_t)(const void *)(logFileName) == 1) ? (((const char *) (logFileName))[0] == '\0' ? (char *) calloc ((size_t) 1, (size_t) 1) : ({ size_t __len = strlen (logFileName) + 1; char *__retval = (char *) malloc (__len); if (__retval != ((void*)0)) __retval = (char *) memcpy (__retval, logFileName, __len); __retval; })) : __strdup (logFileName ))); | |||
490 | } | |||
491 | ||||
492 | ||||
493 | ||||
494 | void | |||
495 | ServerSetKeepaliveTimeout(TServer * const serverP, | |||
496 | xmlrpc_uint32_t const keepaliveTimeout) { | |||
497 | ||||
498 | serverP->srvP->keepalivetimeout = MAX(keepaliveTimeout, 1)((keepaliveTimeout) > (1) ? (keepaliveTimeout) : (1)); | |||
499 | } | |||
500 | ||||
501 | ||||
502 | ||||
503 | void | |||
504 | ServerSetKeepaliveMaxConn(TServer * const serverP, | |||
505 | xmlrpc_uint32_t const keepaliveMaxConn) { | |||
506 | ||||
507 | serverP->srvP->keepalivemaxconn = MAX(keepaliveMaxConn, 1)((keepaliveMaxConn) > (1) ? (keepaliveMaxConn) : (1)); | |||
508 | } | |||
509 | ||||
510 | ||||
511 | ||||
512 | void | |||
513 | ServerSetTimeout(TServer * const serverP, | |||
514 | xmlrpc_uint32_t const timeout) { | |||
515 | ||||
516 | serverP->srvP->timeout = timeout; | |||
517 | } | |||
518 | ||||
519 | ||||
520 | ||||
521 | void | |||
522 | ServerSetAdvertise(TServer * const serverP, | |||
523 | abyss_bool const advertise) { | |||
524 | ||||
525 | serverP->srvP->advertise = advertise; | |||
526 | } | |||
527 | ||||
528 | ||||
529 | ||||
530 | void | |||
531 | ServerSetMimeType(TServer * const serverP, | |||
532 | MIMEType * const mimeTypeP) { | |||
533 | ||||
534 | HandlerSetMimeType(serverP->srvP->builtinHandlerP, mimeTypeP); | |||
535 | } | |||
536 | ||||
537 | ||||
538 | ||||
539 | static URIHandler2 | |||
540 | makeUriHandler2(const struct uriHandler * const handlerP) { | |||
541 | ||||
542 | URIHandler2 retval; | |||
543 | ||||
544 | retval.init = handlerP->init; | |||
545 | retval.term = handlerP->term; | |||
546 | retval.handleReq2 = handlerP->handleReq2; | |||
547 | retval.handleReq1 = handlerP->handleReq1; | |||
548 | retval.userdata = handlerP->userdata; | |||
549 | ||||
550 | return retval; | |||
551 | } | |||
552 | ||||
553 | ||||
554 | ||||
555 | static void | |||
556 | runUserHandler(TSession * const sessionP, | |||
557 | struct _TServer * const srvP) { | |||
558 | ||||
559 | abyss_bool handled; | |||
560 | int i; | |||
561 | ||||
562 | for (i = srvP->handlers.size-1, handled = FALSE0; | |||
563 | i >= 0 && !handled; | |||
564 | --i) { | |||
565 | const struct uriHandler * const handlerP = srvP->handlers.item[i]; | |||
566 | ||||
567 | if (handlerP->handleReq3) | |||
568 | handlerP->handleReq3(handlerP->userdata, sessionP, &handled); | |||
569 | if (handlerP->handleReq2) { | |||
570 | URIHandler2 handler2 = makeUriHandler2(handlerP); | |||
571 | handlerP->handleReq2(&handler2, sessionP, &handled); | |||
572 | } else if (handlerP->handleReq1) | |||
573 | handled = handlerP->handleReq1(sessionP); | |||
574 | } | |||
575 | ||||
576 | assert(srvP->defaultHandler)((srvP->defaultHandler) ? (void) (0) : __assert_fail ("srvP->defaultHandler" , "../../../../libs/xmlrpc-c/lib/abyss/src/server.c", 576, __PRETTY_FUNCTION__ )); | |||
577 | ||||
578 | if (!handled) | |||
579 | srvP->defaultHandler(sessionP); | |||
580 | } | |||
581 | ||||
582 | ||||
583 | ||||
584 | static void | |||
585 | handleReqTooNewHttpVersion(TSession * const sessionP) { | |||
586 | ||||
587 | const char * msg; | |||
588 | ||||
589 | ResponseStatus(sessionP, 505); | |||
590 | ||||
591 | xmlrpc_asprintf(&msg, "Request is in HTTP Version %u" | |||
592 | "We understand only HTTP 1", | |||
593 | sessionP->version.major); | |||
594 | ||||
595 | ResponseError2(sessionP, msg); | |||
596 | ||||
597 | xmlrpc_strfree(msg); | |||
598 | } | |||
599 | ||||
600 | ||||
601 | ||||
602 | static void | |||
603 | handleReqInvalidURI(TSession * const sessionP) { | |||
604 | ||||
605 | ResponseStatus(sessionP, 400); | |||
606 | ||||
607 | ResponseError2(sessionP, "Invalid URI"); | |||
608 | } | |||
609 | ||||
610 | ||||
611 | ||||
612 | static void | |||
613 | processRequestFromClient(TConn * const connectionP, | |||
614 | bool const lastReqOnConn, | |||
615 | uint32_t const timeout, | |||
616 | bool * const keepAliveP) { | |||
617 | /*---------------------------------------------------------------------------- | |||
618 | Get and execute one HTTP request from client connection *connectionP, | |||
619 | through the connection buffer. I.e. Some of the request may already be in | |||
620 | the connection buffer, and we may leave some of later requests in the | |||
621 | connection buffer. | |||
622 | ||||
623 | In fact, due to timing considerations, we assume the client has begun | |||
624 | sending the request, which as a practical matter means Caller has already | |||
625 | deposited some of it in the connection buffer. | |||
626 | ||||
627 | If there isn't one full request in the buffer now, we wait for one full | |||
628 | request to come through the buffer, up to 'timeout'. | |||
629 | ||||
630 | We return as *keepAliveP whether Caller should keep the connection | |||
631 | alive for a while for possible future requests from the client, based | |||
632 | on 'lastReqOnConn' and the content of the HTTP request. | |||
633 | ||||
634 | Executing the request consists primarily of calling the URI handlers that | |||
635 | are associated with the connection (*connectionP), passing each the request | |||
636 | information we read. Each handler can respond according to the HTTP method | |||
637 | (GET, POST, etc) and URL etc, and that response may be either to | |||
638 | execute the request and send the response or refuse the request and let | |||
639 | us call the next one in the list. | |||
640 | -----------------------------------------------------------------------------*/ | |||
641 | TSession session; | |||
642 | const char * error; | |||
643 | uint16_t httpErrorCode; | |||
644 | ||||
645 | RequestInit(&session, connectionP); | |||
646 | ||||
647 | session.serverDeniesKeepalive = lastReqOnConn; | |||
648 | ||||
649 | RequestRead(&session, timeout, &error, &httpErrorCode); | |||
650 | ||||
651 | if (error) { | |||
652 | ResponseStatus(&session, httpErrorCode); | |||
653 | ResponseError2(&session, error); | |||
654 | xmlrpc_strfree(error); | |||
655 | } else { | |||
656 | if (session.version.major >= 2) | |||
657 | handleReqTooNewHttpVersion(&session); | |||
658 | else if (!RequestValidURI(&session)) | |||
659 | handleReqInvalidURI(&session); | |||
660 | else | |||
661 | runUserHandler(&session, connectionP->server->srvP); | |||
662 | } | |||
663 | ||||
664 | assert(session.status != 0)((session.status != 0) ? (void) (0) : __assert_fail ("session.status != 0" , "../../../../libs/xmlrpc-c/lib/abyss/src/server.c", 664, __PRETTY_FUNCTION__ )); | |||
665 | ||||
666 | if (session.responseStarted) | |||
667 | HTTPWriteEndChunk(&session); | |||
668 | else | |||
669 | ResponseError(&session); | |||
670 | ||||
671 | *keepAliveP = HTTPKeepalive(&session); | |||
672 | ||||
673 | SessionLog(&session); | |||
674 | ||||
675 | RequestFree(&session); | |||
676 | } | |||
677 | ||||
678 | ||||
679 | ||||
680 | static TThreadProc serverFunc; | |||
681 | ||||
682 | static void | |||
683 | serverFunc(void * const userHandle) { | |||
684 | /*---------------------------------------------------------------------------- | |||
685 | Do server stuff on one connection. At its simplest, this means do | |||
686 | one HTTP request. But with keepalive, it can be many requests. | |||
687 | -----------------------------------------------------------------------------*/ | |||
688 | TConn * const connectionP = userHandle; | |||
689 | struct _TServer * const srvP = connectionP->server->srvP; | |||
690 | ||||
691 | unsigned int requestCount; | |||
692 | /* Number of requests we've handled so far on this connection */ | |||
693 | bool connectionDone; | |||
694 | /* No more need for this HTTP connection */ | |||
695 | ||||
696 | requestCount = 0; | |||
697 | connectionDone = FALSE0; | |||
698 | ||||
699 | while (!connectionDone) { | |||
700 | bool timedOut, eof; | |||
701 | const char * readError; | |||
702 | ||||
703 | /* Wait for and get beginning (at least ) of next request. We do | |||
704 | this separately from getting the rest of the request because we | |||
705 | treat dead time between requests differently from dead time in | |||
706 | the middle of a request. | |||
707 | */ | |||
708 | ConnRead(connectionP, srvP->keepalivetimeout, | |||
709 | &timedOut, &eof, &readError); | |||
710 | ||||
711 | if (readError) { | |||
712 | TraceMsg("Failed to read from Abyss connection. %s", readError); | |||
713 | xmlrpc_strfree(readError); | |||
714 | connectionDone = TRUE1; | |||
715 | } else if (timedOut) { | |||
716 | connectionDone = TRUE1; | |||
717 | } else if (eof) { | |||
718 | connectionDone = TRUE1; | |||
719 | } else if (srvP->terminationRequested) { | |||
720 | connectionDone = TRUE1; | |||
721 | } else { | |||
722 | bool const lastReqOnConn = | |||
723 | requestCount + 1 >= srvP->keepalivemaxconn; | |||
724 | ||||
725 | bool keepalive; | |||
726 | ||||
727 | processRequestFromClient(connectionP, lastReqOnConn, srvP->timeout, | |||
728 | &keepalive); | |||
729 | ||||
730 | ++requestCount; | |||
731 | ||||
732 | if (!keepalive) | |||
733 | connectionDone = TRUE1; | |||
734 | ||||
735 | /**************** Must adjust the read buffer *****************/ | |||
736 | ConnReadInit(connectionP); | |||
737 | } | |||
738 | } | |||
739 | } | |||
740 | ||||
741 | ||||
742 | ||||
743 | /* This is the maximum amount of stack space, in bytes, serverFunc() | |||
744 | itself requires -- not counting what the user's request handler | |||
745 | (which serverFunc() calls) requires. | |||
746 | */ | |||
747 | #define SERVER_FUNC_STACK1024 1024 | |||
748 | ||||
749 | ||||
750 | ||||
751 | static void | |||
752 | createSwitchFromPortNum(unsigned short const portNumber, | |||
753 | TChanSwitch ** const chanSwitchPP, | |||
754 | const char ** const errorP) { | |||
755 | ||||
756 | #ifdef WIN32 | |||
757 | ChanSwitchWinCreate(portNumber, chanSwitchPP, errorP); | |||
758 | #else | |||
759 | ChanSwitchUnixCreate(portNumber, chanSwitchPP, errorP); | |||
760 | #endif | |||
761 | } | |||
762 | ||||
763 | ||||
764 | ||||
765 | static void | |||
766 | createChanSwitch(struct _TServer * const srvP, | |||
767 | const char ** const errorP) { | |||
768 | ||||
769 | TChanSwitch * chanSwitchP; | |||
770 | const char * error; | |||
771 | ||||
772 | /* Not valid to call this when channel switch already exists: */ | |||
773 | assert(srvP->chanSwitchP == NULL)((srvP->chanSwitchP == ((void*)0)) ? (void) (0) : __assert_fail ("srvP->chanSwitchP == ((void*)0)", "../../../../libs/xmlrpc-c/lib/abyss/src/server.c" , 773, __PRETTY_FUNCTION__)); | |||
774 | ||||
775 | createSwitchFromPortNum(srvP->port, &chanSwitchP, &error); | |||
776 | ||||
777 | if (error) { | |||
778 | xmlrpc_asprintf(errorP, | |||
779 | "Can't create channel switch. %s", error); | |||
780 | xmlrpc_strfree(error); | |||
781 | } else { | |||
782 | *errorP = NULL((void*)0); | |||
783 | ||||
784 | srvP->weCreatedChanSwitch = TRUE1; | |||
785 | srvP->chanSwitchP = chanSwitchP; | |||
786 | } | |||
787 | } | |||
788 | ||||
789 | ||||
790 | ||||
791 | int | |||
792 | ServerInit(TServer * const serverP) { | |||
793 | /*---------------------------------------------------------------------------- | |||
794 | Initialize a server to accept connections. | |||
795 | ||||
796 | Do not confuse this with creating the server -- ServerCreate(). | |||
797 | ||||
798 | Not necessary or valid with a server that doesn't accept connections (i.e. | |||
799 | user supplies the channels (TCP connections)). | |||
800 | -----------------------------------------------------------------------------*/ | |||
801 | struct _TServer * const srvP = serverP->srvP; | |||
802 | const char * retError; | |||
803 | ||||
804 | if (!srvP->serverAcceptsConnections) | |||
805 | xmlrpc_asprintf(&retError, | |||
806 | "ServerInit() is not valid on a server that doesn't " | |||
807 | "accept connections " | |||
808 | "(i.e. created with ServerCreateNoAccept)"); | |||
809 | else { | |||
810 | retError = NULL((void*)0); /* initial value */ | |||
811 | ||||
812 | if (!srvP->chanSwitchP) { | |||
813 | const char * error; | |||
814 | createChanSwitch(srvP, &error); | |||
815 | ||||
816 | if (error) { | |||
817 | xmlrpc_asprintf(&retError, "Unable to create a channel switch " | |||
818 | "for the server. %s", error); | |||
819 | xmlrpc_strfree(error); | |||
820 | } | |||
821 | } | |||
822 | ||||
823 | if (!retError) { | |||
824 | const char * error; | |||
825 | ||||
826 | assert(srvP->chanSwitchP)((srvP->chanSwitchP) ? (void) (0) : __assert_fail ("srvP->chanSwitchP" , "../../../../libs/xmlrpc-c/lib/abyss/src/server.c", 826, __PRETTY_FUNCTION__ )); | |||
827 | ||||
828 | ChanSwitchListen(srvP->chanSwitchP, MAX_CONN16, &error); | |||
829 | ||||
830 | if (error) { | |||
831 | xmlrpc_asprintf(&retError, | |||
832 | "Failed to listen on bound socket. %s", | |||
833 | error); | |||
834 | xmlrpc_strfree(error); | |||
835 | } | |||
836 | } | |||
837 | } | |||
838 | if (retError) { | |||
839 | TraceMsg("ServerInit() failed. %s", retError); | |||
840 | xmlrpc_strfree(retError); | |||
841 | return 0; | |||
842 | } | |||
843 | ||||
844 | return 1; | |||
845 | } | |||
846 | ||||
847 | ||||
848 | ||||
849 | /* We don't do any locking on the outstanding connections list, so | |||
850 | we must make sure that only the master thread (the one that listens | |||
851 | for connections) ever accesses it. | |||
852 | ||||
853 | That's why when a thread completes, it places the connection in | |||
854 | "finished" status, but doesn't destroy the connection. | |||
855 | */ | |||
856 | ||||
857 | typedef struct { | |||
858 | ||||
859 | TConn * firstP; | |||
860 | unsigned int count; | |||
861 | /* Redundant with 'firstP', for quick access */ | |||
862 | } outstandingConnList; | |||
863 | ||||
864 | ||||
865 | ||||
866 | static void | |||
867 | createOutstandingConnList(outstandingConnList ** const listPP) { | |||
868 | ||||
869 | outstandingConnList * listP; | |||
870 | ||||
871 | MALLOCVAR_NOFAIL(listP)do {if ((listP = malloc(sizeof(*listP))) == ((void*)0)) abort ();} while(0); | |||
872 | ||||
873 | listP->firstP = NULL((void*)0); /* empty list */ | |||
874 | listP->count = 0; | |||
875 | ||||
876 | *listPP = listP; | |||
877 | } | |||
878 | ||||
879 | ||||
880 | ||||
881 | static void | |||
882 | destroyOutstandingConnList(outstandingConnList * const listP) { | |||
883 | ||||
884 | assert(listP->firstP == NULL)((listP->firstP == ((void*)0)) ? (void) (0) : __assert_fail ("listP->firstP == ((void*)0)", "../../../../libs/xmlrpc-c/lib/abyss/src/server.c" , 884, __PRETTY_FUNCTION__)); | |||
885 | assert(listP->count == 0)((listP->count == 0) ? (void) (0) : __assert_fail ("listP->count == 0" , "../../../../libs/xmlrpc-c/lib/abyss/src/server.c", 885, __PRETTY_FUNCTION__ )); | |||
886 | ||||
887 | free(listP); | |||
888 | } | |||
889 | ||||
890 | ||||
891 | ||||
892 | static void | |||
893 | addToOutstandingConnList(outstandingConnList * const listP, | |||
894 | TConn * const connectionP) { | |||
895 | ||||
896 | connectionP->nextOutstandingP = listP->firstP; | |||
897 | ||||
898 | listP->firstP = connectionP; | |||
899 | ||||
900 | ++listP->count; | |||
901 | } | |||
902 | ||||
903 | ||||
904 | ||||
905 | static void | |||
906 | freeFinishedConns(outstandingConnList * const listP) { | |||
907 | /*---------------------------------------------------------------------------- | |||
908 | Garbage-collect the resources associated with connections that are | |||
909 | finished with their jobs. Thread resources, connection pool | |||
910 | descriptor, etc. | |||
911 | -----------------------------------------------------------------------------*/ | |||
912 | TConn ** pp; | |||
913 | ||||
914 | pp = &listP->firstP; | |||
915 | ||||
916 | while (*pp) { | |||
917 | TConn * const connectionP = (*pp); | |||
918 | ||||
919 | ThreadUpdateStatus(connectionP->threadP); | |||
920 | ||||
921 | if (connectionP->finished) { | |||
922 | /* Take it out of the list */ | |||
923 | *pp = connectionP->nextOutstandingP; | |||
924 | --listP->count; | |||
925 | ||||
926 | ConnWaitAndRelease(connectionP); | |||
927 | } else { | |||
928 | /* Move to next connection in list */ | |||
929 | pp = &connectionP->nextOutstandingP; | |||
930 | } | |||
931 | } | |||
932 | } | |||
933 | ||||
934 | ||||
935 | ||||
936 | static void | |||
937 | waitForConnectionFreed(outstandingConnList * const outstandingConnListP | |||
938 | ATTR_UNUSED__attribute__((__unused__))) { | |||
939 | /*---------------------------------------------------------------------------- | |||
940 | Wait for a connection descriptor in 'connectionPool' to be probably | |||
941 | freed. | |||
942 | -----------------------------------------------------------------------------*/ | |||
943 | ||||
944 | /* TODO: We should do something more sophisticated here. For pthreads, | |||
945 | we can have a thread signal us by semaphore when it terminates. | |||
946 | For fork, we might be able to use the "done" handler argument | |||
947 | to ConnCreate() to get interrupted when the death of a child | |||
948 | signal happens. | |||
949 | */ | |||
950 | ||||
951 | xmlrpc_millisecond_sleep(2000); | |||
952 | } | |||
953 | ||||
954 | ||||
955 | ||||
956 | static void | |||
957 | waitForNoConnections(outstandingConnList * const outstandingConnListP) { | |||
958 | ||||
959 | while (outstandingConnListP->firstP) { | |||
960 | freeFinishedConns(outstandingConnListP); | |||
961 | ||||
962 | if (outstandingConnListP->firstP) | |||
963 | waitForConnectionFreed(outstandingConnListP); | |||
964 | } | |||
965 | } | |||
966 | ||||
967 | ||||
968 | ||||
969 | static void | |||
970 | waitForConnectionCapacity(outstandingConnList * const outstandingConnListP) { | |||
971 | /*---------------------------------------------------------------------------- | |||
972 | Wait until there are fewer than the maximum allowed connections in | |||
973 | progress. | |||
974 | -----------------------------------------------------------------------------*/ | |||
975 | /* We need to make this number configurable. Note that MAX_CONN (16) is | |||
976 | also the backlog limit on the TCP socket, and they really aren't | |||
977 | related. As it stands, we can have 16 connections in progress inside | |||
978 | Abyss plus 16 waiting in the the channel switch. | |||
979 | */ | |||
980 | ||||
981 | while (outstandingConnListP->count >= MAX_CONN16) { | |||
982 | freeFinishedConns(outstandingConnListP); | |||
983 | if (outstandingConnListP->firstP) | |||
984 | waitForConnectionFreed(outstandingConnListP); | |||
985 | } | |||
986 | } | |||
987 | ||||
988 | ||||
989 | ||||
990 | #ifndef WIN32 | |||
991 | void | |||
992 | ServerHandleSigchld(pid_t const pid) { | |||
993 | ||||
994 | ThreadHandleSigchld(pid); | |||
995 | } | |||
996 | #endif | |||
997 | ||||
998 | ||||
999 | ||||
1000 | void | |||
1001 | ServerUseSigchld(TServer * const serverP) { | |||
1002 | ||||
1003 | struct _TServer * const srvP = serverP->srvP; | |||
1004 | ||||
1005 | srvP->useSigchld = TRUE1; | |||
1006 | } | |||
1007 | ||||
1008 | ||||
1009 | ||||
1010 | static TThreadDoneFn destroyChannel; | |||
1011 | ||||
1012 | static void | |||
1013 | destroyChannel(void * const userHandle) { | |||
1014 | /*---------------------------------------------------------------------------- | |||
1015 | This is a "connection done" function for the connection the server | |||
1016 | serves. It gets called some time after the connection has done its | |||
1017 | thing. Its job is to clean up stuff the server created for use by | |||
1018 | the connection, but the server can't clean up because the | |||
1019 | connection might be processed asynchronously in a background | |||
1020 | thread. | |||
1021 | ||||
1022 | To wit, we destroy the connection's channel and release the memory | |||
1023 | for the information about that channel. | |||
1024 | -----------------------------------------------------------------------------*/ | |||
1025 | TConn * const connectionP = userHandle; | |||
1026 | ||||
1027 | ChannelDestroy(connectionP->channelP); | |||
1028 | free(connectionP->channelInfoP); | |||
1029 | } | |||
1030 | ||||
1031 | ||||
1032 | ||||
1033 | static void | |||
1034 | acceptAndProcessNextConnection( | |||
1035 | TServer * const serverP, | |||
1036 | outstandingConnList * const outstandingConnListP) { | |||
1037 | ||||
1038 | struct _TServer * const srvP = serverP->srvP; | |||
1039 | ||||
1040 | TConn * connectionP; | |||
1041 | const char * error; | |||
1042 | TChannel * channelP; | |||
1043 | void * channelInfoP; | |||
1044 | ||||
1045 | ChanSwitchAccept(srvP->chanSwitchP, &channelP, &channelInfoP, &error); | |||
1046 | ||||
1047 | if (error) { | |||
1048 | TraceMsg("Failed to accept the next connection from a client " | |||
1049 | "at the channel level. %s", error); | |||
1050 | xmlrpc_strfree(error); | |||
1051 | } else { | |||
1052 | if (channelP) { | |||
1053 | const char * error; | |||
1054 | ||||
1055 | freeFinishedConns(outstandingConnListP); | |||
1056 | ||||
1057 | waitForConnectionCapacity(outstandingConnListP); | |||
1058 | ||||
1059 | ConnCreate(&connectionP, serverP, channelP, channelInfoP, | |||
1060 | &serverFunc, | |||
1061 | SERVER_FUNC_STACK1024 + srvP->uriHandlerStackSize, | |||
1062 | &destroyChannel, ABYSS_BACKGROUND, | |||
1063 | srvP->useSigchld, | |||
1064 | &error); | |||
1065 | if (!error) { | |||
1066 | addToOutstandingConnList(outstandingConnListP, | |||
1067 | connectionP); | |||
1068 | ConnProcess(connectionP); | |||
1069 | /* When connection is done (which could be later, courtesy | |||
1070 | of a background thread), destroyChannel() will | |||
1071 | destroy *channelP. | |||
1072 | */ | |||
1073 | } else { | |||
1074 | TraceMsg("Failed to create an Abyss connection " | |||
1075 | "out of new channel %lx. %s", channelP, error); | |||
1076 | xmlrpc_strfree(error); | |||
1077 | ChannelDestroy(channelP); | |||
1078 | free(channelInfoP); | |||
1079 | } | |||
1080 | } else { | |||
1081 | /* Accept function was interrupted before it got a connection */ | |||
1082 | } | |||
1083 | } | |||
1084 | } | |||
1085 | ||||
1086 | ||||
1087 | ||||
1088 | static void | |||
1089 | serverRun2(TServer * const serverP) { | |||
1090 | ||||
1091 | struct _TServer * const srvP = serverP->srvP; | |||
1092 | outstandingConnList * outstandingConnListP; | |||
1093 | ||||
1094 | createOutstandingConnList(&outstandingConnListP); | |||
1095 | ||||
1096 | while (!srvP->terminationRequested) | |||
1097 | acceptAndProcessNextConnection(serverP, outstandingConnListP); | |||
1098 | ||||
1099 | waitForNoConnections(outstandingConnListP); | |||
1100 | ||||
1101 | destroyOutstandingConnList(outstandingConnListP); | |||
1102 | } | |||
1103 | ||||
1104 | ||||
1105 | ||||
1106 | void | |||
1107 | ServerRun(TServer * const serverP) { | |||
1108 | ||||
1109 | struct _TServer * const srvP = serverP->srvP; | |||
1110 | ||||
1111 | if (!srvP->chanSwitchP) | |||
1112 | TraceMsg("This server is not set up to accept connections " | |||
1113 | "on its own, so you can't use ServerRun(). " | |||
1114 | "Try ServerRunConn() or ServerInit()"); | |||
1115 | else | |||
1116 | serverRun2(serverP); | |||
1117 | } | |||
1118 | ||||
1119 | ||||
1120 | ||||
1121 | static void | |||
1122 | serverRunChannel(TServer * const serverP, | |||
1123 | TChannel * const channelP, | |||
1124 | void * const channelInfoP, | |||
1125 | const char ** const errorP) { | |||
1126 | /*---------------------------------------------------------------------------- | |||
1127 | Do the HTTP transaction on the channel 'channelP'. | |||
1128 | (channel must be at the beginning of the HTTP request -- nothing having | |||
1129 | been read or written yet). | |||
1130 | ||||
1131 | channelInfoP == NULL means no channel info supplied. | |||
1132 | -----------------------------------------------------------------------------*/ | |||
1133 | struct _TServer * const srvP = serverP->srvP; | |||
1134 | ||||
1135 | TConn * connectionP; | |||
1136 | const char * error; | |||
1137 | ||||
1138 | srvP->keepalivemaxconn = 1; | |||
1139 | ||||
1140 | ConnCreate(&connectionP, | |||
1141 | serverP, channelP, channelInfoP, | |||
1142 | &serverFunc, SERVER_FUNC_STACK1024 + srvP->uriHandlerStackSize, | |||
1143 | NULL((void*)0), ABYSS_FOREGROUND, srvP->useSigchld, | |||
1144 | &error); | |||
1145 | if (error) { | |||
1146 | xmlrpc_asprintf(errorP, "Couldn't create HTTP connection out of " | |||
1147 | "channel (connected socket). %s", error); | |||
1148 | xmlrpc_strfree(error); | |||
1149 | } else { | |||
1150 | *errorP = NULL((void*)0); | |||
1151 | ||||
1152 | ConnProcess(connectionP); | |||
1153 | ||||
1154 | ConnWaitAndRelease(connectionP); | |||
1155 | } | |||
1156 | } | |||
1157 | ||||
1158 | ||||
1159 | ||||
1160 | void | |||
1161 | ServerRunChannel(TServer * const serverP, | |||
1162 | TChannel * const channelP, | |||
1163 | void * const channelInfoP, | |||
1164 | const char ** const errorP) { | |||
1165 | /*---------------------------------------------------------------------------- | |||
1166 | Do the HTTP transaction on the channel 'channelP'. | |||
1167 | ||||
1168 | (channel must be at the beginning of the HTTP request -- nothing having | |||
1169 | been read or written yet). | |||
1170 | -----------------------------------------------------------------------------*/ | |||
1171 | struct _TServer * const srvP = serverP->srvP; | |||
1172 | ||||
1173 | if (srvP->serverAcceptsConnections) | |||
1174 | xmlrpc_asprintf(errorP, | |||
1175 | "This server is configured to accept connections on " | |||
1176 | "its own socket. " | |||
1177 | "Try ServerRun() or ServerCreateNoAccept()."); | |||
1178 | else | |||
1179 | serverRunChannel(serverP, channelP, channelInfoP, errorP); | |||
1180 | } | |||
1181 | ||||
1182 | ||||
1183 | ||||
1184 | void | |||
1185 | ServerRunConn2(TServer * const serverP, | |||
1186 | TSocket * const connectedSocketP, | |||
1187 | const char ** const errorP) { | |||
1188 | /*---------------------------------------------------------------------------- | |||
1189 | Do the HTTP transaction on the TCP connection on the socket | |||
1190 | *connectedSocketP. | |||
1191 | (socket must be connected state, with nothing having been read or | |||
1192 | written on the connection yet). | |||
1193 | -----------------------------------------------------------------------------*/ | |||
1194 | TChannel * const channelP = SocketGetChannel(connectedSocketP); | |||
1195 | ||||
1196 | if (!channelP) | |||
1197 | xmlrpc_asprintf(errorP, "The socket supplied is not a connected " | |||
1198 | "socket. You should use ServerRunChannel() instead, " | |||
1199 | "anyway."); | |||
1200 | else | |||
1201 | ServerRunChannel(serverP, | |||
1202 | channelP, SocketGetChannelInfo(connectedSocketP), | |||
1203 | errorP); | |||
1204 | } | |||
1205 | ||||
1206 | ||||
1207 | ||||
1208 | void | |||
1209 | ServerRunConn(TServer * const serverP, | |||
1210 | TOsSocket const connectedOsSocket) { | |||
1211 | ||||
1212 | TChannel * channelP; | |||
1213 | void * channelInfoP; | |||
1214 | const char * error; | |||
1215 | ||||
1216 | createChannelFromOsSocket(connectedOsSocket, | |||
1217 | &channelP, &channelInfoP, &error); | |||
1218 | if (error) { | |||
1219 | TraceExit("Unable to use supplied socket"); | |||
1220 | xmlrpc_strfree(error); | |||
1221 | } else { | |||
1222 | const char * error; | |||
1223 | ||||
1224 | ServerRunChannel(serverP, channelP, channelInfoP, &error); | |||
1225 | ||||
1226 | if (error) { | |||
1227 | TraceExit("Failed to run server on connection on file " | |||
1228 | "descriptor %d. %s", connectedOsSocket, error); | |||
1229 | xmlrpc_strfree(error); | |||
1230 | } | |||
1231 | ChannelDestroy(channelP); | |||
1232 | free(channelInfoP); | |||
1233 | } | |||
1234 | } | |||
1235 | ||||
1236 | ||||
1237 | ||||
1238 | void | |||
1239 | ServerRunOnce(TServer * const serverP) { | |||
1240 | /*---------------------------------------------------------------------------- | |||
1241 | Accept a connection from the channel switch and do the HTTP | |||
1242 | transaction that comes over it. | |||
1243 | ||||
1244 | If no connection is presently waiting at the switch, wait for one. | |||
1245 | But return immediately if we receive a signal during the wait. | |||
1246 | -----------------------------------------------------------------------------*/ | |||
1247 | struct _TServer * const srvP = serverP->srvP; | |||
1248 | ||||
1249 | if (!srvP->chanSwitchP) | |||
1250 | TraceMsg("This server is not set up to accept connections " | |||
1251 | "on its own, so you can't use ServerRunOnce(). " | |||
1252 | "Try ServerRunChannel() or ServerInit()"); | |||
1253 | else { | |||
1254 | const char * error; | |||
1255 | TChannel * channelP; | |||
1256 | void * channelInfoP; | |||
1257 | ||||
1258 | srvP->keepalivemaxconn = 1; | |||
1259 | ||||
1260 | ChanSwitchAccept(srvP->chanSwitchP, &channelP, &channelInfoP, &error); | |||
1261 | if (error) { | |||
1262 | TraceMsg("Failed to accept the next connection from a client " | |||
1263 | "at the channel level. %s", error); | |||
1264 | xmlrpc_strfree(error); | |||
1265 | } else { | |||
1266 | if (channelP) { | |||
1267 | const char * error; | |||
1268 | ||||
1269 | serverRunChannel(serverP, channelP, channelInfoP, &error); | |||
1270 | ||||
1271 | if (error) { | |||
1272 | const char * peerDesc; | |||
1273 | ChannelFormatPeerInfo(channelP, &peerDesc); | |||
1274 | TraceExit("Got a connection from '%s', but failed to " | |||
1275 | "run server on it. %s", peerDesc, error); | |||
1276 | xmlrpc_strfree(peerDesc); | |||
1277 | xmlrpc_strfree(error); | |||
1278 | } | |||
1279 | ChannelDestroy(channelP); | |||
1280 | free(channelInfoP); | |||
1281 | } | |||
1282 | } | |||
1283 | } | |||
1284 | } | |||
1285 | ||||
1286 | ||||
1287 | ||||
1288 | void | |||
1289 | ServerRunOnce2(TServer * const serverP, | |||
1290 | enum abyss_foreback const foregroundBackground ATTR_UNUSED__attribute__((__unused__))) { | |||
1291 | /*---------------------------------------------------------------------------- | |||
1292 | This is a backward compatibility interface to ServerRunOnce(). | |||
1293 | ||||
1294 | 'foregroundBackground' is meaningless. We always process the | |||
1295 | connection in the foreground. The parameter exists because we once | |||
1296 | thought we could do them in the background, but we really can't do | |||
1297 | that in any clean way. If Caller wants background execution, he can | |||
1298 | spin his own thread or process to call us. It makes much more sense | |||
1299 | in Caller's context. | |||
1300 | -----------------------------------------------------------------------------*/ | |||
1301 | ServerRunOnce(serverP); | |||
1302 | } | |||
1303 | ||||
1304 | ||||
1305 | ||||
1306 | static void | |||
1307 | setGroups(void) { | |||
1308 | ||||
1309 | #if HAVE_SETGROUPS1 | |||
1310 | if (setgroups(0, NULL((void*)0)) == (-1)) | |||
1311 | TraceExit("Failed to setup the group."); | |||
1312 | #endif | |||
1313 | } | |||
1314 | ||||
1315 | ||||
1316 | ||||
1317 | void | |||
1318 | ServerDaemonize(TServer * const serverP) { | |||
1319 | /*---------------------------------------------------------------------------- | |||
1320 | Turn Caller into a daemon (i.e. fork a child, then exit; the child | |||
1321 | returns to Caller). | |||
1322 | ||||
1323 | NOTE: It's ridiculous, but conventional, for us to do this. It's | |||
1324 | ridiculous because the task of daemonizing is not something | |||
1325 | particular to Abyss. It ought to be done by a higher level. In | |||
1326 | fact, it should be done before the Abyss server program is even | |||
1327 | execed. The user should run a "daemonize" program that creates a | |||
1328 | daemon which execs the Abyss server program. | |||
1329 | -----------------------------------------------------------------------------*/ | |||
1330 | struct _TServer * const srvP = serverP->srvP; | |||
1331 | ||||
1332 | #ifndef _WIN32 | |||
1333 | /* Become a daemon */ | |||
1334 | switch (fork()) { | |||
1335 | case 0: | |||
1336 | break; | |||
1337 | case -1: | |||
1338 | TraceExit("Unable to become a daemon"); | |||
1339 | default: | |||
1340 | /* We are the parent */ | |||
1341 | exit(0); | |||
1342 | } | |||
1343 | ||||
1344 | setsid(); | |||
1345 | ||||
1346 | /* Change the current user if we are root */ | |||
1347 | if (getuid()==0) { | |||
1348 | if (srvP->uid == (uid_t)-1) | |||
1349 | TraceExit("Can't run under root privileges. " | |||
1350 | "Please add a User option in your " | |||
1351 | "Abyss configuration file."); | |||
1352 | ||||
1353 | setGroups(); | |||
1354 | ||||
1355 | if (srvP->gid != (gid_t)-1) | |||
1356 | if (setgid(srvP->gid)==(-1)) | |||
1357 | TraceExit("Failed to change the group."); | |||
1358 | ||||
1359 | if (setuid(srvP->uid) == -1) | |||
1360 | TraceExit("Failed to change the user."); | |||
1361 | } | |||
1362 | ||||
1363 | if (srvP->pidfileP) { | |||
1364 | char z[16]; | |||
1365 | ||||
1366 | sprintf(z, "%d", getpid()); | |||
1367 | FileWrite(srvP->pidfileP, z, strlen(z)); | |||
1368 | FileClose(srvP->pidfileP); | |||
1369 | } | |||
1370 | #endif /* _WIN32 */ | |||
1371 | } | |||
1372 | ||||
1373 | ||||
1374 | ||||
1375 | static void | |||
1376 | serverAddHandler(TServer * const serverP, | |||
1377 | initHandlerFn init, | |||
1378 | termHandlerFn term, | |||
1379 | URIHandler handleReq1, | |||
1380 | handleReq2Fn handleReq2, | |||
1381 | handleReq3Fn handleReq3, | |||
1382 | void * const userdata, | |||
1383 | size_t const handleReqStackSizeReq, | |||
1384 | abyss_bool * const successP) { | |||
1385 | ||||
1386 | struct _TServer * const srvP = serverP->srvP; | |||
1387 | size_t handleReqStackSize = | |||
1388 | handleReqStackSizeReq ? handleReqStackSizeReq : 128*1024; | |||
1389 | ||||
1390 | struct uriHandler * handlerP; | |||
1391 | ||||
1392 | MALLOCVAR(handlerP)handlerP = malloc(sizeof(*handlerP)); | |||
1393 | if (handlerP == NULL((void*)0)) | |||
1394 | *successP = FALSE0; | |||
1395 | else { | |||
1396 | handlerP->init = init; | |||
1397 | handlerP->term = term; | |||
1398 | handlerP->handleReq1 = handleReq1; | |||
1399 | handlerP->handleReq2 = handleReq2; | |||
1400 | handlerP->handleReq3 = handleReq3; | |||
1401 | handlerP->userdata = userdata; | |||
1402 | ||||
1403 | srvP->uriHandlerStackSize = | |||
1404 | MAX(srvP->uriHandlerStackSize, handleReqStackSize)((srvP->uriHandlerStackSize) > (handleReqStackSize) ? ( srvP->uriHandlerStackSize) : (handleReqStackSize)); | |||
1405 | ||||
1406 | if (handlerP->init == NULL((void*)0)) | |||
1407 | *successP = TRUE1; | |||
1408 | else { | |||
1409 | URIHandler2 handler2 = makeUriHandler2(handlerP); | |||
1410 | handlerP->init(&handler2, successP); | |||
1411 | } | |||
1412 | if (*successP) | |||
1413 | *successP = ListAdd(&serverP->srvP->handlers, handlerP); | |||
1414 | ||||
1415 | if (!*successP) | |||
1416 | free(handlerP); | |||
1417 | } | |||
1418 | } | |||
1419 | ||||
1420 | ||||
1421 | ||||
1422 | void | |||
1423 | ServerAddHandler3(TServer * const serverP, | |||
1424 | const struct ServerReqHandler3 * const handlerP, | |||
1425 | abyss_bool * const successP) { | |||
1426 | ||||
1427 | serverAddHandler(serverP, NULL((void*)0), handlerP->term, NULL((void*)0), NULL((void*)0), | |||
1428 | handlerP->handleReq, handlerP->userdata, | |||
1429 | handlerP->handleReqStackSize, successP); | |||
1430 | } | |||
1431 | ||||
1432 | ||||
1433 | ||||
1434 | void | |||
1435 | ServerAddHandler2(TServer * const serverP, | |||
1436 | URIHandler2 * const handlerArgP, | |||
1437 | abyss_bool * const successP) { | |||
1438 | ||||
1439 | /* This generation of the URI handler interface is strange because | |||
1440 | it went through an unfortunate evolution. So it halfway looks like | |||
1441 | the use supplies a handler object and Abyss calls its methods, and | |||
1442 | halfway looks like the user simply describes his handler. | |||
1443 | ||||
1444 | Abyss calls handleReq2 with a pointer to a URIHandler2 like the | |||
1445 | one which is our argument, but it isn't the same one. User can | |||
1446 | discard *handlerArgP as soon as we return. | |||
1447 | */ | |||
1448 | ||||
1449 | serverAddHandler(serverP, | |||
1450 | handlerArgP->init, | |||
1451 | handlerArgP->term, | |||
1452 | handlerArgP->handleReq1, | |||
1453 | handlerArgP->handleReq2, | |||
1454 | NULL((void*)0), | |||
1455 | handlerArgP->userdata, | |||
1456 | 0, | |||
1457 | successP); | |||
1458 | } | |||
1459 | ||||
1460 | ||||
1461 | ||||
1462 | abyss_bool | |||
1463 | ServerAddHandler(TServer * const serverP, | |||
1464 | URIHandler const function) { | |||
1465 | ||||
1466 | URIHandler2 handler; | |||
1467 | abyss_bool success; | |||
1468 | ||||
1469 | handler.init = NULL((void*)0); | |||
1470 | handler.term = NULL((void*)0); | |||
1471 | handler.userdata = NULL((void*)0); | |||
1472 | handler.handleReq2 = NULL((void*)0); | |||
1473 | handler.handleReq1 = function; | |||
1474 | ||||
1475 | ServerAddHandler2(serverP, &handler, &success); | |||
1476 | ||||
1477 | return success; | |||
1478 | } | |||
1479 | ||||
1480 | ||||
1481 | ||||
1482 | /* This is the maximum amount of stack we allow a user's default URI | |||
1483 | handler to use. (If he exceeds this, results are undefined). | |||
1484 | ||||
1485 | We really ought to provide user a way to set this, as he can for | |||
1486 | his non-default URI handlers. | |||
1487 | */ | |||
1488 | #define USER_DEFAULT_HANDLER_STACK128*1024 128*1024 | |||
1489 | ||||
1490 | void | |||
1491 | ServerDefaultHandler(TServer * const serverP, | |||
1492 | URIHandler const handler) { | |||
1493 | ||||
1494 | struct _TServer * const srvP = serverP->srvP; | |||
1495 | ||||
1496 | if (handler) { | |||
1497 | srvP->defaultHandler = handler; | |||
1498 | srvP->uriHandlerStackSize = | |||
1499 | MAX(srvP->uriHandlerStackSize, USER_DEFAULT_HANDLER_STACK)((srvP->uriHandlerStackSize) > (128*1024) ? (srvP->uriHandlerStackSize ) : (128*1024)); | |||
1500 | } else { | |||
1501 | srvP->defaultHandler = HandlerDefaultBuiltin; | |||
1502 | srvP->defaultHandlerContext = srvP->builtinHandlerP; | |||
1503 | srvP->uriHandlerStackSize = | |||
1504 | MAX(srvP->uriHandlerStackSize, HandlerDefaultBuiltinStack)((srvP->uriHandlerStackSize) > (HandlerDefaultBuiltinStack ) ? (srvP->uriHandlerStackSize) : (HandlerDefaultBuiltinStack )); | |||
1505 | } | |||
1506 | } | |||
1507 | ||||
1508 | ||||
1509 | ||||
1510 | void | |||
1511 | LogWrite(TServer * const serverP, | |||
1512 | const char * const msg) { | |||
1513 | ||||
1514 | struct _TServer * const srvP = serverP->srvP; | |||
1515 | ||||
1516 | if (!srvP->logfileisopen && srvP->logfilename) | |||
1517 | logOpen(srvP); | |||
1518 | ||||
1519 | if (srvP->logfileisopen) { | |||
1520 | bool success; | |||
1521 | success = MutexLock(srvP->logmutexP); | |||
1522 | if (success) { | |||
1523 | const char * const lbr = "\n"; | |||
1524 | FileWrite(srvP->logfileP, msg, strlen(msg)); | |||
1525 | FileWrite(srvP->logfileP, lbr, strlen(lbr)); | |||
1526 | ||||
1527 | MutexUnlock(srvP->logmutexP); | |||
1528 | } | |||
1529 | } | |||
1530 | } | |||
1531 | /******************************************************************************* | |||
1532 | ** | |||
1533 | ** server.c | |||
1534 | ** | |||
1535 | ** This file is part of the ABYSS Web server project. | |||
1536 | ** | |||
1537 | ** Copyright (C) 2000 by Moez Mahfoudh <mmoez@bigfoot.com>. | |||
1538 | ** All rights reserved. | |||
1539 | ** | |||
1540 | ** Redistribution and use in source and binary forms, with or without | |||
1541 | ** modification, are permitted provided that the following conditions | |||
1542 | ** are met: | |||
1543 | ** 1. Redistributions of source code must retain the above copyright | |||
1544 | ** notice, this list of conditions and the following disclaimer. | |||
1545 | ** 2. Redistributions in binary form must reproduce the above copyright | |||
1546 | ** notice, this list of conditions and the following disclaimer in the | |||
1547 | ** documentation and/or other materials provided with the distribution. | |||
1548 | ** 3. The name of the author may not be used to endorse or promote products | |||
1549 | ** derived from this software without specific prior written permission. | |||
1550 | ** | |||
1551 | ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |||
1552 | ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||
1553 | ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||
1554 | ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |||
1555 | ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||
1556 | ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||
1557 | ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||
1558 | ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||
1559 | ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||
1560 | ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
1561 | ** SUCH DAMAGE. | |||
1562 | ** | |||
1563 | ******************************************************************************/ |