9f8b98917d0bf3b6d907db7a1778567567a17539
[platform/upstream/nss.git] / nss / cmd / httpserv / httpserv.c
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 #include <stdio.h>
6 #include <string.h>
7
8 #include "secutil.h"
9
10 #if defined(XP_UNIX)
11 #include <unistd.h>
12 #endif
13
14 #if defined(_WINDOWS)
15 #include <process.h>    /* for getpid() */
16 #endif
17
18 #include <signal.h>
19 #include <stdlib.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <stdarg.h>
23
24 #include "nspr.h"
25 #include "prio.h"
26 #include "prerror.h"
27 #include "prnetdb.h"
28 #include "prclist.h"
29 #include "plgetopt.h"
30
31 #ifndef PORT_Sprintf
32 #define PORT_Sprintf sprintf
33 #endif
34
35 #ifndef PORT_Strstr
36 #define PORT_Strstr strstr
37 #endif
38
39 #ifndef PORT_Malloc
40 #define PORT_Malloc PR_Malloc
41 #endif
42
43 static int handle_connection( PRFileDesc *, PRFileDesc *, int );
44
45 static const char inheritableSockName[] = { "SELFSERV_LISTEN_SOCKET" };
46
47 #define DEFAULT_BULK_TEST 16384
48 #define MAX_BULK_TEST     1048576 /* 1 MB */
49 static PRBool testBulk;
50
51 /* data and structures for shutdown */
52 static int      stopping;
53
54 static PRBool  noDelay;
55 static int      verbose;
56
57 static PRThread * acceptorThread;
58
59 static PRLogModuleInfo *lm;
60
61 #define PRINTF  if (verbose)  printf
62 #define FPRINTF if (verbose) fprintf
63 #define FLUSH   if (verbose) { fflush(stdout); fflush(stderr); }
64 #define VLOG(arg) PR_LOG(lm,PR_LOG_DEBUG,arg)
65
66 static void
67 Usage(const char *progName)
68 {
69     fprintf(stderr, 
70
71 "Usage: %s -p port [-Dbv]\n"
72 "         [-t threads] [-i pid_file]\n"
73 "-D means disable Nagle delays in TCP\n"
74 "-b means try binding to the port and exit\n"
75 "-v means verbose output\n"
76 "-t threads -- specify the number of threads to use for connections.\n"
77 "-i pid_file file to write the process id of selfserve\n"
78         ,progName);
79 }
80
81 static const char *
82 errWarn(char * funcString)
83 {
84     PRErrorCode  perr      = PR_GetError();
85     const char * errString = SECU_Strerror(perr);
86
87     fprintf(stderr, "selfserv: %s returned error %d:\n%s\n",
88             funcString, perr, errString);
89     return errString;
90 }
91
92 static void
93 errExit(char * funcString)
94 {
95     errWarn(funcString);
96     exit(3);
97 }
98
99
100 #define MAX_VIRT_SERVER_NAME_ARRAY_INDEX  10
101
102 /**************************************************************************
103 ** Begin thread management routines and data.
104 **************************************************************************/
105 #define MIN_THREADS 3
106 #define DEFAULT_THREADS 8
107 #define MAX_THREADS 4096
108 #define MAX_PROCS 25
109 static int  maxThreads = DEFAULT_THREADS;
110
111
112 typedef struct jobStr {
113     PRCList     link;
114     PRFileDesc *tcp_sock;
115     PRFileDesc *model_sock;
116     int         requestCert;
117 } JOB;
118
119 static PZLock    * qLock; /* this lock protects all data immediately below */
120 static PRLock    * lastLoadedCrlLock; /* this lock protects lastLoadedCrl variable */
121 static PZCondVar * jobQNotEmptyCv;
122 static PZCondVar * freeListNotEmptyCv;
123 static PZCondVar * threadCountChangeCv;
124 static int  threadCount;
125 static PRCList  jobQ;
126 static PRCList  freeJobs;
127 static JOB *jobTable;
128
129 SECStatus
130 setupJobs(int maxJobs)
131 {
132     int i;
133
134     jobTable = (JOB *)PR_Calloc(maxJobs, sizeof(JOB));
135     if (!jobTable)
136         return SECFailure;
137
138     PR_INIT_CLIST(&jobQ);
139     PR_INIT_CLIST(&freeJobs);
140
141     for (i = 0; i < maxJobs; ++i) {
142         JOB * pJob = jobTable + i;
143         PR_APPEND_LINK(&pJob->link, &freeJobs);
144     }
145     return SECSuccess;
146 }
147
148 typedef int startFn(PRFileDesc *a, PRFileDesc *b, int c);
149
150 typedef enum { rs_idle = 0, rs_running = 1, rs_zombie = 2 } runState;
151
152 typedef struct perThreadStr {
153     PRFileDesc *a;
154     PRFileDesc *b;
155     int         c;
156     int         rv;
157     startFn  *  startFunc;
158     PRThread *  prThread;
159     runState    state;
160 } perThread;
161
162 static perThread *threads;
163
164 void
165 thread_wrapper(void * arg)
166 {
167     perThread * slot = (perThread *)arg;
168
169     slot->rv = (* slot->startFunc)(slot->a, slot->b, slot->c);
170
171     /* notify the thread exit handler. */
172     PZ_Lock(qLock);
173     slot->state = rs_zombie;
174     --threadCount;
175     PZ_NotifyAllCondVar(threadCountChangeCv);
176     PZ_Unlock(qLock);
177 }
178
179 int 
180 jobLoop(PRFileDesc *a, PRFileDesc *b, int c)
181 {
182     PRCList * myLink = 0;
183     JOB     * myJob;
184
185     PZ_Lock(qLock);
186     do {
187         myLink = 0;
188         while (PR_CLIST_IS_EMPTY(&jobQ) && !stopping) {
189             PZ_WaitCondVar(jobQNotEmptyCv, PR_INTERVAL_NO_TIMEOUT);
190         }
191         if (!PR_CLIST_IS_EMPTY(&jobQ)) {
192             myLink = PR_LIST_HEAD(&jobQ);
193             PR_REMOVE_AND_INIT_LINK(myLink);
194         }
195         PZ_Unlock(qLock);
196         myJob = (JOB *)myLink;
197         /* myJob will be null when stopping is true and jobQ is empty */
198         if (!myJob) 
199             break;
200         handle_connection( myJob->tcp_sock, myJob->model_sock, 
201                            myJob->requestCert);
202         PZ_Lock(qLock);
203         PR_APPEND_LINK(myLink, &freeJobs);
204         PZ_NotifyCondVar(freeListNotEmptyCv);
205     } while (PR_TRUE);
206     return 0;
207 }
208
209
210 SECStatus
211 launch_threads(
212     startFn    *startFunc,
213     PRFileDesc *a,
214     PRFileDesc *b,
215     int         c,
216     PRBool      local)
217 {
218     int i;
219     SECStatus rv = SECSuccess;
220
221     /* create the thread management serialization structs */
222     qLock               = PZ_NewLock(nssILockSelfServ);
223     jobQNotEmptyCv      = PZ_NewCondVar(qLock);
224     freeListNotEmptyCv  = PZ_NewCondVar(qLock);
225     threadCountChangeCv = PZ_NewCondVar(qLock);
226
227     /* create monitor for crl reload procedure */
228     lastLoadedCrlLock   = PR_NewLock();
229
230     /* allocate the array of thread slots */
231     threads = PR_Calloc(maxThreads, sizeof(perThread));
232     if ( NULL == threads )  {
233         fprintf(stderr, "Oh Drat! Can't allocate the perThread array\n");
234         return SECFailure;
235     }
236     /* 5 is a little extra, intended to keep the jobQ from underflowing. 
237     ** That is, from going empty while not stopping and clients are still
238     ** trying to contact us.
239     */
240     rv = setupJobs(maxThreads + 5);
241     if (rv != SECSuccess)
242         return rv;
243
244     PZ_Lock(qLock);
245     for (i = 0; i < maxThreads; ++i) {
246         perThread * slot = threads + i;
247
248         slot->state = rs_running;
249         slot->a = a;
250         slot->b = b;
251         slot->c = c;
252         slot->startFunc = startFunc;
253         slot->prThread = PR_CreateThread(PR_USER_THREAD, 
254                         thread_wrapper, slot, PR_PRIORITY_NORMAL, 
255                         (PR_TRUE==local)?PR_LOCAL_THREAD:PR_GLOBAL_THREAD,
256                         PR_UNJOINABLE_THREAD, 0);
257         if (slot->prThread == NULL) {
258             printf("selfserv: Failed to launch thread!\n");
259             slot->state = rs_idle;
260             rv = SECFailure;
261             break;
262         } 
263
264         ++threadCount;
265     }
266     PZ_Unlock(qLock); 
267
268     return rv;
269 }
270
271 #define DESTROY_CONDVAR(name) if (name) { \
272         PZ_DestroyCondVar(name); name = NULL; }
273 #define DESTROY_LOCK(name) if (name) { \
274         PZ_DestroyLock(name); name = NULL; }
275         
276
277 void
278 terminateWorkerThreads(void)
279 {
280     VLOG(("selfserv: server_thead: waiting on stopping"));
281     PZ_Lock(qLock);
282     PZ_NotifyAllCondVar(jobQNotEmptyCv);
283     while (threadCount > 0) {
284         PZ_WaitCondVar(threadCountChangeCv, PR_INTERVAL_NO_TIMEOUT);
285     }
286     /* The worker threads empty the jobQ before they terminate. */
287     PORT_Assert(PR_CLIST_IS_EMPTY(&jobQ));
288     PZ_Unlock(qLock); 
289
290     DESTROY_CONDVAR(jobQNotEmptyCv);
291     DESTROY_CONDVAR(freeListNotEmptyCv);
292     DESTROY_CONDVAR(threadCountChangeCv);
293
294     PR_DestroyLock(lastLoadedCrlLock);
295     DESTROY_LOCK(qLock);
296     PR_Free(jobTable);
297     PR_Free(threads);
298 }
299
300 /**************************************************************************
301 ** End   thread management routines.
302 **************************************************************************/
303
304 PRBool NoReuse         = PR_FALSE;
305 PRBool disableLocking  = PR_FALSE;
306 PRBool failedToNegotiateName  = PR_FALSE;
307
308
309 static const char stopCmd[] = { "GET /stop " };
310 static const char getCmd[]  = { "GET " };
311 static const char EOFmsg[]  = { "EOF\r\n\r\n\r\n" };
312 static const char outHeader[] = {
313     "HTTP/1.0 200 OK\r\n"
314     "Server: Generic Web Server\r\n"
315     "Date: Tue, 26 Aug 1997 22:10:05 GMT\r\n"
316     "Content-type: text/plain\r\n"
317     "\r\n"
318 };
319
320 void stop_server()
321 {
322     stopping = 1;
323     PR_Interrupt(acceptorThread);
324     PZ_TraceFlush();
325 }
326
327 int
328 handle_connection( 
329     PRFileDesc *tcp_sock,
330     PRFileDesc *model_sock,
331     int         requestCert
332     )
333 {
334     PRFileDesc *       ssl_sock = NULL;
335     PRFileDesc *       local_file_fd = NULL;
336     char  *            post;
337     char  *            pBuf;                    /* unused space at end of buf */
338     const char *       errString;
339     PRStatus           status;
340     int                bufRem;                  /* unused bytes at end of buf */
341     int                bufDat;                  /* characters received in buf */
342     int                newln    = 0;            /* # of consecutive newlns */
343     int                firstTime = 1;
344     int                reqLen;
345     int                rv;
346     int                numIOVs;
347     PRSocketOptionData opt;
348     PRIOVec            iovs[16];
349     char               msgBuf[160];
350     char               buf[10240];
351     char               fileName[513];
352
353     pBuf   = buf;
354     bufRem = sizeof buf;
355
356     VLOG(("selfserv: handle_connection: starting"));
357     opt.option             = PR_SockOpt_Nonblocking;
358     opt.value.non_blocking = PR_FALSE;
359     PR_SetSocketOption(tcp_sock, &opt);
360
361     VLOG(("selfserv: handle_connection: starting\n"));
362         ssl_sock = tcp_sock;
363
364     if (noDelay) {
365         opt.option         = PR_SockOpt_NoDelay;
366         opt.value.no_delay = PR_TRUE;
367         status = PR_SetSocketOption(ssl_sock, &opt);
368         if (status != PR_SUCCESS) {
369             errWarn("PR_SetSocketOption(PR_SockOpt_NoDelay, PR_TRUE)");
370             if (ssl_sock) {
371                 PR_Close(ssl_sock);
372             }
373             return SECFailure;
374         }
375     }
376
377     while (1) {
378         newln = 0;
379         reqLen     = 0;
380         rv = PR_Read(ssl_sock, pBuf, bufRem - 1);
381         if (rv == 0 || 
382             (rv < 0 && PR_END_OF_FILE_ERROR == PR_GetError())) {
383             if (verbose)
384                 errWarn("HDX PR_Read hit EOF");
385             break;
386         }
387         if (rv < 0) {
388             errWarn("HDX PR_Read");
389             goto cleanup;
390         }
391         /* NULL termination */
392         pBuf[rv] = 0;
393         if (firstTime) {
394             firstTime = 0;
395         }
396
397         pBuf   += rv;
398         bufRem -= rv;
399         bufDat = pBuf - buf;
400         /* Parse the input, starting at the beginning of the buffer.
401          * Stop when we detect two consecutive \n's (or \r\n's) 
402          * as this signifies the end of the GET or POST portion.
403          * The posted data follows.
404          */
405         while (reqLen < bufDat && newln < 2) {
406             int octet = buf[reqLen++];
407             if (octet == '\n') {
408                 newln++;
409             } else if (octet != '\r') {
410                 newln = 0;
411             }
412         }
413
414         /* came to the end of the buffer, or second newln
415          * If we didn't get an empty line (CRLFCRLF) then keep on reading.
416          */
417         if (newln < 2) 
418             continue;
419
420         /* we're at the end of the HTTP request.
421          * If the request is a POST, then there will be one more
422          * line of data.
423          * This parsing is a hack, but ok for SSL test purposes.
424          */
425         post = PORT_Strstr(buf, "POST ");
426         if (!post || *post != 'P') 
427             break;
428
429         /* It's a post, so look for the next and final CR/LF. */
430         /* We should parse content length here, but ... */
431         while (reqLen < bufDat && newln < 3) {
432             int octet = buf[reqLen++];
433             if (octet == '\n') {
434                 newln++;
435             }
436         }
437         if (newln == 3)
438             break;
439     } /* read loop */
440
441     bufDat = pBuf - buf;
442     if (bufDat) do {    /* just close if no data */
443         /* Have either (a) a complete get, (b) a complete post, (c) EOF */
444         if (reqLen > 0 && !strncmp(buf, getCmd, sizeof getCmd - 1)) {
445             char *      fnBegin = buf + 4;
446             char *      fnEnd;
447             PRFileInfo  info;
448             /* try to open the file named.  
449              * If successful, then write it to the client.
450              */
451             fnEnd = strpbrk(fnBegin, " \r\n");
452             if (fnEnd) {
453                 int fnLen = fnEnd - fnBegin;
454                 if (fnLen < sizeof fileName) {
455                     char *fnstart;
456                     strncpy(fileName, fnBegin, fnLen);
457                     fileName[fnLen] = 0;        /* null terminate */
458                     fnstart = fileName;
459                     /* strip initial / because our root is the current directory*/
460                     while (*fnstart && *fnstart=='/')
461                         ++fnstart;
462                     status = PR_GetFileInfo(fnstart, &info);
463                     if (status == PR_SUCCESS &&
464                         info.type == PR_FILE_FILE &&
465                         info.size >= 0 ) {
466                         local_file_fd = PR_Open(fnstart, PR_RDONLY, 0);
467                     }
468                 }
469             }
470         }
471
472         numIOVs = 0;
473
474         iovs[numIOVs].iov_base = (char *)outHeader;
475         iovs[numIOVs].iov_len  = (sizeof(outHeader)) - 1;
476         numIOVs++;
477
478         if (local_file_fd) {
479             PRInt32     bytes;
480             int         errLen;
481             bytes = PR_TransmitFile(ssl_sock, local_file_fd, outHeader,
482                                     sizeof outHeader - 1,
483                                     PR_TRANSMITFILE_KEEP_OPEN,
484                                     PR_INTERVAL_NO_TIMEOUT);
485             if (bytes >= 0) {
486                 bytes -= sizeof outHeader - 1;
487                 FPRINTF(stderr, 
488                         "selfserv: PR_TransmitFile wrote %d bytes from %s\n",
489                         bytes, fileName);
490                 break;
491             }
492             errString = errWarn("PR_TransmitFile");
493             errLen = PORT_Strlen(errString);
494             errLen = PR_MIN(errLen, sizeof msgBuf - 1);
495             PORT_Memcpy(msgBuf, errString, errLen);
496             msgBuf[errLen] = 0;
497             
498             iovs[numIOVs].iov_base = msgBuf;
499             iovs[numIOVs].iov_len  = PORT_Strlen(msgBuf);
500             numIOVs++;
501         } else if (reqLen <= 0) {       /* hit eof */
502             PORT_Sprintf(msgBuf, "Get or Post incomplete after %d bytes.\r\n",
503                          bufDat);
504
505             iovs[numIOVs].iov_base = msgBuf;
506             iovs[numIOVs].iov_len  = PORT_Strlen(msgBuf);
507             numIOVs++;
508         } else if (reqLen < bufDat) {
509             PORT_Sprintf(msgBuf, "Discarded %d characters.\r\n", 
510                          bufDat - reqLen);
511
512             iovs[numIOVs].iov_base = msgBuf;
513             iovs[numIOVs].iov_len  = PORT_Strlen(msgBuf);
514             numIOVs++;
515         }
516
517         if (reqLen > 0) {
518             if (verbose > 1) 
519                 fwrite(buf, 1, reqLen, stdout); /* display it */
520
521             iovs[numIOVs].iov_base = buf;
522             iovs[numIOVs].iov_len  = reqLen;
523             numIOVs++;
524         }
525
526         /* Don't add the EOF if we want to test bulk encryption */
527         if (!testBulk) {
528             iovs[numIOVs].iov_base = (char *)EOFmsg;
529             iovs[numIOVs].iov_len  = sizeof EOFmsg - 1;
530             numIOVs++;
531         }
532
533         rv = PR_Writev(ssl_sock, iovs, numIOVs, PR_INTERVAL_NO_TIMEOUT);
534         if (rv < 0) {
535             errWarn("PR_Writev");
536             break;
537         }
538
539     } while (0);
540
541 cleanup:
542     if (ssl_sock) {
543         PR_Close(ssl_sock);
544     } else if (tcp_sock) {
545         PR_Close(tcp_sock);
546     }
547     if (local_file_fd)
548         PR_Close(local_file_fd);
549     VLOG(("selfserv: handle_connection: exiting\n"));
550
551     /* do a nice shutdown if asked. */
552     if (!strncmp(buf, stopCmd, sizeof stopCmd - 1)) {
553         VLOG(("selfserv: handle_connection: stop command"));
554         stop_server();
555     }
556     VLOG(("selfserv: handle_connection: exiting"));
557     return SECSuccess;  /* success */
558 }
559
560 #ifdef XP_UNIX
561
562 void sigusr1_handler(int sig)
563 {
564     VLOG(("selfserv: sigusr1_handler: stop server"));
565     stop_server();
566 }
567
568 #endif
569
570 SECStatus
571 do_accepts(
572     PRFileDesc *listen_sock,
573     PRFileDesc *model_sock,
574     int         requestCert
575     )
576 {
577     PRNetAddr   addr;
578     PRErrorCode  perr;
579 #ifdef XP_UNIX
580     struct sigaction act;
581 #endif
582
583     VLOG(("selfserv: do_accepts: starting"));
584     PR_SetThreadPriority( PR_GetCurrentThread(), PR_PRIORITY_HIGH);
585
586     acceptorThread = PR_GetCurrentThread();
587 #ifdef XP_UNIX
588     /* set up the signal handler */
589     act.sa_handler = sigusr1_handler;
590     sigemptyset(&act.sa_mask);
591     act.sa_flags = 0;
592     if (sigaction(SIGUSR1, &act, NULL)) {
593         fprintf(stderr, "Error installing signal handler.\n");
594         exit(1);
595     }
596 #endif
597     while (!stopping) {
598         PRFileDesc *tcp_sock;
599         PRCList    *myLink;
600
601         FPRINTF(stderr, "\n\n\nselfserv: About to call accept.\n");
602         tcp_sock = PR_Accept(listen_sock, &addr, PR_INTERVAL_NO_TIMEOUT);
603         if (tcp_sock == NULL) {
604             perr      = PR_GetError();
605             if ((perr != PR_CONNECT_RESET_ERROR &&
606                  perr != PR_PENDING_INTERRUPT_ERROR) || verbose) {
607                 errWarn("PR_Accept");
608             } 
609             if (perr == PR_CONNECT_RESET_ERROR) {
610                 FPRINTF(stderr, 
611                         "Ignoring PR_CONNECT_RESET_ERROR error - continue\n");
612                 continue;
613             }
614             stopping = 1;
615             break;
616         }
617
618         VLOG(("selfserv: do_accept: Got connection\n"));
619
620         PZ_Lock(qLock);
621         while (PR_CLIST_IS_EMPTY(&freeJobs) && !stopping) {
622             PZ_WaitCondVar(freeListNotEmptyCv, PR_INTERVAL_NO_TIMEOUT);
623         }
624         if (stopping) {
625             PZ_Unlock(qLock);
626             if (tcp_sock) {
627                 PR_Close(tcp_sock);
628             }
629             break;
630         }
631         myLink = PR_LIST_HEAD(&freeJobs);
632         PR_REMOVE_AND_INIT_LINK(myLink);
633         /* could release qLock here and reaquire it 7 lines below, but 
634         ** why bother for 4 assignment statements? 
635         */
636         {
637             JOB * myJob = (JOB *)myLink;
638             myJob->tcp_sock    = tcp_sock;
639             myJob->model_sock  = model_sock;
640             myJob->requestCert = requestCert;
641         }
642
643         PR_APPEND_LINK(myLink, &jobQ);
644         PZ_NotifyCondVar(jobQNotEmptyCv);
645         PZ_Unlock(qLock);
646     }
647
648     FPRINTF(stderr, "selfserv: Closing listen socket.\n");
649     VLOG(("selfserv: do_accepts: exiting"));
650     if (listen_sock) {
651         PR_Close(listen_sock);
652     }
653     return SECSuccess;
654 }
655
656 PRFileDesc *
657 getBoundListenSocket(unsigned short port)
658 {
659     PRFileDesc *       listen_sock;
660     int                listenQueueDepth = 5 + (2 * maxThreads);
661     PRStatus           prStatus;
662     PRNetAddr          addr;
663     PRSocketOptionData opt;
664
665     addr.inet.family = PR_AF_INET;
666     addr.inet.ip     = PR_INADDR_ANY;
667     addr.inet.port   = PR_htons(port);
668
669     listen_sock = PR_NewTCPSocket();
670     if (listen_sock == NULL) {
671         errExit("PR_NewTCPSocket");
672     }
673
674     opt.option = PR_SockOpt_Nonblocking;
675     opt.value.non_blocking = PR_FALSE;
676     prStatus = PR_SetSocketOption(listen_sock, &opt);
677     if (prStatus < 0) {
678         PR_Close(listen_sock);
679         errExit("PR_SetSocketOption(PR_SockOpt_Nonblocking)");
680     }
681
682     opt.option=PR_SockOpt_Reuseaddr;
683     opt.value.reuse_addr = PR_TRUE;
684     prStatus = PR_SetSocketOption(listen_sock, &opt);
685     if (prStatus < 0) {
686         PR_Close(listen_sock);
687         errExit("PR_SetSocketOption(PR_SockOpt_Reuseaddr)");
688     }
689
690 #ifndef WIN95
691     /* Set PR_SockOpt_Linger because it helps prevent a server bind issue
692      * after clean shutdown . See bug 331413 .
693      * Don't do it in the WIN95 build configuration because clean shutdown is
694      * not implemented, and PR_SockOpt_Linger causes a hang in ssl.sh .
695      * See bug 332348 */
696     opt.option=PR_SockOpt_Linger;
697     opt.value.linger.polarity = PR_TRUE;
698     opt.value.linger.linger = PR_SecondsToInterval(1);
699     prStatus = PR_SetSocketOption(listen_sock, &opt);
700     if (prStatus < 0) {
701         PR_Close(listen_sock);
702         errExit("PR_SetSocketOption(PR_SockOpt_Linger)");
703     }
704 #endif
705
706     prStatus = PR_Bind(listen_sock, &addr);
707     if (prStatus < 0) {
708         PR_Close(listen_sock);
709         errExit("PR_Bind");
710     }
711
712     prStatus = PR_Listen(listen_sock, listenQueueDepth);
713     if (prStatus < 0) {
714         PR_Close(listen_sock);
715         errExit("PR_Listen");
716     }
717     return listen_sock;
718 }
719
720 void
721 server_main(
722     PRFileDesc *        listen_sock,
723     int                 requestCert, 
724     SECKEYPrivateKey ** privKey,
725     CERTCertificate **  cert,
726     const char *expectedHostNameVal)
727 {
728     PRFileDesc *model_sock      = NULL;
729
730     /* Now, do the accepting, here in the main thread. */
731     do_accepts(listen_sock, model_sock, requestCert);
732
733     terminateWorkerThreads();
734
735         if (model_sock) {
736             PR_Close(model_sock);
737         }
738
739 }
740
741 int          numChildren;
742 PRProcess *  child[MAX_PROCS];
743
744 PRProcess *
745 haveAChild(int argc, char **argv, PRProcessAttr * attr)
746 {
747     PRProcess *  newProcess;
748
749     newProcess = PR_CreateProcess(argv[0], argv, NULL, attr);
750     if (!newProcess) {
751         errWarn("Can't create new process.");
752     } else {
753         child[numChildren++] = newProcess;
754     }
755     return newProcess;
756 }
757
758 int
759 main(int argc, char **argv)
760 {
761     char *               progName    = NULL;
762     const char *         pidFile     = NULL;
763     char *               tmp;
764     PRFileDesc *         listen_sock;
765     int                  optionsFound = 0;
766     unsigned short       port        = 0;
767     SECStatus            rv;
768     PRStatus             prStatus;
769     PRBool               bindOnly = PR_FALSE;
770     PRBool               useLocalThreads = PR_FALSE;
771     PLOptState          *optstate;
772     PLOptStatus          status;
773
774     tmp = strrchr(argv[0], '/');
775     tmp = tmp ? tmp + 1 : argv[0];
776     progName = strrchr(tmp, '\\');
777     progName = progName ? progName + 1 : tmp;
778
779     PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
780
781     /* please keep this list of options in ASCII collating sequence.
782     ** numbers, then capital letters, then lower case, alphabetical. 
783     */
784     optstate = PL_CreateOptState(argc, argv, 
785         "Dbhi:p:t:v");
786     while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
787         ++optionsFound;
788         switch(optstate->option) {
789         case 'D': noDelay = PR_TRUE; break;
790
791         case 'b': bindOnly = PR_TRUE; break;
792
793         case 'h': Usage(progName); exit(0); break;
794
795         case 'i': pidFile = optstate->value; break;
796
797         case 'p': port = PORT_Atoi(optstate->value); break;
798
799         case 't':
800             maxThreads = PORT_Atoi(optstate->value);
801             if ( maxThreads > MAX_THREADS ) maxThreads = MAX_THREADS;
802             if ( maxThreads < MIN_THREADS ) maxThreads = MIN_THREADS;
803             break;
804
805         case 'v': verbose++; break;
806
807         default:
808         case '?':
809             fprintf(stderr, "Unrecognized or bad option specified.\n");
810             fprintf(stderr, "Run '%s -h' for usage information.\n", progName);
811             exit(4);
812             break;
813         }
814     }
815     PL_DestroyOptState(optstate);
816     if (status == PL_OPT_BAD) {
817         fprintf(stderr, "Unrecognized or bad option specified.\n");
818         fprintf(stderr, "Run '%s -h' for usage information.\n", progName);
819         exit(5);
820     }
821     if (!optionsFound) {
822         Usage(progName);
823         exit(51);
824     } 
825
826     /* The -b (bindOnly) option is only used by the ssl.sh test
827      * script on Linux to determine whether a previous selfserv
828      * process has fully died and freed the port.  (Bug 129701)
829      */
830     if (bindOnly) {
831         listen_sock = getBoundListenSocket(port);
832         if (!listen_sock) {
833             exit(1);
834         }
835         if (listen_sock) {
836             PR_Close(listen_sock);
837         }
838         exit(0);
839     }
840
841     if (port == 0) {
842         fprintf(stderr, "Required argument 'port' must be non-zero value\n");
843         exit(7);
844     }
845
846     if (pidFile) {
847         FILE *tmpfile=fopen(pidFile,"w+");
848
849         if (tmpfile) {
850             fprintf(tmpfile,"%d",getpid());
851             fclose(tmpfile);
852         }
853     }
854
855     tmp = getenv("TMP");
856     if (!tmp)
857         tmp = getenv("TMPDIR");
858     if (!tmp)
859         tmp = getenv("TEMP");
860     /* we're an ordinary single process server. */
861     listen_sock = getBoundListenSocket(port);
862     prStatus = PR_SetFDInheritable(listen_sock, PR_FALSE);
863     if (prStatus != PR_SUCCESS)
864         errExit("PR_SetFDInheritable");
865
866     lm = PR_NewLogModule("TestCase");
867
868 /* allocate the array of thread slots, and launch the worker threads. */
869     rv = launch_threads(&jobLoop, 0, 0, 0, useLocalThreads);
870
871     if (rv == SECSuccess) {
872         server_main(listen_sock, 0, 0, 0,
873                     0);
874     }
875
876     VLOG(("selfserv: server_thread: exiting"));
877
878     if (failedToNegotiateName) {
879         fprintf(stderr, "selfserv: Failed properly negotiate server name\n");
880         exit(1);
881     }
882
883     PR_Cleanup();
884     printf("selfserv: normal termination\n");
885     return 0;
886 }
887