📄 httpd.c
字号:
/*
* Novell NetWare HTTP TCP server -- main, etc.
*
* John Calcote 5/10/95
* internet: jcalcote@novell.com
*
* httpd.c - TCP/IP & SPX server for WWW file requests.
*
* Notes:
*
* This web server daemon was written for the NetWare NON-PREEMPTIVE
* multitasking environment. Thus, there is no critical section
* handling (we always know when we are going to switch threads).
*
* The places to handle critical sections would be in the code for
* linked-list manipulation (addition, removal, creation, destruction).
* Most of the rest of the code is not re-entered by different threads.
*
* This code will port fairly easily to any other non-preemptive
* environment which supports TLI. It should also be a fairly trivial
* process to port it to any Unix system.
*
* Finally, since it contains nothing specific to HTTP, it can easily
* be used for any server listen daemon.
*/
#include "hcommon.h"
#define LOCAL static
#include <signal.h>
#include <stropts.h>
#include <io.h>
#include <tiuser.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
/* Transport server connection indication data block */
typedef struct cidata {
struct cidata *pNext;
struct cidata *pPrev;
struct t_call *pCall; /* indication call structure */
int cfd; /* indication client fd */
} CIDATA;
/* Static prototypes */
LOCAL int ParseCommandLine(int, char **);
LOCAL void TCPMonitor(void);
LOCAL void SPXMonitor(void);
LOCAL int TLIListen(int, char *);
LOCAL int CreateCIFreeList(CIDATA **, int, int);
LOCAL CIDATA *GetLastInCIList(CIDATA **);
LOCAL CIDATA *GetSequenceInCIList(CIDATA **, int);
LOCAL void GetGivenNodeInCIList(CIDATA **, CIDATA *);
LOCAL void PutFirstInCIList(CIDATA **, CIDATA *);
LOCAL void DestroyCIList(CIDATA **);
LOCAL void RejectAllCallsInCIList(CIDATA *, int);
LOCAL int AcceptIndications(int, CIDATA **, CIDATA **, CIDATA **, char *);
LOCAL int HandleAsyncEvent(int, CIDATA **, CIDATA **);
LOCAL int OpenSpecificTLIfd(char *, int, void *, int, void *);
LOCAL int OpenArbitraryTLIfd(char *);
LOCAL REQUEST *AllocThreadData(CIDATA *, char *);
LOCAL char *TCPAddr2Str(void *);
LOCAL char *SPXAddr2Str(void *);
LOCAL void FreeRequest(REQUEST *);
LOCAL int QueryCreateThread(void);
LOCAL int QueryKillThread(void);
LOCAL int InitResponsePool(void);
LOCAL void KillResponsePool(void);
LOCAL void ProcessRequest(void);
LOCAL void KillServers(void);
LOCAL void KillResponders(void);
LOCAL void SignalHandler(int);
/* module globals */
int debugLevel; /* debugging on or off (1 or 0) */
int debugScreen; /* debug screen handle */
/* Module static variables */
LOCAL char syserr[256]; /* buffer for system error message strings */
LOCAL int listening; /* listening flag, touched by all monitors */
LOCAL int executing; /* executing flag, touched by SEGTERM hndlr */
LOCAL int responding; /* number of threads responding */
LOCAL int maxRspThreads; /* maximum number of rsp threads allowed */
LOCAL int minRspThreads; /* minimum number of rsp threads allowed */
LOCAL int curRspThreads; /* number of response threads in pool */
LOCAL LONG requestWaiting; /* request queue semaphore handle */
LOCAL REQUEST *pRqstQueue; /* general request queue */
LOCAL int reqCount; /* general request queue count */
/* -------- Transport specific static globals --------- */
/* TCP Transport */
LOCAL int tcp_server; /* tcp server socket */
unsigned short usTCPPort = 80;
/* SPX Transport */
LOCAL int spx_server; /* spx server socket */
unsigned int usSPXPort = 0x3280;
/*************************************************************************
* main - System entry point.
*
* Parameters:
* <standard>
*
* Returns:
* Nothing.
*/
void main (int argc, char *argv[]) {
printf("HTTPD - Web Document Server. (Version 0.90)\n");
printf("Compiled on " __DATE__ " at " __TIME__".\n\n");
executing = 1; /* are we executing? */
listening = 0; /* how many daemons are listening? */
responding = 0; /* how many requests are we servicing? */
curRspThreads = 0; /* number of responder threads */
uniqueThreadID = 0; /* unique thread ID number (for name) */
maxRspThreads = MAX_RSP_THREADS;
minRspThreads = MIN_RSP_THREADS;
tcp_server = -1;
spx_server = -1;
if (InitResponsePool() != 0) {
printf("HTTPD: Failed to build response thread pool. Terminating.\n");
return;
}
OpenLogs(); /* open error and access logs */
signal(SIGTERM, SignalHandler);
BeginThread((void(*)(void*))TCPMonitor, NULL, 0, NULL);
BeginThread((void(*)(void*))SPXMonitor, NULL, 0, NULL);
return;
}
/*************************************************************************
* ParseCommandLine - parse command line parameters into globals.
* Fills in globals, ndsUserName, ndsPassword, ndsTreeName, and
* debugLevel.
*
* Command Line Syntax:
* LOAD HTTPD [/L=<NDS Login> [/P=<NDS Password>] [/T=<NDS Tree>]] [/D(ebug)]
*
* Parameters:
* argc - [IN ] argument count (not used in this implementation)
* argv - [IN ] argument array
*
* Returns:
* 0 for success, or non-zero error code indicating bad parameter, etc.
*/
#pragma argsused
int ParseCommandLine(int argc, char *argv[]) {
char *argp, opt;
debugLevel = 0;
while (*++argv) {
argp = *argv;
if (*argp == '/' || *argp == '-')
argp++;
opt = (char)toupper(*argp++);
if (*argp == '=')
argp++;
switch(opt) {
case 'D':
debugLevel = *argp? atoi(argp) : 1;
break;
default:
return -1;
}
}
return 0;
}
/*************************************************************************
* TCPMonitor - entry point for TCP connection indication monitor.
*
* Parameters:
* None.
*
* Returns:
* Nothing.
*/
void TCPMonitor(void) {
char *transport = "/dev/tcp"; /* TCP streams device name */
struct sockaddr_in saddr, saout; /* TCP address buffers */
RenameThread(GetThreadID(), "HTTP TCP Monitor");
memset(&saddr, 0, sizeof(saddr));
saddr.sin_port = htons(usTCPPort); /* specify only the port # */
if ((tcp_server = OpenSpecificTLIfd(transport, TLI_QLEN,
&saddr, sizeof(saddr), &saout)) < 0) {
printf("HTTPD: Unable to open TLI(%s) monitor end point."
"\n Terminating this monitor.\n", transport);
return;
}
if (saddr.sin_port != saout.sin_port)
printf("HTTPD: WARNING! Requested TCP port (%d) not available."
"\n TCP listen end point bound to port %d.\n",
usTCPPort, ntohs(saout.sin_port));
else
printf("HTTPD: TCP monitor listening on port %d.\n", usTCPPort);
listening++;
if (TLIListen(tcp_server, transport) < 0) {
t_close(tcp_server);
tcp_server = -1;
}
listening--;
return;
}
/*************************************************************************
* SPXMonitor - entry point for SPX connection indication monitor.
*
* Parameters:
* None.
*
* Returns:
* Nothing.
*/
void SPXMonitor(void) {
char *transport = "/dev/nspx"; /* SPX streams device name */
IPX_ADDR saddr, saout; /* SPX address buffers */
RenameThread(GetThreadID(), "HTTP SPX Monitor");
memset(&saddr, 0, sizeof(saddr));
*(unsigned short *)saddr.ipxa_socket = htons(usSPXPort);
if ((spx_server = OpenSpecificTLIfd(transport, TLI_QLEN,
&saddr, sizeof(saddr), &saout)) == -1) {
printf("HTTPD: Unable to open TLI(%s) monitor end point."
"\n Terminating this monitor.\n", transport);
return;
}
if (*(unsigned short *)saddr.ipxa_socket != *(unsigned short *)saout.ipxa_socket)
printf("HTTPD: WARNING! Requested SPX socket (0x%x) not available."
"\n SPX listen end point bound to socket 0x%x.\n",
usSPXPort, ntohs(*(unsigned short *)saout.ipxa_socket));
else
printf("HTTPD: SPX monitor listening on socket 0x%x.\n", usSPXPort);
listening++;
if (TLIListen(spx_server, transport) < 0) {
t_close(spx_server);
spx_server = -1;
}
listening--;
return;
}
/*************************************************************************
* TLIListen - listen on an an open TLI endpoint (sfd) using 'transport'.
*
* Parameters:
* sfd - [IN ] server file descriptor
* transport - [IN ] transport device name
*
* Returns:
* the value of 'executing' == non-zero for abnormal termination,
* or 0 for normal (SIGTERM) termination.
*/
int TLIListen(int sfd, char *transport) {
REQUEST *pRequest; /* connected request */
CIDATA *pCIFree = NULL; /* head of free block list */
CIDATA *pCIHead = NULL; /* head of pending cxtn list */
CIDATA *pCIData; /* current indication */
int rc;
/* allocate free block list */
if (CreateCIFreeList(&pCIFree, sfd, TLI_QLEN) < 0) {
printf("HTTPD: Unable to allocate free cxtn block list."
"\n Terminating device (%s) monitor.\n", transport);
return -1;
}
/* start listening for connect requests */
while (executing) {
ThreadSwitchWithDelay();
pCIData = GetLastInCIList(&pCIFree);
if (t_listen(sfd, pCIData->pCall) < 0) {
if (!executing) {
PutFirstInCIList(&pCIFree, pCIData);
goto ListenExit;
}
/* This should never happen, but if it does for SOME reason ... */
sprintf(syserr, "SYSERR %d - %s", errno, sys_errlist[errno]);
LogError("SVR: t_listen failed in %s (%d) - reason: (%d) %s. %s",
__FILE__, __LINE__, t_errno, t_errlist[t_errno],
t_errno == TSYSERR? syserr : "");
PutFirstInCIList(&pCIFree, pCIData);
goto ListenExit;
}
PutFirstInCIList(&pCIHead, pCIData);
while (pCIHead) {
ThreadSwitchWithDelay();
/* AcceptIndications may fail for many reasons, but only
* unrecoverable (memory) errors should return an error code
* so that the system may keep running.
*/
rc = AcceptIndications(sfd, &pCIHead, &pCIFree, &pCIData, transport);
if (rc < 0)
goto ListenExit; /* fatal, if AI returns -1 */
if (rc > 0)
continue;
if ((pRequest = AllocThreadData(pCIData, transport)) == NULL) { /* memory error */
t_snddis(pCIData->cfd, pCIData->pCall);
t_unbind(pCIData->cfd);
t_close(pCIData->cfd);
PutFirstInCIList(&pCIFree, pCIData);
goto ListenExit;
}
PutFirstInCIList((CIDATA **)&pRqstQueue, (CIDATA *)pRequest);
reqCount++;
SignalLocalSemaphore(requestWaiting);
PutFirstInCIList(&pCIFree, pCIData);
}
}
ListenExit:
DestroyCIList(&pCIFree);
RejectAllCallsInCIList(pCIHead, sfd);
DestroyCIList(&pCIHead);
return executing;
}
/*************************************************************************
* CreateCIFreeList - INTERNAL create a list of 'count' CIDATA nodes
* whose head pointer is stored in 'ppCIHead'. Use 'sfd' to
* initialize the t_call structure via t_alloc.
*
* NOTE: This routine assumes that *ppCIHead has been initialized to
* NULL before it was passed in.
*
* Parameters:
* ppCIHead - [I/O] address of list head pointer
* sfd - [IN ] server file descriptor (used by t_alloc)
* count - [IN ] number of nodes to create
*
* Returns:
* -1 for memory allocation error, 0 for successful allocation.
*/
int CreateCIFreeList(CIDATA **ppCIHead, int sfd, int count) {
CIDATA *pCIData;
int i;
for (i = 0; i < count; i++) {
if ((pCIData = (CIDATA *)malloc(sizeof(CIDATA))) == NULL) {
DestroyCIList(ppCIHead);
return -1;
}
if ((pCIData->pCall = (struct t_call *)t_alloc(sfd, T_CALL, T_ALL)) == NULL) {
free(pCIData);
DestroyCIList(ppCIHead);
return -1;
}
PutFirstInCIList(ppCIHead, pCIData);
}
return 0;
}
/*************************************************************************
* GetLastInCIList - INTERNAL remove a node from the end of the
* specified CI list. Return the node's address or NULL if there
* aren't any nodes in the list.
*
* NOTE: this list is a ring on the previous chain and a list on the
* next chain.
*
* NOTE: this function always removes from the tail of the list by
* backing up one from head.
*
* Parameters:
* ppCIHead - [I/O] address of list head pointer
*
* Returns:
* address of removed node, or NULL if there are none to remove.
*/
CIDATA *GetLastInCIList(CIDATA **ppCIHead) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -