1 /* Copyright information is at end of file */
16 #include "xmlrpc_config.h"
17 #include "mallocvar.h"
18 #include "xmlrpc-c/string_int.h"
19 #include "xmlrpc-c/sleep_int.h"
21 #include "xmlrpc-c/abyss.h"
27 #include "socket_win.h"
29 #include "socket_unix.h"
33 #include "abyss_info.h"
39 ServerTerminate(TServer * const serverP) {
41 struct _TServer * const srvP = serverP->srvP;
43 srvP->terminationRequested = true;
49 ServerResetTerminate(TServer * const serverP) {
51 struct _TServer * const srvP = serverP->srvP;
53 srvP->terminationRequested = false;
58 typedef int (*TQSortProc)(const void *, const void *);
61 cmpfilenames(const TFileInfo **f1,const TFileInfo **f2) {
62 if (((*f1)->attrib & A_SUBDIR) && !((*f2)->attrib & A_SUBDIR))
64 if (!((*f1)->attrib & A_SUBDIR) && ((*f2)->attrib & A_SUBDIR))
67 return strcmp((*f1)->name,(*f2)->name);
71 cmpfiledates(const TFileInfo **f1,const TFileInfo **f2) {
72 if (((*f1)->attrib & A_SUBDIR) && !((*f2)->attrib & A_SUBDIR))
74 if (!((*f1)->attrib & A_SUBDIR) && ((*f2)->attrib & A_SUBDIR))
77 return ((*f1)->time_write-(*f2)->time_write);
83 determineSortType(const char * const query,
84 abyss_bool * const ascendingP,
85 uint16_t * const sortP,
86 abyss_bool * const textP,
87 const char ** const errorP) {
95 if (xmlrpc_streq(query, "plain"))
97 else if (xmlrpc_streq(query, "name-up")) {
100 } else if (xmlrpc_streq(query, "name-down")) {
103 } else if (xmlrpc_streq(query, "date-up")) {
106 } else if (xmlrpc_streq(query, "date-down")) {
110 xmlrpc_asprintf(errorP, "invalid query value '%s'", query);
118 generateListing(TList * const listP,
120 const char * const uri,
122 const char ** const errorP,
123 uint16_t * const responseStatusP) {
126 TFileFind findhandle;
130 if (!FileFindFirst(&findhandle, z, &fileinfo)) {
131 *responseStatusP = ResponseStatusFromErrno(errno);
132 xmlrpc_asprintf(errorP, "Can't read first entry in directory");
138 /* Files whose names start with a dot are ignored */
139 /* This includes implicitly the ./ and ../ */
140 if (*fileinfo.name == '.') {
141 if (xmlrpc_streq(fileinfo.name, "..")) {
142 if (xmlrpc_streq(uri, "/"))
147 fi = (TFileInfo *)PoolAlloc(poolP, sizeof(fileinfo));
150 memcpy(fi, &fileinfo, sizeof(fileinfo));
151 success = ListAdd(listP, fi);
153 xmlrpc_asprintf(errorP, "ListAdd() failed");
155 xmlrpc_asprintf(errorP, "PoolAlloc() failed.");
156 } while (!*errorP && FileFindNext(&findhandle, &fileinfo));
159 *responseStatusP = 500;
162 FileFindClose(&findhandle);
169 sendDirectoryDocument(TList * const listP,
170 abyss_bool const ascending,
172 abyss_bool const text,
173 const char * const uri,
174 MIMEType * const mimeTypeP,
175 TSession * const sessionP,
178 char *p,z1[26],z2[20],z3[9],u;
184 sprintf(z, "Index of %s" CRLF, uri);
196 "Date-Time Type" CRLF
197 "------------------------------------"
198 "--------------------------------------------"CRLF);
200 sprintf(z, "<HTML><HEAD><TITLE>Index of %s</TITLE></HEAD><BODY>"
201 "<H1>Index of %s</H1><PRE>",
203 strcat(z, "Name Size "
204 "Date-Time Type<HR WIDTH=100%>"CRLF);
207 HTTPWriteBodyChunk(sessionP, z, strlen(z));
210 qsort(listP->item, listP->size, sizeof(void *),
211 (TQSortProc)(sort == 1 ? cmpfilenames : cmpfiledates));
213 /* Write the listing */
219 while ((i < listP->size) && (i >= 0)) {
234 if (fi->attrib & A_SUBDIR) {
243 strcat(z1, z + k - 11);
257 ftm = *gmtime(&fi->time_write);
258 sprintf(z2, "%02u/%02u/%04u %02u:%02u:%02u",ftm.tm_mday,ftm.tm_mon+1,
259 ftm.tm_year+1900,ftm.tm_hour,ftm.tm_min,ftm.tm_sec);
261 if (fi->attrib & A_SUBDIR) {
280 sprintf(z3, "%5llu %c", fi->size, u);
282 if (xmlrpc_streq(fi->name, ".."))
285 z4 = MIMETypeFromFileName2(mimeTypeP, fi->name);
292 sprintf(z, "%s%s %s %s %s"CRLF, z1, p, z3, z2, z4);
294 sprintf(z, "<A HREF=\"%s%s\">%s</A>%s %s %s %s"CRLF,
295 fi->name, fi->attrib & A_SUBDIR ? "/" : "",
298 HTTPWriteBodyChunk(sessionP, z, strlen(z));
301 /* Write the tail of the file */
303 strcpy(z, SERVER_PLAIN_INFO);
305 strcpy(z, "</PRE>" SERVER_HTML_INFO "</BODY></HTML>" CRLF CRLF);
307 HTTPWriteBodyChunk(sessionP, z, strlen(z));
313 fileDate(TSession * const sessionP,
314 time_t const statFilemodTime,
315 TDate * const fileDateP) {
320 haveDate = DateFromLocal(&filemodDate, statFilemodTime);
323 if (DateCompare(&sessionP->date, &filemodDate) < 0)
324 *fileDateP = sessionP->date;
326 *fileDateP = filemodDate;
328 *fileDateP = sessionP->date;
334 ServerDirectoryHandler(TSession * const r,
336 time_t const fileModTime,
337 MIMEType * const mimeTypeP) {
341 abyss_bool ascending;
342 uint16_t sort; /* 1=by name, 2=by date */
346 uint16_t responseStatus;
350 determineSortType(r->request_info.query, &ascending, &sort, &text, &error);
353 ResponseStatus(r,400);
354 xmlrpc_strfree(error);
358 fileDate(r, fileModTime, &dirdate);
360 imsHdr = RequestHeaderValue(r, "If-Modified-Since");
362 if (DateDecode(imsHdr, &date)) {
363 if (DateCompare(&dirdate, &date) <= 0) {
364 ResponseStatus(r, 304);
371 if (!PoolCreate(&pool, 1024)) {
372 ResponseStatus(r, 500);
376 generateListing(&list, z, r->request_info.uri,
377 &pool, &error, &responseStatus);
379 ResponseStatus(r, responseStatus);
380 xmlrpc_strfree(error);
385 /* Send something to the user to show that we are still alive */
386 ResponseStatus(r, 200);
387 ResponseContentType(r, (text ? "text/plain" : "text/html"));
389 if (DateToString(&dirdate, z))
390 ResponseAddField(r, "Last-Modified", z);
395 if (r->request_info.method!=m_head)
396 sendDirectoryDocument(&list, ascending, sort, text,
397 r->request_info.uri, mimeTypeP, r, z);
399 HTTPWriteEndChunk(r);
401 /* Free memory and exit */
410 #define BOUNDARY "##123456789###BOUNDARY"
413 sendBody(TSession * const sessionP,
415 uint64_t const filesize,
416 const char * const mediatype,
417 uint64_t const start0,
421 if (sessionP->ranges.size == 0)
422 ConnWriteFromFile(sessionP->conn, fileP, 0, filesize - 1, z, 4096, 0);
423 else if (sessionP->ranges.size == 1)
424 ConnWriteFromFile(sessionP->conn, fileP, start0, end0, z, 4096, 0);
427 for (i = 0; i <= sessionP->ranges.size; ++i) {
428 ConnWrite(sessionP->conn,"--", 2);
429 ConnWrite(sessionP->conn, BOUNDARY, strlen(BOUNDARY));
430 ConnWrite(sessionP->conn, CRLF, 2);
432 if (i < sessionP->ranges.size) {
437 decoded = RangeDecode((char *)(sessionP->ranges.item[i]),
441 /* Entity header, not response header */
442 sprintf(z, "Content-type: %s" CRLF
443 "Content-range: bytes %llu-%llu/%llu" CRLF
444 "Content-length: %llu" CRLF
445 CRLF, mediatype, start, end,
446 filesize, end-start+1);
448 ConnWrite(sessionP->conn, z, strlen(z));
450 ConnWriteFromFile(sessionP->conn, fileP, start, end, z,
461 ServerFileHandler(TSession * const r,
463 time_t const fileModTime,
464 MIMEType * const mimeTypeP) {
466 const char * mediatype;
475 mediatype = MIMETypeGuessFromFile2(mimeTypeP, z);
477 if (!FileOpen(&file,z,O_BINARY | O_RDONLY)) {
478 ResponseStatusErrno(r);
482 fileDate(r, fileModTime, &filedate);
484 p = RequestHeaderValue(r, "if-modified-since");
486 if (DateDecode(p,&date)) {
487 if (DateCompare(&filedate, &date) <= 0) {
488 ResponseStatus(r, 304);
495 filesize = FileSize(&file);
497 switch (r->ranges.size) {
499 ResponseStatus(r, 200);
504 decoded = RangeDecode((char *)(r->ranges.item[0]), filesize,
507 ListFree(&(r->ranges));
508 ResponseStatus(r, 200);
512 sprintf(z, "bytes %llu-%llu/%llu", start, end, filesize);
514 ResponseAddField(r, "Content-range", z);
515 ResponseContentLength(r, end - start + 1);
516 ResponseStatus(r, 206);
520 ResponseContentType(r, "multipart/ranges; boundary=" BOUNDARY);
521 ResponseStatus(r, 206);
525 if (r->ranges.size == 0) {
526 ResponseContentLength(r, filesize);
527 ResponseContentType(r, mediatype);
530 if (DateToString(&filedate, z))
531 ResponseAddField(r, "Last-Modified", z);
535 if (r->request_info.method != m_head)
536 sendBody(r, &file, filesize, mediatype, start, end, z);
546 ServerDefaultHandlerFunc(TSession * const sessionP) {
548 struct _TServer * const srvP = ConnServer(sessionP->conn)->srvP;
554 abyss_bool endingslash=FALSE;
556 if (!RequestValidURIPath(sessionP)) {
557 ResponseStatus(sessionP, 400);
561 /* Must check for * (asterisk uri) in the future */
562 if (sessionP->request_info.method == m_options) {
563 ResponseAddField(sessionP, "Allow", "GET, HEAD");
564 ResponseContentLength(sessionP, 0);
565 ResponseStatus(sessionP, 200);
569 if ((sessionP->request_info.method != m_get) &&
570 (sessionP->request_info.method != m_head)) {
571 ResponseAddField(sessionP, "Allow", "GET, HEAD");
572 ResponseStatus(sessionP, 405);
576 strcpy(z, srvP->filespath);
577 strcat(z, sessionP->request_info.uri);
579 p = z + strlen(z) - 1;
595 if (!FileStat(z, &fs)) {
596 ResponseStatusErrno(sessionP);
600 if (fs.st_mode & S_IFDIR) {
601 /* Redirect to the same directory but with the ending slash
602 ** to avoid problems with some browsers (IE for examples) when
603 ** they generate relative urls */
605 strcpy(z, sessionP->request_info.uri);
609 ResponseAddField(sessionP, "Location", z);
610 ResponseStatus(sessionP, 302);
611 ResponseWrite(sessionP);
615 *p = DIRECTORY_SEPARATOR[0];
618 i = srvP->defaultfilenames.size;
621 strcat(z, (srvP->defaultfilenames.item[i]));
622 if (FileStat(z, &fs)) {
623 if (!(fs.st_mode & S_IFDIR))
624 return ServerFileHandler(sessionP, z, fs.st_mtime,
631 if (!FileStat(z, &fs)) {
632 ResponseStatusErrno(sessionP);
635 return ServerDirectoryHandler(sessionP, z, fs.st_mtime,
638 return ServerFileHandler(sessionP, z, fs.st_mtime,
645 initUnixStuff(struct _TServer * const srvP) {
647 srvP->pidfile = srvP->uid = srvP->gid = -1;
654 logOpen(struct _TServer * const srvP) {
658 success = FileOpenCreate(&srvP->logfile, srvP->logfilename,
659 O_WRONLY | O_APPEND);
662 success = MutexCreate(&srvP->logmutex);
664 srvP->logfileisopen = TRUE;
666 TraceMsg("Can't create mutex for log file");
669 FileClose(&srvP->logfile);
671 TraceMsg("Can't open log file '%s'", srvP->logfilename);
679 logClose(struct _TServer * const srvP) {
681 if (srvP->logfileisopen) {
682 FileClose(&srvP->logfile);
683 MutexFree(&srvP->logmutex);
684 srvP->logfileisopen = FALSE;
691 initSocketStuff(struct _TServer * const srvP,
692 abyss_bool const noAccept,
693 TSocket * const userSocketP,
695 const char ** const errorP) {
699 srvP->serverAcceptsConnections = TRUE;
700 srvP->socketBound = TRUE;
701 srvP->listenSocketP = userSocketP;
702 } else if (noAccept) {
704 srvP->serverAcceptsConnections = FALSE;
705 srvP->socketBound = FALSE;
708 srvP->serverAcceptsConnections = TRUE;
709 srvP->socketBound = FALSE;
712 srvP->weCreatedListenSocket = FALSE;
718 createServer(struct _TServer ** const srvPP,
719 abyss_bool const noAccept,
720 TSocket * const userSocketP,
721 uint16_t const portNumber,
722 const char ** const errorP) {
724 struct _TServer * srvP;
729 xmlrpc_asprintf(errorP,
730 "Unable to allocate space for server descriptor");
732 srvP->terminationRequested = false;
734 initSocketStuff(srvP, noAccept, userSocketP, portNumber, errorP);
737 srvP->defaulthandler = ServerDefaultHandlerFunc;
739 srvP->name = strdup("unnamed");
740 srvP->filespath = strdup(DEFAULT_DOCS);
741 srvP->logfilename = NULL;
742 srvP->keepalivetimeout = 15;
743 srvP->keepalivemaxconn = 30;
745 srvP->advertise = TRUE;
746 srvP->mimeTypeP = NULL;
747 srvP->useSigchld = FALSE;
751 ListInitAutoFree(&srvP->handlers);
752 ListInitAutoFree(&srvP->defaultfilenames);
754 srvP->logfileisopen = FALSE;
767 setNamePathLog(TServer * const serverP,
768 const char * const name,
769 const char * const filesPath,
770 const char * const logFileName) {
771 /*----------------------------------------------------------------------------
772 This odd function exists to help with backward compatibility.
773 Today, we have the expandable model where you create a server with
774 default parameters, then use ServerSet... functions to choose
775 non-default parameters. But before, you specified these three
776 parameters right in the arguments of various create functions.
777 -----------------------------------------------------------------------------*/
779 ServerSetName(serverP, name);
781 ServerSetFilesPath(serverP, filesPath);
783 ServerSetLogFileName(serverP, logFileName);
789 ServerCreate(TServer * const serverP,
790 const char * const name,
791 uint16_t const portNumber,
792 const char * const filesPath,
793 const char * const logFileName) {
795 abyss_bool const noAcceptFalse = FALSE;
800 createServer(&serverP->srvP, noAcceptFalse, NULL, portNumber, &error);
804 xmlrpc_strfree(error);
809 setNamePathLog(serverP, name, filesPath, logFileName);
818 createSocketFromOsSocket(TOsSocket const osSocket,
819 TSocket ** const socketPP) {
822 SocketWinCreateWinsock(osSocket, socketPP);
824 SocketUnixCreateFd(osSocket, socketPP);
831 ServerCreateSocket(TServer * const serverP,
832 const char * const name,
833 TOsSocket const socketFd,
834 const char * const filesPath,
835 const char * const logFileName) {
840 createSocketFromOsSocket(socketFd, &socketP);
843 abyss_bool const noAcceptFalse = FALSE;
847 createServer(&serverP->srvP, noAcceptFalse, socketP, 0, &error);
852 xmlrpc_strfree(error);
856 setNamePathLog(serverP, name, filesPath, logFileName);
867 ServerCreateNoAccept(TServer * const serverP,
868 const char * const name,
869 const char * const filesPath,
870 const char * const logFileName) {
872 abyss_bool const noAcceptTrue = TRUE;
877 createServer(&serverP->srvP, noAcceptTrue, NULL, 0, &error);
882 xmlrpc_strfree(error);
886 setNamePathLog(serverP, name, filesPath, logFileName);
894 ServerCreateSocket2(TServer * const serverP,
895 TSocket * const socketP,
896 const char ** const errorP) {
898 abyss_bool const noAcceptFalse = FALSE;
902 createServer(&serverP->srvP, noAcceptFalse, socketP, 0, errorP);
908 terminateHandlers(TList * const handlersP) {
909 /*----------------------------------------------------------------------------
910 Terminate all handlers in the list '*handlersP'.
912 I.e. call each handler's terminate function.
913 -----------------------------------------------------------------------------*/
914 if (handlersP->item) {
916 for (i = handlersP->size; i > 0; --i) {
917 URIHandler2 * const handlerP = handlersP->item[i-1];
919 handlerP->term(handlerP->userdata);
927 ServerFree(TServer * const serverP) {
929 struct _TServer * const srvP = serverP->srvP;
931 if (srvP->weCreatedListenSocket)
932 SocketDestroy(srvP->listenSocketP);
934 xmlrpc_strfree(srvP->name);
936 xmlrpc_strfree(srvP->filespath);
938 ListFree(&srvP->defaultfilenames);
940 terminateHandlers(&srvP->handlers);
942 ListFree(&srvP->handlers);
946 if (srvP->logfilename)
947 xmlrpc_strfree(srvP->logfilename);
955 ServerSetName(TServer * const serverP,
956 const char * const name) {
958 xmlrpc_strfree(serverP->srvP->name);
960 serverP->srvP->name = strdup(name);
966 ServerSetFilesPath(TServer * const serverP,
967 const char * const filesPath) {
969 xmlrpc_strfree(serverP->srvP->filespath);
971 serverP->srvP->filespath = strdup(filesPath);
977 ServerSetLogFileName(TServer * const serverP,
978 const char * const logFileName) {
980 struct _TServer * const srvP = serverP->srvP;
982 if (srvP->logfilename)
983 xmlrpc_strfree(srvP->logfilename);
985 srvP->logfilename = strdup(logFileName);
991 ServerSetKeepaliveTimeout(TServer * const serverP,
992 uint32_t const keepaliveTimeout) {
994 serverP->srvP->keepalivetimeout = keepaliveTimeout;
1000 ServerSetKeepaliveMaxConn(TServer * const serverP,
1001 uint32_t const keepaliveMaxConn) {
1003 serverP->srvP->keepalivemaxconn = keepaliveMaxConn;
1009 ServerSetTimeout(TServer * const serverP,
1010 uint32_t const timeout) {
1012 serverP->srvP->timeout = timeout;
1018 ServerSetAdvertise(TServer * const serverP,
1019 abyss_bool const advertise) {
1021 serverP->srvP->advertise = advertise;
1027 ServerSetMimeType(TServer * const serverP,
1028 MIMEType * const MIMETypeP) {
1030 serverP->srvP->mimeTypeP = MIMETypeP;
1036 runUserHandler(TSession * const sessionP,
1037 struct _TServer * const srvP) {
1042 for (i = srvP->handlers.size-1, handled = FALSE;
1045 URIHandler2 * const handlerP = srvP->handlers.item[i];
1047 if (handlerP->handleReq2)
1048 handlerP->handleReq2(handlerP, sessionP, &handled);
1049 else if (handlerP->handleReq1)
1050 handled = handlerP->handleReq1(sessionP);
1054 ((URIHandler)(srvP->defaulthandler))(sessionP);
1060 processDataFromClient(TConn * const connectionP,
1061 abyss_bool const lastReqOnConn,
1062 abyss_bool * const keepAliveP) {
1066 RequestInit(&session, connectionP);
1068 session.serverDeniesKeepalive = lastReqOnConn;
1070 RequestRead(&session);
1071 if (session.status == 0) {
1072 if (session.version.major >= 2)
1073 ResponseStatus(&session, 505);
1074 else if (!RequestValidURI(&session))
1075 ResponseStatus(&session, 400);
1077 runUserHandler(&session, connectionP->server->srvP);
1079 assert(session.status != 0);
1081 if (session.responseStarted)
1082 HTTPWriteEndChunk(&session);
1084 ResponseError(&session);
1086 *keepAliveP = HTTPKeepalive(&session);
1088 SessionLog(&session);
1090 RequestFree(&session);
1094 static TThreadProc serverFunc;
1097 serverFunc(void * const userHandle) {
1098 /*----------------------------------------------------------------------------
1099 Do server stuff on one connection. At its simplest, this means do
1100 one HTTP request. But with keepalive, it can be many requests.
1101 -----------------------------------------------------------------------------*/
1102 TConn * const connectionP = userHandle;
1103 struct _TServer * const srvP = connectionP->server->srvP;
1105 unsigned int requestCount;
1106 /* Number of requests we've handled so far on this connection */
1107 abyss_bool connectionDone;
1108 /* No more need for this HTTP connection */
1111 connectionDone = FALSE;
1113 while (!connectionDone) {
1116 /* Wait to read until timeout */
1117 success = ConnRead(connectionP, srvP->keepalivetimeout);
1120 connectionDone = TRUE;
1122 abyss_bool const lastReqOnConn =
1123 requestCount + 1 >= srvP->keepalivemaxconn;
1125 abyss_bool keepalive;
1127 processDataFromClient(connectionP, lastReqOnConn, &keepalive);
1132 connectionDone = TRUE;
1134 /**************** Must adjust the read buffer *****************/
1135 ConnReadInit(connectionP);
1143 createAndBindSocket(struct _TServer * const srvP) {
1147 success = SocketInit();
1149 TraceMsg("Can't initialize TCP sockets");
1153 SocketUnixCreate(&socketP);
1156 TraceMsg("Can't create a socket");
1160 success = SocketBind(socketP, NULL, srvP->port);
1163 TraceMsg("Failed to bind listening socket to port number %u",
1166 srvP->weCreatedListenSocket = TRUE;
1167 srvP->socketBound = TRUE;
1168 srvP->listenSocketP = socketP;
1171 SocketDestroy(socketP);
1179 ServerInit(TServer * const serverP) {
1180 /*----------------------------------------------------------------------------
1181 Initialize a server to accept connections.
1183 Do not confuse this with creating the server -- ServerCreate().
1185 Not necessary or valid with a server that doesn't accept connections (i.e.
1186 user supplies the TCP connections).
1187 -----------------------------------------------------------------------------*/
1188 struct _TServer * const srvP = serverP->srvP;
1191 if (!srvP->serverAcceptsConnections) {
1192 TraceMsg("ServerInit() is not valid on a server that doesn't "
1193 "accept connections "
1194 "(i.e. created with ServerCreateNoAccept)");
1197 if (!srvP->socketBound)
1198 createAndBindSocket(srvP);
1200 if (srvP->socketBound) {
1201 success = SocketListen(srvP->listenSocketP, MAX_CONN);
1204 TraceMsg("Failed to listen on bound socket.");
1214 /* We don't do any locking on the outstanding connections list, so
1215 we must make sure that only the master thread (the one that listens
1216 for connections) ever accesses it.
1218 That's why when a thread completes, it places the connection in
1219 "finished" status, but doesn't destroy the connection.
1226 /* Redundant with 'firstP', for quick access */
1227 } outstandingConnList;
1232 createOutstandingConnList(outstandingConnList ** const listPP) {
1234 outstandingConnList * listP;
1236 MALLOCVAR_NOFAIL(listP);
1238 listP->firstP = NULL; /* empty list */
1247 destroyOutstandingConnList(outstandingConnList * const listP) {
1249 assert(listP->firstP == NULL);
1250 assert(listP->count == 0);
1258 addToOutstandingConnList(outstandingConnList * const listP,
1259 TConn * const connectionP) {
1261 connectionP->nextOutstandingP = listP->firstP;
1263 listP->firstP = connectionP;
1271 freeFinishedConns(outstandingConnList * const listP) {
1272 /*----------------------------------------------------------------------------
1273 Garbage-collect the resources associated with connections that are
1274 finished with their jobs. Thread resources, connection pool
1276 -----------------------------------------------------------------------------*/
1279 pp = &listP->firstP;
1282 TConn * const connectionP = (*pp);
1284 ThreadUpdateStatus(connectionP->threadP);
1286 if (connectionP->finished) {
1287 /* Take it out of the list */
1288 *pp = connectionP->nextOutstandingP;
1291 ConnWaitAndRelease(connectionP);
1293 /* Move to next connection in list */
1294 pp = &connectionP->nextOutstandingP;
1302 waitForConnectionFreed(outstandingConnList * const outstandingConnListP
1304 /*----------------------------------------------------------------------------
1305 Wait for a connection descriptor in 'connectionPool' to be probably
1307 -----------------------------------------------------------------------------*/
1309 /* TODO: We should do something more sophisticated here. For pthreads,
1310 we can have a thread signal us by semaphore when it terminates.
1311 For fork, we might be able to use the "done" handler argument
1312 to ConnCreate() to get interrupted when the death of a child
1316 xmlrpc_millisecond_sleep(2000);
1322 waitForNoConnections(outstandingConnList * const outstandingConnListP) {
1324 while (outstandingConnListP->firstP) {
1325 freeFinishedConns(outstandingConnListP);
1327 if (outstandingConnListP->firstP)
1328 waitForConnectionFreed(outstandingConnListP);
1335 waitForConnectionCapacity(outstandingConnList * const outstandingConnListP) {
1336 /*----------------------------------------------------------------------------
1337 Wait until there are fewer than the maximum allowed connections in
1339 -----------------------------------------------------------------------------*/
1340 while (outstandingConnListP->count >= MAX_CONN) {
1341 freeFinishedConns(outstandingConnListP);
1342 if (outstandingConnListP->firstP)
1343 waitForConnectionFreed(outstandingConnListP);
1351 ServerHandleSigchld(pid_t const pid) {
1353 ThreadHandleSigchld(pid);
1360 ServerUseSigchld(TServer * const serverP) {
1362 struct _TServer * const srvP = serverP->srvP;
1364 srvP->useSigchld = TRUE;
1369 TThreadDoneFn destroySocket;
1372 destroyConnSocket(void * const userHandle) {
1373 /*----------------------------------------------------------------------------
1374 This is a "connection done" function for the connection the server
1375 serves. It gets called some time after the connection has done its
1376 thing. Its job is to clean up stuff the server created for use by
1377 the connection, but the server can't clean up because the
1378 connection might be processed asynchronously in a background
1381 To wit, we destroy the connection's socket.
1382 -----------------------------------------------------------------------------*/
1383 TConn * const connectionP = userHandle;
1385 SocketDestroy(connectionP->socketP);
1391 serverRun2(TServer * const serverP) {
1393 struct _TServer * const srvP = serverP->srvP;
1394 outstandingConnList * outstandingConnListP;
1396 createOutstandingConnList(&outstandingConnListP);
1398 while (!srvP->terminationRequested) {
1399 TConn * connectionP;
1401 abyss_bool connected;
1403 TSocket * connectedSocketP;
1406 SocketAccept(srvP->listenSocketP,
1407 &connected, &failed,
1408 &connectedSocketP, &peerIpAddr);
1413 freeFinishedConns(outstandingConnListP);
1415 waitForConnectionCapacity(outstandingConnListP);
1417 ConnCreate(&connectionP, serverP, connectedSocketP,
1418 &serverFunc, &destroyConnSocket, ABYSS_BACKGROUND,
1422 addToOutstandingConnList(outstandingConnListP, connectionP);
1423 ConnProcess(connectionP);
1424 /* When connection is done (which could be later, courtesy
1425 of a background thread), destroyConnSocket() will
1426 destroy *connectedSocketP.
1429 xmlrpc_strfree(error);
1430 SocketDestroy(connectedSocketP);
1433 TraceMsg("Socket Error=%d", SocketError(srvP->listenSocketP));
1435 waitForNoConnections(outstandingConnListP);
1437 destroyOutstandingConnList(outstandingConnListP);
1443 ServerRun(TServer * const serverP) {
1445 struct _TServer * const srvP = serverP->srvP;
1447 if (!srvP->socketBound)
1448 TraceMsg("This server is not set up to accept connections "
1449 "on its own, so you can't use ServerRun(). "
1450 "Try ServerRunConn() or ServerInit()");
1452 serverRun2(serverP);
1458 serverRunConn(TServer * const serverP,
1459 TSocket * const connectedSocketP) {
1460 /*----------------------------------------------------------------------------
1461 Do the HTTP transaction on the TCP connection on the socket
1463 (socket must be in connected state, with nothing having been read or
1464 written on the connection yet).
1465 -----------------------------------------------------------------------------*/
1466 struct _TServer * const srvP = serverP->srvP;
1468 TConn * connectionP;
1471 srvP->keepalivemaxconn = 1;
1473 ConnCreate(&connectionP,
1474 serverP, connectedSocketP,
1475 &serverFunc, NULL, ABYSS_FOREGROUND, srvP->useSigchld,
1478 TraceMsg("Couldn't create HTTP connection out of "
1479 "connected socket. %s", error);
1480 xmlrpc_strfree(error);
1482 ConnProcess(connectionP);
1484 ConnWaitAndRelease(connectionP);
1491 ServerRunConn2(TServer * const serverP,
1492 TSocket * const connectedSocketP,
1493 const char ** const errorP) {
1494 /*----------------------------------------------------------------------------
1495 Do the HTTP transaction on the TCP connection on the socket
1496 'connectedOsSocket'.
1497 (socket must be connected state, with nothing having been read or
1498 written on the connection yet).
1499 -----------------------------------------------------------------------------*/
1500 struct _TServer * const srvP = serverP->srvP;
1502 if (srvP->serverAcceptsConnections)
1503 xmlrpc_asprintf(errorP,
1504 "This server is configured to accept connections on "
1506 "Try ServerRun() or ServerCreateNoAccept().");
1508 serverRunConn(serverP, connectedSocketP);
1516 ServerRunConn(TServer * const serverP,
1517 TOsSocket const connectedOsSocket) {
1520 createSocketFromOsSocket(connectedOsSocket, &socketP);
1522 TraceExit("Unable to use supplied socket");
1526 ServerRunConn2(serverP, socketP, &error);
1529 TraceExit("Failed to run server on connection on file "
1530 "descriptor %d. %s", connectedOsSocket, error);
1531 xmlrpc_strfree(error);
1533 SocketDestroy(socketP);
1540 ServerRunOnce(TServer * const serverP) {
1541 /*----------------------------------------------------------------------------
1542 Accept a connection from the listening socket and do the HTTP
1543 transaction that comes over it.
1545 If no connection is presently waiting on the listening socket, wait
1546 for one. But return immediately if we receive a signal during the
1548 -----------------------------------------------------------------------------*/
1549 struct _TServer * const srvP = serverP->srvP;
1551 if (!srvP->socketBound)
1552 TraceMsg("This server is not set up to accept connections "
1553 "on its own, so you can't use ServerRunOnce(). "
1554 "Try ServerRunConn() or ServerInit()");
1556 abyss_bool connected;
1558 TSocket * connectedSocketP;
1561 srvP->keepalivemaxconn = 1;
1563 SocketAccept(srvP->listenSocketP,
1564 &connected, &failed,
1565 &connectedSocketP, &remoteAddr);
1567 serverRunConn(serverP, connectedSocketP);
1568 SocketDestroy(connectedSocketP);
1570 TraceMsg("Socket Error=%d", SocketError(srvP->listenSocketP));
1577 ServerRunOnce2(TServer * const serverP,
1578 enum abyss_foreback const foregroundBackground ATTR_UNUSED) {
1579 /*----------------------------------------------------------------------------
1580 This is a backward compatibility interface to ServerRunOnce().
1582 'foregroundBackground' is meaningless. We always process the
1583 connection in the foreground. The parameter exists because we once
1584 thought we could do them in the background, but we really can't do
1585 that in any clean way. If Caller wants background execution, he can
1586 spin his own thread or process to call us. It makes much more sense
1587 in Caller's context.
1588 -----------------------------------------------------------------------------*/
1589 ServerRunOnce(serverP);
1597 #ifdef HAVE_SETGROUPS
1598 if (setgroups(0, NULL) == (-1))
1599 TraceExit("Failed to setup the group.");
1606 ServerDaemonize(TServer * const serverP) {
1607 /*----------------------------------------------------------------------------
1608 Turn Caller into a daemon (i.e. fork a child, then exit; the child
1611 NOTE: It's ridiculous, but conventional, for us to do this. It's
1612 ridiculous because the task of daemonizing is not something
1613 particular to Abyss. It ought to be done by a higher level. In
1614 fact, it should be done before the Abyss server program is even
1615 execed. The user should run a "daemonize" program that creates a
1616 daemon which execs the Abyss server program.
1617 -----------------------------------------------------------------------------*/
1618 struct _TServer * const srvP = serverP->srvP;
1621 /* Become a daemon */
1626 TraceExit("Unable to become a daemon");
1628 /* We are the parent */
1634 /* Change the current user if we are root */
1636 if (srvP->uid == (uid_t)-1)
1637 TraceExit("Can't run under root privileges. "
1638 "Please add a User option in your "
1639 "Abyss configuration file.");
1643 if (srvP->gid != (gid_t)-1)
1644 if (setgid(srvP->gid)==(-1))
1645 TraceExit("Failed to change the group.");
1647 if (setuid(srvP->uid) == -1)
1648 TraceExit("Failed to change the user.");
1651 if (srvP->pidfile != -1) {
1654 sprintf(z, "%d", getpid());
1655 FileWrite(&srvP->pidfile, z, strlen(z));
1656 FileClose(&srvP->pidfile);
1664 ServerAddHandler2(TServer * const serverP,
1665 URIHandler2 * const handlerArgP,
1666 abyss_bool * const successP) {
1668 URIHandler2 * handlerP;
1670 MALLOCVAR(handlerP);
1671 if (handlerP == NULL)
1674 *handlerP = *handlerArgP;
1676 if (handlerP->init == NULL)
1679 handlerP->init(handlerP, successP);
1682 *successP = ListAdd(&serverP->srvP->handlers, handlerP);
1691 static URIHandler2 *
1692 createHandler(URIHandler const function) {
1694 URIHandler2 * handlerP;
1696 MALLOCVAR(handlerP);
1697 if (handlerP != NULL) {
1698 handlerP->init = NULL;
1699 handlerP->term = NULL;
1700 handlerP->userdata = NULL;
1701 handlerP->handleReq2 = NULL;
1702 handlerP->handleReq1 = function;
1710 ServerAddHandler(TServer * const serverP,
1711 URIHandler const function) {
1713 URIHandler2 * handlerP;
1716 handlerP = createHandler(function);
1718 if (handlerP == NULL)
1721 success = ListAdd(&serverP->srvP->handlers, handlerP);
1732 ServerDefaultHandler(TServer * const serverP,
1733 URIHandler const handler) {
1735 serverP->srvP->defaulthandler =
1736 handler ? handler : ServerDefaultHandlerFunc;
1742 LogWrite(TServer * const serverP,
1743 const char * const msg) {
1745 struct _TServer * const srvP = serverP->srvP;
1747 if (!srvP->logfileisopen && srvP->logfilename)
1750 if (srvP->logfileisopen) {
1752 success = MutexLock(&srvP->logmutex);
1754 const char * const lbr = "\n";
1755 FileWrite(&srvP->logfile, msg, strlen(msg));
1756 FileWrite(&srvP->logfile, lbr, strlen(lbr));
1758 MutexUnlock(&srvP->logmutex);
1762 /*******************************************************************************
1766 ** This file is part of the ABYSS Web server project.
1768 ** Copyright (C) 2000 by Moez Mahfoudh <mmoez@bigfoot.com>.
1769 ** All rights reserved.
1771 ** Redistribution and use in source and binary forms, with or without
1772 ** modification, are permitted provided that the following conditions
1774 ** 1. Redistributions of source code must retain the above copyright
1775 ** notice, this list of conditions and the following disclaimer.
1776 ** 2. Redistributions in binary form must reproduce the above copyright
1777 ** notice, this list of conditions and the following disclaimer in the
1778 ** documentation and/or other materials provided with the distribution.
1779 ** 3. The name of the author may not be used to endorse or promote products
1780 ** derived from this software without specific prior written permission.
1782 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1783 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1784 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1785 ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1786 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1787 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1788 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1789 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1790 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
1791 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1794 ******************************************************************************/