|
|
1.1 root 1: /* main.cpp */
2:
3: /* Synchronet main/telnet server thread and related functions */
4:
5: /* $Id: main.cpp,v 1.468 2006/12/30 04:36:44 rswindell Exp $ */
6:
7: /****************************************************************************
8: * @format.tab-size 4 (Plain Text/Source Code File Header) *
9: * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
10: * *
11: * Copyright 2006 Rob Swindell - http://www.synchro.net/copyright.html *
12: * *
13: * This program is free software; you can redistribute it and/or *
14: * modify it under the terms of the GNU General Public License *
15: * as published by the Free Software Foundation; either version 2 *
16: * of the License, or (at your option) any later version. *
17: * See the GNU General Public License for more details: gpl.txt or *
18: * http://www.fsf.org/copyleft/gpl.html *
19: * *
20: * Anonymous FTP access to the most recent released source is available at *
21: * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
22: * *
23: * Anonymous CVS access to the development source and modification history *
24: * is available at cvs.synchro.net:/cvsroot/sbbs, example: *
25: * cvs -d :pserver:[email protected]:/cvsroot/sbbs login *
26: * (just hit return, no password is necessary) *
27: * cvs -d :pserver:[email protected]:/cvsroot/sbbs checkout src *
28: * *
29: * For Synchronet coding style and modification guidelines, see *
30: * http://www.synchro.net/source.html *
31: * *
32: * You are encouraged to submit any modifications (preferably in Unix diff *
33: * format) via e-mail to [email protected] *
34: * *
35: * Note: If this box doesn't appear square, then you need to fix your tabs. *
36: ****************************************************************************/
37:
38: #include "sbbs.h"
39: #include "ident.h"
40: #include "telnet.h"
41:
42: #ifdef __unix__
43: #include <sys/un.h>
44: #ifndef SUN_LEN
45: #define SUN_LEN(su) \
46: (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
47: #endif
48: #endif
49:
50: //---------------------------------------------------------------------------
51:
52: #define TELNET_SERVER "Synchronet Telnet Server"
53: #define STATUS_WFC "Listening"
54:
55: #define TIMEOUT_THREAD_WAIT 60 // Seconds (was 15)
56: #define IO_THREAD_BUF_SIZE 20000 // Bytes
57:
58: // Globals
59: #ifdef _WIN32
60: HANDLE exec_mutex=NULL;
61: HINSTANCE hK32=NULL;
62:
63: #if defined(_DEBUG) && defined(_MSC_VER)
64: HANDLE debug_log=INVALID_HANDLE_VALUE;
65: _CrtMemState mem_chkpoint;
66: #endif // _DEBUG && _MSC_VER
67:
68: #endif // _WIN32
69:
70: #ifdef USE_CRYPTLIB
71: #define SSH_END() if(ssh) cryptDestroySession(sbbs->ssh_session);
72: #else
73: #define SSH_END()
74: #endif
75:
76: time_t uptime=0;
77: DWORD served=0;
78:
79: static ulong node_threads_running=0;
80: static ulong thread_count=0;
81:
82: char lastuseron[LEN_ALIAS+1]; /* Name of user last online */
83: RingBuf* node_inbuf[MAX_NODES];
84: SOCKET spy_socket[MAX_NODES];
85: #ifdef __unix__
86: SOCKET uspy_socket[MAX_NODES]; /* UNIX domain spy sockets */
87: #endif
88: SOCKET node_socket[MAX_NODES];
89: static SOCKET telnet_socket=INVALID_SOCKET;
90: static SOCKET rlogin_socket=INVALID_SOCKET;
91: #ifdef USE_CRYPTLIB
92: static SOCKET ssh_socket=INVALID_SOCKET;
93: #endif
94: static sbbs_t* sbbs=NULL;
95: static scfg_t scfg;
96: static char * text[TOTAL_TEXT];
97: static WORD first_node;
98: static WORD last_node;
99: static bool terminate_server=false;
100: static str_list_t recycle_semfiles;
101: static str_list_t shutdown_semfiles;
102: #ifdef _THREAD_SUID_BROKEN
103: int thread_suid_broken=TRUE; /* NPTL is no longer broken */
104: #endif
105:
106: extern "C" {
107:
108: static bbs_startup_t* startup=NULL;
109:
110: static void status(char* str)
111: {
112: if(startup!=NULL && startup->status!=NULL)
113: startup->status(startup->cbdata,str);
114: }
115:
116: static void update_clients()
117: {
118: if(startup!=NULL && startup->clients!=NULL)
119: startup->clients(startup->cbdata,node_threads_running);
120: }
121:
122: void client_on(SOCKET sock, client_t* client, BOOL update)
123: {
124: if(startup!=NULL && startup->client_on!=NULL)
125: startup->client_on(startup->cbdata,TRUE,sock,client,update);
126: }
127:
128: static void client_off(SOCKET sock)
129: {
130: if(startup!=NULL && startup->client_on!=NULL)
131: startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
132: }
133:
134: static void thread_up(BOOL setuid)
135: {
136: thread_count++;
137: if(startup!=NULL && startup->thread_up!=NULL)
138: startup->thread_up(startup->cbdata,TRUE,setuid);
139: }
140:
141: static void thread_down()
142: {
143: if(thread_count>0)
144: thread_count--;
145: if(startup!=NULL && startup->thread_up!=NULL)
146: startup->thread_up(startup->cbdata,FALSE,FALSE);
147: }
148:
149: int lputs(int level, char* str)
150: {
151: if(startup==NULL || startup->lputs==NULL || str==NULL)
152: return(0);
153:
154: return(startup->lputs(startup->cbdata,level,str));
155: }
156:
157: int lprintf(int level, char *fmt, ...)
158: {
159: va_list argptr;
160: char sbuf[1024];
161:
162: va_start(argptr,fmt);
163: vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
164: sbuf[sizeof(sbuf)-1]=0;
165: va_end(argptr);
166: return(lputs(level,sbuf));
167: }
168:
169: int eprintf(int level, char *fmt, ...)
170: {
171: va_list argptr;
172: char sbuf[1024];
173:
174: if(startup==NULL || startup->event_lputs==NULL)
175: return(0);
176:
177: va_start(argptr,fmt);
178: vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
179: sbuf[sizeof(sbuf)-1]=0;
180: va_end(argptr);
181: strip_ctrl(sbuf);
182: return(startup->event_lputs(level,sbuf));
183: }
184:
185: SOCKET open_socket(int type, const char* protocol)
186: {
187: SOCKET sock;
188: char error[256];
189:
190: sock=socket(AF_INET, type, IPPROTO_IP);
191: if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL)
192: startup->socket_open(startup->cbdata,TRUE);
193: if(sock!=INVALID_SOCKET && set_socket_options(&scfg, sock, protocol, error, sizeof(error)))
194: lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
195:
196: return(sock);
197: }
198:
199: SOCKET accept_socket(SOCKET s, SOCKADDR* addr, socklen_t* addrlen)
200: {
201: SOCKET sock;
202:
203: sock=accept(s,addr,addrlen);
204: if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL)
205: startup->socket_open(startup->cbdata,TRUE);
206:
207: return(sock);
208: }
209:
210: int close_socket(SOCKET sock)
211: {
212: int result;
213:
214: if(sock==INVALID_SOCKET || sock==0)
215: return(0);
216:
217: shutdown(sock,SHUT_RDWR); /* required on Unix */
218: result=closesocket(sock);
219: if(startup!=NULL && startup->socket_open!=NULL)
220: startup->socket_open(startup->cbdata,FALSE);
221: if(result!=0 && ERROR_VALUE!=ENOTSOCK)
222: lprintf(LOG_ERR,"!ERROR %d closing socket %d",ERROR_VALUE,sock);
223: return(result);
224: }
225:
226:
227: u_long resolve_ip(char *addr)
228: {
229: HOSTENT* host;
230: char* p;
231:
232: if(*addr==0)
233: return((u_long)INADDR_NONE);
234:
235: for(p=addr;*p;p++)
236: if(*p!='.' && !isdigit(*p))
237: break;
238: if(!(*p))
239: return(inet_addr(addr));
240: if((host=gethostbyname(addr))==NULL)
241: return((u_long)INADDR_NONE);
242: return(*((ulong*)host->h_addr_list[0]));
243: }
244:
245: } /* extern "C" */
246:
247: #ifdef _WINSOCKAPI_
248:
249: WSADATA WSAData;
250: #define SOCKLIB_DESC WSAData.szDescription
251: static BOOL WSAInitialized=FALSE;
252:
253: static BOOL winsock_startup(void)
254: {
255: int status; /* Status Code */
256:
257: if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
258: lprintf(LOG_INFO,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
259: WSAInitialized=TRUE;
260: return(TRUE);
261: }
262:
263: lprintf(LOG_ERR,"!WinSock startup ERROR %d", status);
264: return(FALSE);
265: }
266:
267: #else /* No WINSOCK */
268:
269: #define winsock_startup() (TRUE)
270: #define SOCKLIB_DESC NULL
271:
272: #endif
273:
274: DLLEXPORT void DLLCALL sbbs_srand()
275: {
276: DWORD seed = time(NULL) ^ (DWORD)GetCurrentThreadId();
277:
278: #if defined(HAS_DEV_RANDOM) && defined(RANDOM_DEV)
279: int rf;
280:
281: if((rf=open(RANDOM_DEV, O_RDONLY))!=-1) {
282: read(rf, &seed, sizeof(seed));
283: close(rf);
284: }
285: #endif
286:
287: srand(seed);
288: sbbs_random(10); /* Throw away first number */
289: }
290:
291: int DLLCALL sbbs_random(int n)
292: {
293: return(xp_random(n));
294: }
295:
296: #ifdef JAVASCRIPT
297:
298: static js_server_props_t js_server_props;
299:
300: JSBool
301: DLLCALL js_CreateArrayOfStrings(JSContext* cx, JSObject* parent, const char* name, char* str[],uintN flags)
302: {
303: JSObject* array;
304: JSString* js_str;
305: jsval val;
306: size_t i;
307: jsuint len=0;
308:
309: if(JS_GetProperty(cx,parent,name,&val) && val!=JSVAL_VOID)
310: array=JSVAL_TO_OBJECT(val);
311: else
312: if((array=JS_NewArrayObject(cx, 0, NULL))==NULL) /* Assertion here, in _heap_alloc_dbg, June-21-2004 */
313: return(JS_FALSE); /* Caused by nntpservice.js? */
314:
315: if(!JS_DefineProperty(cx, parent, name, OBJECT_TO_JSVAL(array)
316: ,NULL,NULL,flags))
317: return(JS_FALSE);
318:
319: if(!JS_GetArrayLength(cx, array, &len))
320: return(JS_FALSE);
321:
322: for(i=0;str[i]!=NULL;i++) {
323: if((js_str = JS_NewStringCopyZ(cx, str[i]))==NULL)
324: break;
325: val = STRING_TO_JSVAL(js_str);
326: if(!JS_SetElement(cx, array, len+i, &val))
327: break;
328: }
329:
330: return(JS_TRUE);
331: }
332:
333: /* Convert from Synchronet-specific jsSyncMethodSpec to JSAPI's JSFunctionSpec */
334:
335: JSBool
336: DLLCALL js_DescribeSyncObject(JSContext* cx, JSObject* obj, const char* str, int ver)
337: {
338: JSString* js_str = JS_NewStringCopyZ(cx, str);
339:
340: if(js_str==NULL)
341: return(JS_FALSE);
342:
343: if(ver < 10000) /* auto convert 313 to 31300 */
344: ver*=100;
345:
346: return(JS_DefineProperty(cx,obj,"_description"
347: ,STRING_TO_JSVAL(js_str),NULL,NULL,JSPROP_READONLY)
348: && JS_DefineProperty(cx,obj,"_ver"
349: ,INT_TO_JSVAL(ver),NULL,NULL,JSPROP_READONLY));
350: }
351:
352: JSBool
353: DLLCALL js_DescribeSyncConstructor(JSContext* cx, JSObject* obj, const char* str)
354: {
355: JSString* js_str = JS_NewStringCopyZ(cx, str);
356:
357: if(js_str==NULL)
358: return(JS_FALSE);
359:
360: return(JS_DefineProperty(cx,obj,"_constructor"
361: ,STRING_TO_JSVAL(js_str),NULL,NULL,JSPROP_READONLY));
362: }
363:
364: #ifdef BUILD_JSDOCS
365:
366: static const char* method_array_name = "_method_list";
367: static const char* propver_array_name = "_property_ver_list";
368:
369: /*
370: * from jsatom.c:
371: * Keep this in sync with jspubtd.h -- an assertion below will insist that
372: * its length match the JSType enum's JSTYPE_LIMIT limit value.
373: */
374: static const char *js_type_str[] = {
375: "void", // changed from "undefined"
376: "object",
377: "function",
378: "string",
379: "number",
380: "boolean",
381: "array",
382: "alias",
383: "undefined",
384: "null"
385: };
386:
387: JSBool
388: DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
389: {
390: uint i;
391: long ver;
392: jsval val;
393: jsuint len=0;
394: JSObject* array;
395:
396: if((array=JS_NewArrayObject(cx, 0, NULL))==NULL)
397: return(JS_FALSE);
398:
399: if(!JS_DefineProperty(cx, obj, propver_array_name, OBJECT_TO_JSVAL(array)
400: ,NULL,NULL,JSPROP_READONLY))
401: return(JS_FALSE);
402:
403: for(i=0;props[i].name;i++) {
404: if(!JS_DefinePropertyWithTinyId(cx, obj, /* Never reserve any "slots" for properties */
405: props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
406: return(JS_FALSE);
407: if(props[i].flags&JSPROP_ENUMERATE) { /* No need to version invisible props */
408: if((ver=props[i].ver) < 10000) /* auto convert 313 to 31300 */
409: ver*=100;
410: val = INT_TO_JSVAL(ver);
411: if(!JS_SetElement(cx, array, len++, &val))
412: return(JS_FALSE);
413: }
414: }
415:
416: return(JS_TRUE);
417: }
418:
419: JSBool
420: DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs, BOOL append)
421: {
422: int i;
423: jsuint len=0;
424: long ver;
425: jsval val;
426: JSObject* method;
427: JSObject* method_array;
428: JSString* js_str;
429:
430: /* Return existing method_list array if it's already been created */
431: if(JS_GetProperty(cx,obj,method_array_name,&val) && val!=JSVAL_VOID)
432: method_array=JSVAL_TO_OBJECT(val);
433: else
434: if((method_array=JS_NewArrayObject(cx, 0, NULL))==NULL)
435: return(JS_FALSE);
436:
437: if(!JS_DefineProperty(cx, obj, method_array_name, OBJECT_TO_JSVAL(method_array)
438: , NULL, NULL, 0))
439: return(JS_FALSE);
440:
441: if(append)
442: if(!JS_GetArrayLength(cx, method_array, &len))
443: return(JS_FALSE);
444:
445: for(i=0;funcs[i].name;i++) {
446:
447: if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
448: return(JS_FALSE);
449:
450: if(funcs[i].type==JSTYPE_ALIAS)
451: continue;
452:
453: method = JS_NewObject(cx, NULL, NULL, method_array); /* exception here June-7-2003 */
454:
455: if(method==NULL)
456: return(JS_FALSE);
457:
458: if(funcs[i].name!=NULL) {
459: if((js_str=JS_NewStringCopyZ(cx,funcs[i].name))==NULL)
460: return(JS_FALSE);
461: val = STRING_TO_JSVAL(js_str);
462: JS_SetProperty(cx, method, "name", &val);
463: }
464:
465: val = INT_TO_JSVAL(funcs[i].nargs);
466: if(!JS_SetProperty(cx, method, "nargs", &val))
467: return(JS_FALSE);
468:
469: if((js_str=JS_NewStringCopyZ(cx,js_type_str[funcs[i].type]))==NULL)
470: return(JS_FALSE);
471: val = STRING_TO_JSVAL(js_str);
472: JS_SetProperty(cx, method, "type", &val);
473:
474: if(funcs[i].args!=NULL) {
475: if((js_str=JS_NewStringCopyZ(cx,funcs[i].args))==NULL)
476: return(JS_FALSE);
477: val = STRING_TO_JSVAL(js_str);
478: JS_SetProperty(cx, method, "args", &val);
479: }
480:
481: if(funcs[i].desc!=NULL) {
482: if((js_str=JS_NewStringCopyZ(cx,funcs[i].desc))==NULL)
483: return(JS_FALSE);
484: val = STRING_TO_JSVAL(js_str);
485: JS_SetProperty(cx, method, "desc", &val);
486: }
487:
488: if(funcs[i].ver) {
489: if((ver=funcs[i].ver) < 10000) /* auto convert 313 to 31300 */
490: ver*=100;
491: val = INT_TO_JSVAL(ver);
492: JS_SetProperty(cx,method, "ver", &val);
493: }
494:
495: val=OBJECT_TO_JSVAL(method);
496: if(!JS_SetElement(cx, method_array, len+i, &val))
497: return(JS_FALSE);
498: }
499:
500: return(JS_TRUE);
501: }
502:
503: #else // NON-JSDOCS
504:
505: JSBool
506: DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
507: {
508: uint i;
509:
510: for(i=0;props[i].name;i++)
511: if(!JS_DefinePropertyWithTinyId(cx, obj,
512: props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
513: return(JS_FALSE);
514:
515: return(JS_TRUE);
516: }
517:
518:
519: JSBool
520: DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs, BOOL append)
521: {
522: uint i;
523:
524: for(i=0;funcs[i].name;i++)
525: if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
526: return(JS_FALSE);
527: return(JS_TRUE);
528: }
529:
530: #endif
531:
532: /* This is a stream-lined version of JS_DefineConstDoubles */
533: JSBool
534: DLLCALL js_DefineConstIntegers(JSContext* cx, JSObject* obj, jsConstIntSpec* ints, int flags)
535: {
536: uint i;
537: jsval val;
538:
539: for(i=0;ints[i].name;i++) {
540: if(!JS_NewNumberValue(cx, ints[i].val, &val))
541: return(JS_FALSE);
542:
543: if(!JS_DefineProperty(cx, obj, ints[i].name, val ,NULL, NULL, flags))
544: return(JS_FALSE);
545: }
546:
547: return(JS_TRUE);
548: }
549:
550: char*
551: DLLCALL js_ValueToStringBytes(JSContext* cx, jsval val, size_t* len)
552: {
553: JSString* str;
554:
555: if((str=JS_ValueToString(cx, val))==NULL)
556: return(NULL);
557:
558: if(len!=NULL)
559: *len = JS_GetStringLength(str);
560:
561: return(JS_GetStringBytes(str));
562: }
563:
564: static JSBool
565: js_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
566: {
567: uintN i=0;
568: int32 level=LOG_INFO;
569: JSString* str=NULL;
570: sbbs_t* sbbs;
571:
572: if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
573: return(JS_FALSE);
574:
575: if(argc > 1 && JSVAL_IS_NUMBER(argv[i]))
576: JS_ValueToInt32(cx,argv[i++],&level);
577:
578: for(; i<argc; i++) {
579: if((str=JS_ValueToString(cx, argv[i]))==NULL)
580: return(JS_FALSE);
581: if(sbbs->online==ON_LOCAL) {
582: if(startup!=NULL && startup->event_lputs!=NULL)
583: startup->event_lputs(level,JS_GetStringBytes(str));
584: } else
585: lputs(level,JS_GetStringBytes(str));
586: }
587:
588: if(str==NULL)
589: *rval = JSVAL_VOID;
590: else
591: *rval = STRING_TO_JSVAL(str);
592: return(JS_TRUE);
593: }
594:
595: static JSBool
596: js_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
597: {
598: uchar* buf;
599: int32 len=128;
600: sbbs_t* sbbs;
601:
602: if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
603: return(JS_FALSE);
604:
605: if(argc)
606: JS_ValueToInt32(cx,argv[0],&len);
607:
608: if((buf=(uchar*)malloc(len))==NULL)
609: return(JS_TRUE);
610:
611: len=RingBufRead(&sbbs->inbuf,buf,len);
612:
613: if(len>0)
614: *rval = STRING_TO_JSVAL(JS_NewStringCopyN(cx,(char*)buf,len));
615:
616: free(buf);
617: return(JS_TRUE);
618: }
619:
620: static JSBool
621: js_readln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
622: {
623: char* buf;
624: int32 len=128;
625: sbbs_t* sbbs;
626:
627: if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
628: return(JS_FALSE);
629:
630: if(argc)
631: JS_ValueToInt32(cx,argv[0],&len);
632:
633: if((buf=(char*)malloc(len))==NULL)
634: return(JS_TRUE);
635:
636: len=sbbs->getstr(buf,len,K_NONE);
637:
638: if(len>0)
639: *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,buf));
640:
641: free(buf);
642: return(JS_TRUE);
643: }
644:
645: static JSBool
646: js_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
647: {
648: uintN i;
649: JSString* str=NULL;
650: sbbs_t* sbbs;
651:
652: if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
653: return(JS_FALSE);
654:
655: for (i = 0; i < argc; i++) {
656: if((str=JS_ValueToString(cx, argv[i]))==NULL)
657: return(JS_FALSE);
658: if(sbbs->online==ON_LOCAL)
659: eprintf(LOG_INFO,"%s",JS_GetStringBytes(str));
660: else
661: sbbs->bputs(JS_GetStringBytes(str));
662: }
663:
664: if(str==NULL)
665: *rval = JSVAL_VOID;
666: else
667: *rval = STRING_TO_JSVAL(str);
668: return(JS_TRUE);
669: }
670:
671: static JSBool
672: js_write_raw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
673: {
674: uintN i;
675: char* str=NULL;
676: size_t len;
677: sbbs_t* sbbs;
678:
679: if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
680: return(JS_FALSE);
681:
682: for (i = 0; i < argc; i++) {
683: if((str=js_ValueToStringBytes(cx, argv[i], &len))==NULL)
684: return(JS_FALSE);
685: sbbs->putcom(str, len);
686: }
687:
688: return(JS_TRUE);
689: }
690:
691: static JSBool
692: js_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
693: {
694: sbbs_t* sbbs;
695:
696: if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
697: return(JS_FALSE);
698:
699: js_write(cx,obj,argc,argv,rval);
700: if(sbbs->online==ON_REMOTE)
701: sbbs->bputs(crlf);
702:
703: return(JS_TRUE);
704: }
705:
706: static JSBool
707: js_printf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
708: {
709: char* p;
710: sbbs_t* sbbs;
711:
712: if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
713: return(JS_FALSE);
714:
715: if((p = js_sprintf(cx, 0, argc, argv))==NULL) {
716: JS_ReportError(cx,"js_sprintf failed");
717: return(JS_FALSE);
718: }
719:
720: if(sbbs->online==ON_LOCAL)
721: eprintf(LOG_INFO,"%s",p);
722: else
723: sbbs->bputs(p);
724:
725: *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p));
726:
727: js_sprintf_free(p);
728:
729: return(JS_TRUE);
730: }
731:
732: static JSBool
733: js_alert(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
734: {
735: JSString * str;
736: sbbs_t* sbbs;
737:
738: if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
739: return(JS_FALSE);
740:
741: if((str=JS_ValueToString(cx, argv[0]))==NULL)
742: return(JS_FALSE);
743:
744: sbbs->attr(sbbs->cfg.color[clr_err]);
745: sbbs->bputs(JS_GetStringBytes(str));
746: sbbs->attr(LIGHTGRAY);
747: sbbs->bputs(crlf);
748:
749: return(JS_TRUE);
750: }
751:
752: static JSBool
753: js_confirm(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
754: {
755: JSString * str;
756: sbbs_t* sbbs;
757:
758: if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
759: return(JS_FALSE);
760:
761: if((str=JS_ValueToString(cx, argv[0]))==NULL)
762: return(JS_FALSE);
763:
764: *rval = BOOLEAN_TO_JSVAL(sbbs->yesno(JS_GetStringBytes(str)));
765: return(JS_TRUE);
766: }
767:
768: static JSBool
769: js_prompt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
770: {
771: char instr[81];
772: JSString * prompt;
773: JSString * str;
774: sbbs_t* sbbs;
775:
776: if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
777: return(JS_FALSE);
778:
779: if((prompt=JS_ValueToString(cx, argv[0]))==NULL)
780: return(JS_FALSE);
781:
782: if(argc>1) {
783: if((str=JS_ValueToString(cx, argv[1]))==NULL)
784: return(JS_FALSE);
785: SAFECOPY(instr,JS_GetStringBytes(str));
786: } else
787: instr[0]=0;
788:
789: sbbs->bprintf("\1n\1y\1h%s\1w: ",JS_GetStringBytes(prompt));
790:
791: if(!sbbs->getstr(instr,sizeof(instr)-1,K_EDIT)) {
792: *rval = JSVAL_NULL;
793: return(JS_TRUE);
794: }
795:
796: if((str=JS_NewStringCopyZ(cx, instr))==NULL)
797: return(JS_FALSE);
798:
799: *rval = STRING_TO_JSVAL(str);
800: return(JS_TRUE);
801: }
802:
803: static jsSyncMethodSpec js_global_functions[] = {
804: {"log", js_log, 1, JSTYPE_STRING, JSDOCSTR("[level,] value [,value]")
805: ,JSDOCSTR("add a line of text to the server and/or system log, "
806: "<i>values</i> are typically string constants or variables, "
807: "<i>level</i> is the debug level/priority (default: <tt>LOG_INFO</tt>)")
808: ,311
809: },
810: {"read", js_read, 0, JSTYPE_STRING, JSDOCSTR("[count]")
811: ,JSDOCSTR("read up to count characters from input stream")
812: ,311
813: },
814: {"readln", js_readln, 0, JSTYPE_STRING, JSDOCSTR("[count]")
815: ,JSDOCSTR("read a single line, up to count characters, from input stream")
816: ,311
817: },
818: {"write", js_write, 0, JSTYPE_VOID, JSDOCSTR("value [,value]")
819: ,JSDOCSTR("send one or more values (typically strings) to the server output")
820: ,311
821: },
822: {"write_raw", js_write_raw, 0, JSTYPE_VOID, JSDOCSTR("value [,value]")
823: ,JSDOCSTR("send a stream of bytes (possibly containing NULLs or special control code sequences) to the server output")
824: ,314
825: },
826: {"print", js_writeln, 0, JSTYPE_ALIAS },
827: {"writeln", js_writeln, 0, JSTYPE_VOID, JSDOCSTR("value [,value]")
828: ,JSDOCSTR("send a line of text to the console or event log with automatic line termination (CRLF), "
829: "<i>values</i> are typically string constants or variables (AKA print)")
830: ,311
831: },
832: {"printf", js_printf, 1, JSTYPE_STRING, JSDOCSTR("string format [,value][,value]")
833: ,JSDOCSTR("print a formatted string - <small>CAUTION: for experienced C programmers ONLY</small>")
834: ,310
835: },
836: {"alert", js_alert, 1, JSTYPE_VOID, JSDOCSTR("value")
837: ,JSDOCSTR("print an alert message (ala client-side JS)")
838: ,310
839: },
840: {"prompt", js_prompt, 1, JSTYPE_STRING, JSDOCSTR("[value]")
841: ,JSDOCSTR("displays a prompt (<i>value</i>) and returns a string of user input (ala clent-side JS)")
842: ,310
843: },
844: {"confirm", js_confirm, 1, JSTYPE_BOOLEAN, JSDOCSTR("value")
845: ,JSDOCSTR("displays a Yes/No prompt and returns <i>true</i> or <i>false</i> "
846: "based on users confirmation (ala client-side JS)")
847: ,310
848: },
849: {0}
850: };
851:
852: static void
853: js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
854: {
855: char line[64];
856: char file[MAX_PATH+1];
857: sbbs_t* sbbs;
858: const char* warning;
859:
860: if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
861: return;
862:
863: if(report==NULL) {
864: lprintf(LOG_ERR,"!JavaScript: %s", message);
865: return;
866: }
867:
868: if(report->filename)
869: SAFEPRINTF(file," %s",report->filename);
870: else
871: file[0]=0;
872:
873: if(report->lineno)
874: SAFEPRINTF(line," line %d",report->lineno);
875: else
876: line[0]=0;
877:
878: if(JSREPORT_IS_WARNING(report->flags)) {
879: if(JSREPORT_IS_STRICT(report->flags))
880: warning="strict warning";
881: else
882: warning="warning";
883: } else
884: warning=nulstr;
885:
886: if(sbbs->online==ON_LOCAL)
887: eprintf(LOG_ERR,"!JavaScript %s%s%s: %s",warning,file,line,message);
888: else {
889: lprintf(LOG_ERR,"!JavaScript %s%s%s: %s",warning,file,line,message);
890: sbbs->bprintf("!JavaScript %s%s%s: %s\r\n",warning,file,line,message);
891: }
892: }
893:
894: bool sbbs_t::js_init(ulong* stack_frame)
895: {
896: char node[128];
897:
898: if(cfg.node_num)
899: SAFEPRINTF(node,"Node %d",cfg.node_num);
900: else
901: SAFECOPY(node,client_name);
902:
903: if(startup->js.max_bytes==0) startup->js.max_bytes=JAVASCRIPT_MAX_BYTES;
904: if(startup->js.cx_stack==0) startup->js.cx_stack=JAVASCRIPT_CONTEXT_STACK;
905:
906: lprintf(LOG_DEBUG,"%s JavaScript: Creating runtime: %lu bytes"
907: ,node,startup->js.max_bytes);
908:
909: if((js_runtime = JS_NewRuntime(startup->js.max_bytes))==NULL)
910: return(false);
911:
912: lprintf(LOG_DEBUG,"%s JavaScript: Initializing context (stack: %lu bytes)"
913: ,node,startup->js.cx_stack);
914:
915: if((js_cx = JS_NewContext(js_runtime, startup->js.cx_stack))==NULL)
916: return(false);
917:
918: memset(&js_branch,0,sizeof(js_branch));
919: js_branch.limit = startup->js.branch_limit;
920: js_branch.gc_interval = startup->js.gc_interval;
921: js_branch.yield_interval = startup->js.yield_interval;
922: js_branch.terminated = &terminated;
923: js_branch.auto_terminate = TRUE;
924:
925: bool success=false;
926:
927: do {
928:
929: JS_SetErrorReporter(js_cx, js_ErrorReporter);
930:
931: JS_SetContextPrivate(js_cx, this); /* Store a pointer to sbbs_t instance */
932:
933: /* Global Objects (including system, js, client, Socket, MsgBase, File, User, etc. */
934: if((js_glob=js_CreateCommonObjects(js_cx, &scfg, &cfg, js_global_functions
935: ,uptime, startup->host_name, SOCKLIB_DESC /* system */
936: ,&js_branch /* js */
937: ,&client, client_socket /* client */
938: ,&js_server_props /* server */
939: ))==NULL)
940: break;
941:
942: /* BBS Object */
943: if(js_CreateBbsObject(js_cx, js_glob)==NULL)
944: break;
945:
946: /* Console Object */
947: if(js_CreateConsoleObject(js_cx, js_glob)==NULL)
948: break;
949:
950: if(startup->js.thread_stack) {
951: ulong stack_limit;
952:
953: #if JS_STACK_GROWTH_DIRECTION > 0
954: stack_limit=((ulong)stack_frame)+startup->js.thread_stack;
955: #else
956: stack_limit=((ulong)stack_frame)-startup->js.thread_stack;
957: #endif
958: JS_SetThreadStackLimit(js_cx, stack_limit);
959:
960: lprintf(LOG_DEBUG,"%s JavaScript: Thread stack limit: %lu bytes"
961: ,node, startup->js.thread_stack);
962: }
963:
964: success=true;
965:
966: } while(0);
967:
968: if(!success) {
969: JS_DestroyContext(js_cx);
970: js_cx=NULL;
971: return(false);
972: }
973:
974: return(true);
975: }
976:
977: void sbbs_t::js_create_user_objects(void)
978: {
979: if(js_cx==NULL)
980: return;
981:
982: if(!js_CreateUserObjects(js_cx, js_glob, &cfg, &useron, NULL, subscan))
983: lprintf(LOG_ERR,"!JavaScript ERROR creating user objects");
984: }
985:
986: #endif /* JAVASCRIPT */
987:
988: static BYTE* telnet_interpret(sbbs_t* sbbs, BYTE* inbuf, int inlen,
989: BYTE* outbuf, int& outlen)
990: {
991: BYTE* first_iac=NULL;
992: BYTE* first_cr=NULL;
993: int i;
994:
995: if(inlen<1) {
996: outlen=0;
997: return(inbuf); // no length? No interpretation
998: }
999:
1000: first_iac=(BYTE*)memchr(inbuf, TELNET_IAC, inlen);
1001:
1002: if(!(sbbs->telnet_mode&TELNET_MODE_GATE)
1003: && sbbs->telnet_remote_option[TELNET_BINARY_TX]!=TELNET_WILL
1004: && !(sbbs->console&CON_RAW_IN)) {
1005: if(sbbs->telnet_last_rxch==CR)
1006: first_cr=inbuf;
1007: else
1008: first_cr=(BYTE*)memchr(inbuf, CR, inlen);
1009: }
1010:
1011: if(!sbbs->telnet_cmdlen && first_iac==NULL && first_cr==NULL) {
1012: outlen=inlen;
1013: return(inbuf); // no interpretation needed
1014: }
1015:
1016: if(first_iac!=NULL || first_cr!=NULL) {
1017: if(first_iac!=NULL && (first_cr==NULL || first_iac<first_cr))
1018: outlen=first_iac-inbuf;
1019: else
1020: outlen=first_cr-inbuf;
1021: memcpy(outbuf, inbuf, outlen);
1022: } else
1023: outlen=0;
1024:
1025: for(i=outlen;i<inlen;i++) {
1026: if(!(sbbs->telnet_mode&TELNET_MODE_GATE)
1027: && sbbs->telnet_remote_option[TELNET_BINARY_TX]!=TELNET_WILL
1028: && !(sbbs->console&CON_RAW_IN)) {
1029: if(sbbs->telnet_last_rxch==CR
1030: && (inbuf[i]==LF || inbuf[i]==0)) { // CR/LF or CR/NUL, ignore 2nd char
1031: #if 0 /* Debug CR/LF problems */
1032: lprintf(LOG_INFO,"Node %d CR/%02Xh detected and ignored"
1033: ,sbbs->cfg.node_num, inbuf[i]);
1034: #endif
1035: sbbs->telnet_last_rxch=inbuf[i];
1036: continue;
1037: }
1038: sbbs->telnet_last_rxch=inbuf[i];
1039: }
1040:
1041: if(inbuf[i]==TELNET_IAC && sbbs->telnet_cmdlen==1) { /* escaped 255 */
1042: sbbs->telnet_cmdlen=0;
1043: outbuf[outlen++]=TELNET_IAC;
1044: continue;
1045: }
1046: if(inbuf[i]==TELNET_IAC || sbbs->telnet_cmdlen) {
1047:
1048: if(sbbs->telnet_cmdlen<sizeof(sbbs->telnet_cmd))
1049: sbbs->telnet_cmd[sbbs->telnet_cmdlen++]=inbuf[i];
1050:
1051: uchar command = sbbs->telnet_cmd[1];
1052: uchar option = sbbs->telnet_cmd[2];
1053:
1054: if(sbbs->telnet_cmdlen>=2 && command==TELNET_SB) {
1055: if(inbuf[i]==TELNET_SE
1056: && sbbs->telnet_cmd[sbbs->telnet_cmdlen-2]==TELNET_IAC) {
1057: /* sub-option terminated */
1058: if(option==TELNET_TERM_TYPE
1059: && sbbs->telnet_cmd[3]==TELNET_TERM_IS) {
1060: sprintf(sbbs->terminal,"%.*s",(int)sbbs->telnet_cmdlen-6,sbbs->telnet_cmd+4);
1061: lprintf(LOG_DEBUG,"Node %d %s telnet terminal type: %s"
1062: ,sbbs->cfg.node_num
1063: ,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
1064: ,sbbs->terminal);
1065:
1066: } else if(option==TELNET_TERM_SPEED
1067: && sbbs->telnet_cmd[3]==TELNET_TERM_IS) {
1068: char speed[128];
1069: sprintf(speed,"%.*s",(int)sbbs->telnet_cmdlen-6,sbbs->telnet_cmd+4);
1070: lprintf(LOG_DEBUG,"Node %d %s telnet terminal speed: %s"
1071: ,sbbs->cfg.node_num
1072: ,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
1073: ,speed);
1074:
1075: } else if(option==TELNET_NEGOTIATE_WINDOW_SIZE) {
1076: long cols = (sbbs->telnet_cmd[3]<<8) | sbbs->telnet_cmd[4];
1077: long rows = (sbbs->telnet_cmd[5]<<8) | sbbs->telnet_cmd[6];
1078: lprintf(LOG_DEBUG,"Node %d %s telnet window size: %ux%u"
1079: ,sbbs->cfg.node_num
1080: ,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
1081: ,cols
1082: ,rows);
1083: if(rows && !sbbs->useron.rows) /* auto-detect rows */
1084: sbbs->rows=rows;
1085: if(cols)
1086: sbbs->cols=cols;
1087:
1088: } else if(startup->options&BBS_OPT_DEBUG_TELNET)
1089: lprintf(LOG_DEBUG,"Node %d %s unsupported telnet sub-negotiation cmd: %s"
1090: ,sbbs->cfg.node_num
1091: ,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
1092: ,telnet_opt_desc(option));
1093: sbbs->telnet_cmdlen=0;
1094: }
1095: }
1096: else if(sbbs->telnet_cmdlen==2 && inbuf[i]<TELNET_WILL) {
1097: if(startup->options&BBS_OPT_DEBUG_TELNET)
1098: lprintf(LOG_DEBUG,"Node %d %s telnet cmd: %s"
1099: ,sbbs->cfg.node_num
1100: ,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
1101: ,telnet_cmd_desc(option));
1102: sbbs->telnet_cmdlen=0;
1103: }
1104: else if(sbbs->telnet_cmdlen>=3) { /* telnet option negotiation */
1105:
1106: if(startup->options&BBS_OPT_DEBUG_TELNET)
1107: lprintf(LOG_DEBUG,"Node %d %s telnet cmd: %s %s"
1108: ,sbbs->cfg.node_num
1109: ,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
1110: ,telnet_cmd_desc(command)
1111: ,telnet_opt_desc(option));
1112:
1113: if(!(sbbs->telnet_mode&TELNET_MODE_GATE)) {
1114: if(command==TELNET_DO || command==TELNET_DONT) { /* local options */
1115: if(sbbs->telnet_local_option[option]!=command) {
1116: sbbs->telnet_local_option[option]=command;
1117: sbbs->send_telnet_cmd(telnet_opt_ack(command),option);
1118: }
1119: } else { /* WILL/WONT (remote options) */
1120: if(sbbs->telnet_remote_option[option]!=command) {
1121:
1122: switch(option) {
1123: case TELNET_BINARY_TX:
1124: case TELNET_ECHO:
1125: case TELNET_TERM_TYPE:
1126: case TELNET_TERM_SPEED:
1127: case TELNET_SUP_GA:
1128: case TELNET_NEGOTIATE_WINDOW_SIZE:
1129: sbbs->telnet_remote_option[option]=command;
1130: sbbs->send_telnet_cmd(telnet_opt_ack(command),option);
1131: break;
1132: default: /* unsupported remote options */
1133: if(command==TELNET_WILL) /* NAK */
1134: sbbs->send_telnet_cmd(telnet_opt_nak(command),option);
1135: break;
1136: }
1137: }
1138:
1139: if(command==TELNET_WILL && option==TELNET_TERM_TYPE) {
1140: if(startup->options&BBS_OPT_DEBUG_TELNET)
1141: lprintf(LOG_DEBUG,"Node %d requesting telnet terminal type"
1142: ,sbbs->cfg.node_num);
1143:
1144: char buf[64];
1145: sprintf(buf,"%c%c%c%c%c%c"
1146: ,TELNET_IAC,TELNET_SB
1147: ,TELNET_TERM_TYPE,TELNET_TERM_SEND
1148: ,TELNET_IAC,TELNET_SE);
1149: sbbs->putcom(buf,6);
1150: }
1151: else if(command==TELNET_WILL && option==TELNET_TERM_SPEED) {
1152: if(startup->options&BBS_OPT_DEBUG_TELNET)
1153: lprintf(LOG_DEBUG,"Node %d requesting telnet terminal speed"
1154: ,sbbs->cfg.node_num);
1155:
1156: char buf[64];
1157: sprintf(buf,"%c%c%c%c%c%c"
1158: ,TELNET_IAC,TELNET_SB
1159: ,TELNET_TERM_SPEED,TELNET_TERM_SEND
1160: ,TELNET_IAC,TELNET_SE);
1161: sbbs->putcom(buf,6);
1162: }
1163: }
1164: }
1165:
1166: sbbs->telnet_cmdlen=0;
1167:
1168: }
1169: if(sbbs->telnet_mode&TELNET_MODE_GATE) // Pass-through commads
1170: outbuf[outlen++]=inbuf[i];
1171: } else
1172: outbuf[outlen++]=inbuf[i];
1173: }
1174: return(outbuf);
1175: }
1176:
1177: void sbbs_t::send_telnet_cmd(uchar cmd, uchar opt)
1178: {
1179: char buf[16];
1180:
1181: if(telnet_mode&TELNET_MODE_OFF)
1182: return;
1183:
1184: if(cmd<TELNET_WILL) {
1185: if(startup->options&BBS_OPT_DEBUG_TELNET)
1186: lprintf(LOG_DEBUG,"Node %d sending telnet cmd: %s"
1187: ,cfg.node_num
1188: ,telnet_cmd_desc(cmd));
1189: sprintf(buf,"%c%c",TELNET_IAC,cmd);
1190: putcom(buf,2);
1191: } else {
1192: if(startup->options&BBS_OPT_DEBUG_TELNET)
1193: lprintf(LOG_DEBUG,"Node %d sending telnet cmd: %s %s"
1194: ,cfg.node_num
1195: ,telnet_cmd_desc(cmd)
1196: ,telnet_opt_desc(opt));
1197: sprintf(buf,"%c%c%c",TELNET_IAC,cmd,opt);
1198: putcom(buf,3);
1199: }
1200: }
1201:
1202: void sbbs_t::request_telnet_opt(uchar cmd, uchar opt)
1203: {
1204: if(cmd==TELNET_DO || cmd==TELNET_DONT) { /* remote option */
1205: if(telnet_remote_option[opt]==telnet_opt_ack(cmd))
1206: return; /* already set in this mode, do nothing */
1207: telnet_remote_option[opt]=telnet_opt_ack(cmd);
1208: } else { /* local option */
1209: if(telnet_local_option[opt]==telnet_opt_ack(cmd))
1210: return; /* already set in this mode, do nothing */
1211: telnet_local_option[opt]=telnet_opt_ack(cmd);
1212: }
1213: send_telnet_cmd(cmd,opt);
1214: }
1215:
1216: void input_thread(void *arg)
1217: {
1218: BYTE inbuf[4000];
1219: BYTE telbuf[sizeof(inbuf)];
1220: BYTE *wrbuf;
1221: int i,rd,wr,avail;
1222: ulong total_recv=0;
1223: ulong total_pkts=0;
1224: fd_set socket_set;
1225: sbbs_t* sbbs = (sbbs_t*) arg;
1226: struct timeval tv;
1227: SOCKET high_socket;
1228: SOCKET sock;
1229:
1230: thread_up(TRUE /* setuid */);
1231:
1232: #ifdef _DEBUG
1233: lprintf(LOG_DEBUG,"Node %d input thread started",sbbs->cfg.node_num);
1234: #endif
1235:
1236: pthread_mutex_init(&sbbs->input_thread_mutex,NULL);
1237: sbbs->input_thread_running = true;
1238: sbbs->console|=CON_R_INPUT;
1239:
1240: while(sbbs->online && sbbs->client_socket!=INVALID_SOCKET
1241: && node_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET) {
1242:
1243: if(pthread_mutex_lock(&sbbs->input_thread_mutex)!=0)
1244: sbbs->errormsg(WHERE,ERR_LOCK,"input_thread_mutex",0);
1245:
1246: FD_ZERO(&socket_set);
1247: FD_SET(sbbs->client_socket,&socket_set);
1248: high_socket=sbbs->client_socket;
1249: #ifdef __unix__
1250: if(uspy_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET) {
1251: FD_SET(uspy_socket[sbbs->cfg.node_num-1],&socket_set);
1252: if(uspy_socket[sbbs->cfg.node_num-1] > high_socket)
1253: high_socket=uspy_socket[sbbs->cfg.node_num-1];
1254: }
1255: #endif
1256:
1257: tv.tv_sec=1;
1258: tv.tv_usec=0;
1259:
1260: if((i=select(high_socket+1,&socket_set,NULL,NULL,&tv))<1) {
1261: if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
1262: sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
1263: if(i==0) {
1264: YIELD(); /* This kludge is necessary on some Linux distros */
1265: continue; /* to allow other threads to lock the input_thread_mutex */
1266: }
1267:
1268: if(sbbs->client_socket==INVALID_SOCKET)
1269: break;
1270: #ifdef __unix__
1271: if(uspy_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET) {
1272: if(!socket_check(uspy_socket[sbbs->cfg.node_num-1],NULL,NULL,0)) {
1273: close_socket(uspy_socket[sbbs->cfg.node_num-1]);
1274: lprintf(LOG_NOTICE,"Closing local spy socket: %d",uspy_socket[sbbs->cfg.node_num-1]);
1275: uspy_socket[sbbs->cfg.node_num-1]=INVALID_SOCKET;
1276: continue;
1277: }
1278: }
1279: #endif
1280: if(ERROR_VALUE == ENOTSOCK)
1281: lprintf(LOG_NOTICE,"Node %d socket closed by peer on input->select", sbbs->cfg.node_num);
1282: else if(ERROR_VALUE==ESHUTDOWN)
1283: lprintf(LOG_NOTICE,"Node %d socket shutdown on input->select", sbbs->cfg.node_num);
1284: else if(ERROR_VALUE==EINTR)
1285: lprintf(LOG_DEBUG,"Node %d input thread interrupted",sbbs->cfg.node_num);
1286: else if(ERROR_VALUE==ECONNRESET)
1287: lprintf(LOG_NOTICE,"Node %d connection reset by peer on input->select", sbbs->cfg.node_num);
1288: else if(ERROR_VALUE==ECONNABORTED)
1289: lprintf(LOG_NOTICE,"Node %d connection aborted by peer on input->select", sbbs->cfg.node_num);
1290: else
1291: lprintf(LOG_WARNING,"Node %d !ERROR %d input->select socket %d"
1292: ,sbbs->cfg.node_num, ERROR_VALUE, sbbs->client_socket);
1293: break;
1294: }
1295:
1296: if(sbbs->client_socket==INVALID_SOCKET) {
1297: if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
1298: sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
1299: break;
1300: }
1301:
1302: /* ^ ^
1303: * \______ ______/
1304: * \ * \ / * /
1305: * ----- ------ /----\
1306: * || -< Boo! |
1307: * /__\ \----/
1308: * \______________/
1309: * \/\/\/\/\/\/\/
1310: * ------------
1311: */
1312:
1313: if(FD_ISSET(sbbs->client_socket,&socket_set))
1314: sock=sbbs->client_socket;
1315: #ifdef __unix__
1316: else if(uspy_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET
1317: && FD_ISSET(uspy_socket[sbbs->cfg.node_num-1],&socket_set)) {
1318: if(!socket_check(uspy_socket[sbbs->cfg.node_num-1],NULL,NULL,0)) {
1319: close_socket(uspy_socket[sbbs->cfg.node_num-1]);
1320: lprintf(LOG_NOTICE,"Closing local spy socket: %d",uspy_socket[sbbs->cfg.node_num-1]);
1321: uspy_socket[sbbs->cfg.node_num-1]=INVALID_SOCKET;
1322: if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
1323: sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
1324: continue;
1325: }
1326: sock=uspy_socket[sbbs->cfg.node_num-1];
1327: }
1328: #endif
1329: else {
1330: if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
1331: sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
1332: continue;
1333: }
1334:
1335: rd=RingBufFree(&sbbs->inbuf);
1336:
1337: if(!rd) { // input buffer full
1338: lprintf(LOG_WARNING,"Node %d !WARNING input buffer full", sbbs->cfg.node_num);
1339: // wait up to 5 seconds to empty (1 byte min)
1340: time_t start=time(NULL);
1341: while((rd=RingBufFree(&sbbs->inbuf))==0) {
1342: if(time(NULL)-start>=5) {
1343: rd=1;
1344: if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
1345: sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
1346: break;
1347: }
1348: YIELD();
1349: }
1350: }
1351:
1352: if(rd > (int)sizeof(inbuf))
1353: rd=sizeof(inbuf);
1354:
1355: #ifdef USE_CRYPTLIB
1356: if(sbbs->ssh_mode && sock==sbbs->client_socket) {
1357: if(!cryptStatusOK(cryptPopData(sbbs->ssh_session, (char*)inbuf, rd, &i)))
1358: rd=0;
1359: else {
1360: if(!i) {
1361: if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
1362: sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
1363: continue;
1364: }
1365: rd=i;
1366: }
1367: }
1368: else
1369: #endif
1370: rd = recv(sock, (char*)inbuf, rd, 0);
1371:
1372: if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
1373: sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
1374:
1375: if(rd == SOCKET_ERROR)
1376: {
1377: #ifdef __unix__
1378: if(sock==sbbs->client_socket) {
1379: #endif
1380: if(ERROR_VALUE == ENOTSOCK)
1381: lprintf(LOG_NOTICE,"Node %d socket closed by peer on receive", sbbs->cfg.node_num);
1382: else if(ERROR_VALUE==ECONNRESET)
1383: lprintf(LOG_NOTICE,"Node %d connection reset by peer on receive", sbbs->cfg.node_num);
1384: else if(ERROR_VALUE==ESHUTDOWN)
1385: lprintf(LOG_NOTICE,"Node %d socket shutdown on receive", sbbs->cfg.node_num);
1386: else if(ERROR_VALUE==ECONNABORTED)
1387: lprintf(LOG_NOTICE,"Node %d connection aborted by peer on receive", sbbs->cfg.node_num);
1388: else
1389: lprintf(LOG_WARNING,"Node %d !ERROR %d receiving from socket %d"
1390: ,sbbs->cfg.node_num, ERROR_VALUE, sock);
1391: break;
1392: #ifdef __unix__
1393: } else {
1394: if(ERROR_VALUE != EAGAIN) {
1395: lprintf(LOG_ERR,"Node %d !ERROR %d on local spy socket %d receive"
1396: , sbbs->cfg.node_num, errno, sock);
1397: close_socket(uspy_socket[sbbs->cfg.node_num-1]);
1398: uspy_socket[sbbs->cfg.node_num-1]=INVALID_SOCKET;
1399: }
1400: continue;
1401: }
1402: #endif
1403: }
1404:
1405: if(rd == 0 && sock==sbbs->client_socket)
1406: {
1407: lprintf(LOG_NOTICE,"Node %d disconnected", sbbs->cfg.node_num);
1408: break;
1409: }
1410:
1411: total_recv+=rd;
1412: total_pkts++;
1413:
1414: // telbuf and wr are modified to reflect telnet escaped data
1415: wr=rd;
1416: #ifdef __unix__
1417: if(sock!=sbbs->client_socket)
1418: wrbuf=inbuf;
1419: else
1420: #endif
1421: if(sbbs->telnet_mode&TELNET_MODE_OFF)
1422: wrbuf=inbuf;
1423: else
1424: wrbuf=telnet_interpret(sbbs, inbuf, rd, telbuf, wr);
1425: if(wr > (int)sizeof(telbuf))
1426: lprintf(LOG_ERR,"!TELBUF OVERFLOW (%d>%d)",wr,sizeof(telbuf));
1427:
1428: /* First level Ctrl-C checking */
1429: if(!(sbbs->cfg.ctrlkey_passthru&(1<<CTRL_C))
1430: && sbbs->rio_abortable
1431: && !(sbbs->telnet_mode&TELNET_MODE_GATE)
1432: && sbbs->telnet_remote_option[TELNET_BINARY_TX]!=TELNET_WILL
1433: && memchr(wrbuf, CTRL_C, wr)) {
1434: if(RingBufFull(&sbbs->inbuf))
1435: lprintf(LOG_DEBUG,"Node %d Ctrl-C hit with %lu bytes in input buffer"
1436: ,sbbs->cfg.node_num,RingBufFull(&sbbs->inbuf));
1437: if(RingBufFull(&sbbs->outbuf))
1438: lprintf(LOG_DEBUG,"Node %d Ctrl-C hit with %lu bytes in output buffer"
1439: ,sbbs->cfg.node_num,RingBufFull(&sbbs->outbuf));
1440: sbbs->sys_status|=SS_ABORT;
1441: RingBufReInit(&sbbs->inbuf); /* Purge input buffer */
1442: RingBufReInit(&sbbs->outbuf); /* Purge output buffer */
1443: sem_post(&sbbs->inbuf.sem);
1444: continue; // Ignore the entire buffer
1445: }
1446:
1447: avail=RingBufFree(&sbbs->inbuf);
1448:
1449: if(avail<wr)
1450: lprintf(LOG_ERR,"!INPUT BUFFER FULL (%d free)", avail);
1451: else
1452: RingBufWrite(&sbbs->inbuf, wrbuf, wr);
1453: // if(wr>100)
1454: // mswait(500); // Throttle sender
1455: }
1456: sbbs->online=FALSE;
1457: sbbs->sys_status|=SS_ABORT; /* as though Ctrl-C were hit */
1458:
1459: sbbs->input_thread_running = false;
1460: if(node_socket[sbbs->cfg.node_num-1]==INVALID_SOCKET) // Shutdown locally
1461: sbbs->terminated = true; // Signal JS to stop execution
1462:
1463: while(pthread_mutex_destroy(&sbbs->input_thread_mutex)==EBUSY)
1464: mswait(1);
1465:
1466: thread_down();
1467: lprintf(LOG_DEBUG,"Node %d input thread terminated (received %lu bytes in %lu blocks)"
1468: ,sbbs->cfg.node_num, total_recv, total_pkts);
1469: }
1470:
1471: #ifdef USE_CRYPTLIB
1472: /*
1473: * This thread copies anything recieved from the client to the passthru_socket
1474: * It can only do that when the input thread is locked.
1475: * Luckily, the input thread is currently locked exactly when we want it to be.
1476: * Since the passthru socket is 8-bit clean and does NOT use a protocol,
1477: * we must handle telnet stuff HERE.
1478: * However, for JS stuff, direct operations on client_socket should generally
1479: * be done to the passthru_socket instead... THIS is the biggest problem here.
1480: */
1481: void passthru_output_thread(void* arg)
1482: {
1483: fd_set socket_set;
1484: sbbs_t *sbbs = (sbbs_t*) arg;
1485: struct timeval tv;
1486: int i;
1487: BYTE inbuf[4000];
1488: BYTE telbuf[sizeof(inbuf)];
1489: BYTE *wrbuf;
1490: int rd;
1491: int wr;
1492:
1493: thread_up(FALSE /* setuid */);
1494:
1495: sbbs->passthru_output_thread_running = true;
1496:
1497: while(sbbs->client_socket!=INVALID_SOCKET && sbbs->passthru_socket!=INVALID_SOCKET && !terminate_server) {
1498: if(!sbbs->input_thread_mutex_locked) {
1499: SLEEP(1);
1500: continue;
1501: }
1502:
1503: FD_ZERO(&socket_set);
1504: FD_SET(sbbs->client_socket,&socket_set);
1505:
1506: tv.tv_sec=1;
1507: tv.tv_usec=0;
1508:
1509: if((i=select(sbbs->client_socket+1,&socket_set,NULL,NULL,&tv))<1) {
1510: if(i==0) {
1511: YIELD(); /* This kludge is necessary on some Linux distros */
1512: continue; /* to allow other threads to lock the input_thread_mutex */
1513: }
1514:
1515: if(sbbs->client_socket==INVALID_SOCKET)
1516: break;
1517: if(ERROR_VALUE == ENOTSOCK)
1518: lprintf(LOG_NOTICE,"Node %d socket closed by peer on input->select", sbbs->cfg.node_num);
1519: else if(ERROR_VALUE==ESHUTDOWN)
1520: lprintf(LOG_NOTICE,"Node %d socket shutdown on input->select", sbbs->cfg.node_num);
1521: else if(ERROR_VALUE==EINTR)
1522: lprintf(LOG_DEBUG,"Node %d passthru output thread interrupted",sbbs->cfg.node_num);
1523: else if(ERROR_VALUE==ECONNRESET)
1524: lprintf(LOG_NOTICE,"Node %d connection reset by peer on input->select", sbbs->cfg.node_num);
1525: else if(ERROR_VALUE==ECONNABORTED)
1526: lprintf(LOG_NOTICE,"Node %d connection aborted by peer on input->select", sbbs->cfg.node_num);
1527: else
1528: lprintf(LOG_WARNING,"Node %d !ERROR %d ->select socket %d"
1529: ,sbbs->cfg.node_num, ERROR_VALUE, sbbs->client_socket);
1530: break;
1531: }
1532:
1533: if(sbbs->client_socket==INVALID_SOCKET)
1534: break;
1535:
1536: rd=sizeof(inbuf);
1537:
1538: #ifdef USE_CRYPTLIB
1539: if(sbbs->ssh_mode) {
1540: if(!cryptStatusOK(cryptPopData(sbbs->ssh_session, (char*)inbuf, rd, &i)))
1541: rd=0;
1542: else {
1543: if(!i)
1544: continue;
1545: rd=i;
1546: }
1547: }
1548: else
1549: #endif
1550: rd = recv(sbbs->client_socket, (char*)inbuf, rd, 0);
1551:
1552: if(rd == SOCKET_ERROR)
1553: {
1554: if(ERROR_VALUE == ENOTSOCK)
1555: lprintf(LOG_NOTICE,"Node %d socket closed by peer on receive", sbbs->cfg.node_num);
1556: else if(ERROR_VALUE==ECONNRESET)
1557: lprintf(LOG_NOTICE,"Node %d connection reset by peer on receive", sbbs->cfg.node_num);
1558: else if(ERROR_VALUE==ESHUTDOWN)
1559: lprintf(LOG_NOTICE,"Node %d socket shutdown on receive", sbbs->cfg.node_num);
1560: else if(ERROR_VALUE==ECONNABORTED)
1561: lprintf(LOG_NOTICE,"Node %d connection aborted by peer on receive", sbbs->cfg.node_num);
1562: else
1563: lprintf(LOG_WARNING,"Node %d !ERROR %d receiving from socket %d"
1564: ,sbbs->cfg.node_num, ERROR_VALUE, sbbs->client_socket);
1565: break;
1566: }
1567:
1568: if(rd == 0)
1569: {
1570: lprintf(LOG_NOTICE,"Node %d passthru input socket disconnected", sbbs->cfg.node_num);
1571: break;
1572: }
1573:
1574: // telbuf and wr are modified to reflect telnet escaped data
1575: wr=rd;
1576: if(sbbs->telnet_mode&TELNET_MODE_OFF)
1577: wrbuf=inbuf;
1578: else
1579: wrbuf=telnet_interpret(sbbs, inbuf, rd, telbuf, wr);
1580: if(wr > (int)sizeof(telbuf))
1581: lprintf(LOG_ERR,"!TELBUF OVERFLOW (%d>%d)",wr,sizeof(telbuf));
1582:
1583: /*
1584: * TODO: This should check for writability etc.
1585: */
1586: sendsocket(sbbs->passthru_socket, (char*)wrbuf, wr);
1587: }
1588:
1589: sbbs->passthru_output_thread_running = false;
1590: }
1591:
1592: /*
1593: * This thread simply copies anything it manages to read from the
1594: * passthru_socket into the output ringbuffer.
1595: */
1596: void passthru_input_thread(void* arg)
1597: {
1598: fd_set r_set;
1599: sbbs_t *sbbs = (sbbs_t*) arg;
1600: struct timeval tv;
1601: BYTE ch;
1602: int i;
1603:
1604: thread_up(FALSE /* setuid */);
1605:
1606: sbbs->passthru_input_thread_running = true;
1607:
1608: while(sbbs->passthru_socket!=INVALID_SOCKET && !terminate_server) {
1609: tv.tv_sec=1;
1610: tv.tv_usec=0;
1611:
1612: FD_ZERO(&r_set);
1613: FD_SET(sbbs->passthru_socket,&r_set);
1614: if((i=select(sbbs->passthru_socket+1,&r_set,NULL,NULL,&tv))<1) {
1615: if(i==0) {
1616: YIELD(); /* This kludge is necessary on some Linux distros */
1617: continue; /* to allow other threads to lock the input_thread_mutex */
1618: }
1619:
1620: if(sbbs->passthru_socket==INVALID_SOCKET)
1621: break;
1622: if(ERROR_VALUE == ENOTSOCK)
1623: lprintf(LOG_NOTICE,"Node %d socket closed by peer on passthru->select", sbbs->cfg.node_num);
1624: else if(ERROR_VALUE==ESHUTDOWN)
1625: lprintf(LOG_NOTICE,"Node %d socket shutdown on passthru->select", sbbs->cfg.node_num);
1626: else if(ERROR_VALUE==EINTR)
1627: lprintf(LOG_DEBUG,"Node %d passthru thread interrupted",sbbs->cfg.node_num);
1628: else if(ERROR_VALUE==ECONNRESET)
1629: lprintf(LOG_NOTICE,"Node %d connection reset by peer on passthru->select", sbbs->cfg.node_num);
1630: else if(ERROR_VALUE==ECONNABORTED)
1631: lprintf(LOG_NOTICE,"Node %d connection aborted by peer on passthru->select", sbbs->cfg.node_num);
1632: else
1633: lprintf(LOG_WARNING,"Node %d !ERROR %d passthru->select socket %d"
1634: ,sbbs->cfg.node_num, ERROR_VALUE, sbbs->passthru_socket);
1635: break;
1636: }
1637: if(!RingBufFree(&sbbs->outbuf))
1638: continue;
1639:
1640: i = recv(sbbs->passthru_socket, (char*)(&ch), 1, 0);
1641:
1642: if(i == SOCKET_ERROR)
1643: {
1644: if(ERROR_VALUE == ENOTSOCK)
1645: lprintf(LOG_NOTICE,"Node %d passthru socket closed by peer on receive", sbbs->cfg.node_num);
1646: else if(ERROR_VALUE==ECONNRESET)
1647: lprintf(LOG_NOTICE,"Node %d passthru connection reset by peer on receive", sbbs->cfg.node_num);
1648: else if(ERROR_VALUE==ESHUTDOWN)
1649: lprintf(LOG_NOTICE,"Node %d passthru socket shutdown on receive", sbbs->cfg.node_num);
1650: else if(ERROR_VALUE==ECONNABORTED)
1651: lprintf(LOG_NOTICE,"Node %d passthru connection aborted by peer on receive", sbbs->cfg.node_num);
1652: else
1653: lprintf(LOG_WARNING,"Node %d !ERROR %d receiving from passthru socket %d"
1654: ,sbbs->cfg.node_num, ERROR_VALUE, sbbs->passthru_socket);
1655: break;
1656: }
1657:
1658: if(i == 0)
1659: {
1660: lprintf(LOG_NOTICE,"Node %d passthru disconnected", sbbs->cfg.node_num);
1661: break;
1662: }
1663:
1664: if(!RingBufWrite(&sbbs->outbuf, &ch, 1)) {
1665: lprintf(LOG_ERR,"Cannot pass from passthru socket to outbuf");
1666: break;
1667: }
1668: }
1669: if(sbbs->passthru_socket!=INVALID_SOCKET) {
1670: close_socket(sbbs->passthru_socket);
1671: sbbs->passthru_socket=INVALID_SOCKET;
1672: }
1673: thread_down();
1674:
1675: sbbs->passthru_input_thread_running = false;
1676: }
1677: #endif
1678:
1679: void output_thread(void* arg)
1680: {
1681: char node[128];
1682: char stats[128];
1683: BYTE buf[IO_THREAD_BUF_SIZE];
1684: int i;
1685: ulong avail;
1686: ulong total_sent=0;
1687: ulong total_pkts=0;
1688: ulong short_sends=0;
1689: ulong bufbot=0;
1690: ulong buftop=0;
1691: sbbs_t* sbbs = (sbbs_t*) arg;
1692: fd_set socket_set;
1693: struct timeval tv;
1694: ulong mss=IO_THREAD_BUF_SIZE;
1695:
1696: thread_up(TRUE /* setuid */);
1697:
1698: if(sbbs->cfg.node_num)
1699: SAFEPRINTF(node,"Node %d",sbbs->cfg.node_num);
1700: else
1701: SAFECOPY(node,sbbs->client_name);
1702: #ifdef _DEBUG
1703: lprintf(LOG_DEBUG,"%s output thread started",node);
1704: #endif
1705:
1706: sbbs->output_thread_running = true;
1707: sbbs->console|=CON_R_ECHO;
1708:
1709: #ifdef TCP_MAXSEG
1710: /*
1711: * Auto-tune the highwater mark to be the negotiated MSS for the
1712: * socket (when possible)
1713: */
1714: if(!sbbs->outbuf.highwater_mark) {
1715: socklen_t sl;
1716: sl=sizeof(i);
1717: if(!getsockopt(sbbs->client_socket, IPPROTO_TCP, TCP_MAXSEG, &i, &sl)) {
1718: /* Check for sanity... */
1719: if(i>100) {
1720: sbbs->outbuf.highwater_mark=i;
1721: lprintf(LOG_DEBUG,"Autotuning outbuf highwater mark to %d based on MSS",i);
1722: mss=sbbs->outbuf.highwater_mark;
1723: if(mss>IO_THREAD_BUF_SIZE) {
1724: mss=IO_THREAD_BUF_SIZE;
1725: lprintf(LOG_DEBUG,"MSS (%d) is higher than IO_THREAD_BUF_SIZE (%d)",i,IO_THREAD_BUF_SIZE);
1726: }
1727: }
1728: }
1729: }
1730: #endif
1731:
1732: while(sbbs->client_socket!=INVALID_SOCKET && !terminate_server) {
1733: /*
1734: * I'd like to check the linear buffer against the highwater
1735: * at this point, but it would get too clumsy imho - Deuce
1736: *
1737: * Actually, another option would just be to have the size
1738: * of the linear buffer equal to the MSS... any larger and
1739: * you could have small sends off the end. this would
1740: * probobly be even clumbsier
1741: */
1742: if(bufbot == buftop) {
1743: /* Wait for something to output in the RingBuffer */
1744: if((avail=RingBufFull(&sbbs->outbuf))==0) { /* empty */
1745: if(sem_trywait_block(&sbbs->outbuf.sem,1000))
1746: continue;
1747: /* Check for spurious sem post... */
1748: if((avail=RingBufFull(&sbbs->outbuf))==0)
1749: continue;
1750: }
1751: else
1752: sem_trywait(&sbbs->outbuf.sem);
1753:
1754: /* Wait for full buffer or drain timeout */
1755: if(sbbs->outbuf.highwater_mark) {
1756: if(avail<sbbs->outbuf.highwater_mark) {
1757: sem_trywait_block(&sbbs->outbuf.highwater_sem,startup->outbuf_drain_timeout);
1758: /* We (potentially) blocked, so get fill level again */
1759: avail=RingBufFull(&sbbs->outbuf);
1760: } else
1761: sem_trywait(&sbbs->outbuf.highwater_sem);
1762: }
1763:
1764: /*
1765: * At this point, there's something to send and,
1766: * if the highwater mark is set, the timeout has
1767: * passed or we've hit highwater. Read ring buffer
1768: * into linear buffer.
1769: */
1770: if(avail>sizeof(buf)) {
1771: lprintf(LOG_WARNING,"!%s: Insufficient linear output buffer (%lu > %lu)"
1772: ,node, avail, sizeof(buf));
1773: avail=sizeof(buf);
1774: }
1775: /* If we know the MSS, use it as the max send() size. */
1776: if(avail>mss)
1777: avail=mss;
1778: buftop=RingBufRead(&sbbs->outbuf, buf, avail);
1779: bufbot=0;
1780: }
1781:
1782: /* Check socket for writability (using select) */
1783: tv.tv_sec=0;
1784: tv.tv_usec=1000;
1785:
1786: FD_ZERO(&socket_set);
1787: FD_SET(sbbs->client_socket,&socket_set);
1788:
1789: i=select(sbbs->client_socket+1,NULL,&socket_set,NULL,&tv);
1790: if(i==SOCKET_ERROR) {
1791: if(sbbs->client_socket!=INVALID_SOCKET)
1792: lprintf(LOG_ERR,"!%s: ERROR %d selecting socket %u for send"
1793: ,node,ERROR_VALUE,sbbs->client_socket);
1794: if(sbbs->cfg.node_num) /* Only break if node output (not server) */
1795: break;
1796: RingBufReInit(&sbbs->outbuf); /* Flush output buffer */
1797: continue;
1798: }
1799: if(i<1) {
1800: continue;
1801: }
1802:
1803: #ifdef USE_CRYPTLIB
1804: if(sbbs->ssh_mode) {
1805: if(!cryptStatusOK(cryptPushData(sbbs->ssh_session, (char*)buf+bufbot, buftop-bufbot, &i)))
1806: i=-1;
1807: else
1808: cryptFlushData(sbbs->ssh_session);
1809: }
1810: else
1811: #endif
1812: i=sendsocket(sbbs->client_socket, (char*)buf+bufbot, buftop-bufbot);
1813: if(i==SOCKET_ERROR) {
1814: if(ERROR_VALUE == ENOTSOCK)
1815: lprintf(LOG_NOTICE,"%s client socket closed on send", node);
1816: else if(ERROR_VALUE==ECONNRESET)
1817: lprintf(LOG_NOTICE,"%s connection reset by peer on send", node);
1818: else if(ERROR_VALUE==ECONNABORTED)
1819: lprintf(LOG_NOTICE,"%s connection aborted by peer on send", node);
1820: else
1821: lprintf(LOG_WARNING,"!%s: ERROR %d sending on socket %d"
1822: ,node, ERROR_VALUE, sbbs->client_socket);
1823: sbbs->online=FALSE;
1824: /* was break; on 4/7/00 */
1825: i=buftop-bufbot; // Pretend we sent it all
1826: }
1827:
1828: if(sbbs->cfg.node_num>0 && !(sbbs->sys_status&SS_FILEXFER)) {
1829: /* Spy on the user locally */
1830: if(startup->node_spybuf!=NULL
1831: && startup->node_spybuf[sbbs->cfg.node_num-1]!=NULL) {
1832: RingBufWrite(startup->node_spybuf[sbbs->cfg.node_num-1],buf+bufbot,i);
1833: /* Signal spy output semaphore? */
1834: if(startup->node_spysem!=NULL
1835: && startup->node_spysem[sbbs->cfg.node_num-1]!=NULL)
1836: sem_post(startup->node_spysem[sbbs->cfg.node_num-1]);
1837: }
1838: /* Spy on the user remotely */
1839: if(spy_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET)
1840: sendsocket(spy_socket[sbbs->cfg.node_num-1],(char*)buf+bufbot,i);
1841: #ifdef __unix__
1842: if(uspy_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET)
1843: sendsocket(uspy_socket[sbbs->cfg.node_num-1],(char*)buf+bufbot,i);
1844: #endif
1845: }
1846:
1847: if(i!=(int)(buftop-bufbot)) {
1848: lprintf(LOG_WARNING,"!%s: Short socket send (%u instead of %u)"
1849: ,node, i ,buftop-bufbot);
1850: short_sends++;
1851: }
1852: bufbot+=i;
1853: total_sent+=i;
1854: total_pkts++;
1855: }
1856:
1857: sbbs->spymsg("Disconnected");
1858:
1859: sbbs->output_thread_running = false;
1860:
1861: if(total_sent)
1862: sprintf(stats,"(sent %lu bytes in %lu blocks, %lu average, %lu short)"
1863: ,total_sent, total_pkts, total_sent/total_pkts, short_sends);
1864: else
1865: stats[0]=0;
1866:
1867: thread_down();
1868: lprintf(LOG_DEBUG,"%s output thread terminated %s", node, stats);
1869: }
1870:
1871: void event_thread(void* arg)
1872: {
1873: ulong stack_frame;
1874: char str[MAX_PATH+1];
1875: char bat_list[MAX_PATH+1];
1876: char semfile[MAX_PATH+1];
1877: int i,j,k;
1878: int file;
1879: int offset;
1880: bool check_semaphores;
1881: bool packed_rep;
1882: ulong l;
1883: time_t now;
1884: time_t start;
1885: time_t lastsemchk=0;
1886: time_t lastnodechk=0;
1887: time_t lastprepack=0;
1888: node_t node;
1889: glob_t g;
1890: sbbs_t* sbbs = (sbbs_t*) arg;
1891: struct tm now_tm;
1892: struct tm tm;
1893:
1894: eprintf(LOG_DEBUG,"BBS Events thread started");
1895:
1896: sbbs->event_thread_running = true;
1897:
1898: sbbs_srand(); /* Seed random number generator */
1899:
1900: thread_up(TRUE /* setuid */);
1901:
1902: #ifdef JAVASCRIPT
1903: if(!(startup->options&BBS_OPT_NO_JAVASCRIPT)) {
1904: if(!sbbs->js_init(&stack_frame)) /* This must be done in the context of the event thread */
1905: lprintf(LOG_ERR,"!JavaScript Initialization FAILURE");
1906: }
1907: #endif
1908:
1909: // Read TIME.DAB
1910: SAFEPRINTF(str,"%stime.dab",sbbs->cfg.ctrl_dir);
1911: if((file=sbbs->nopen(str,O_RDWR|O_CREAT))==-1)
1912: sbbs->errormsg(WHERE,ERR_OPEN,str,0);
1913: else {
1914: for(i=0;i<sbbs->cfg.total_events;i++) {
1915: sbbs->cfg.event[i]->last=0;
1916: if(filelength(file)<(long)(sizeof(time_t)*(i+1))) {
1917: eprintf(LOG_WARNING,"Initializing last run time for event: %s"
1918: ,sbbs->cfg.event[i]->code);
1919: write(file,&sbbs->cfg.event[i]->last,sizeof(time_t));
1920: } else {
1921: if(read(file,&sbbs->cfg.event[i]->last,sizeof(time_t))!=sizeof(time_t))
1922: sbbs->errormsg(WHERE,ERR_READ,str,sizeof(time_t));
1923: }
1924: /* Event always runs after initialization? */
1925: if(sbbs->cfg.event[i]->misc&EVENT_INIT)
1926: sbbs->cfg.event[i]->last=-1;
1927: }
1928: lastprepack=0;
1929: read(file,&lastprepack,sizeof(time_t)); /* expected to fail first time */
1930: close(file);
1931: }
1932:
1933: // Read QNET.DAB
1934: SAFEPRINTF(str,"%sqnet.dab",sbbs->cfg.ctrl_dir);
1935: if((file=sbbs->nopen(str,O_RDWR|O_CREAT))==-1)
1936: sbbs->errormsg(WHERE,ERR_OPEN,str,0);
1937: else {
1938: for(i=0;i<sbbs->cfg.total_qhubs;i++) {
1939: sbbs->cfg.qhub[i]->last=0;
1940: if(filelength(file)<(long)(sizeof(time_t)*(i+1))) {
1941: eprintf(LOG_WARNING,"Initializing last call-out time for QWKnet hub: %s"
1942: ,sbbs->cfg.qhub[i]->id);
1943: write(file,&sbbs->cfg.qhub[i]->last,sizeof(time_t));
1944: } else {
1945: if(read(file,&sbbs->cfg.qhub[i]->last,sizeof(time_t))!=sizeof(time_t))
1946: sbbs->errormsg(WHERE,ERR_READ,str,sizeof(time_t));
1947: }
1948: }
1949: close(file);
1950: }
1951:
1952: // Read PNET.DAB
1953: SAFEPRINTF(str,"%spnet.dab",sbbs->cfg.ctrl_dir);
1954: if((file=sbbs->nopen(str,O_RDWR|O_CREAT))==-1)
1955: sbbs->errormsg(WHERE,ERR_OPEN,str,0);
1956: else {
1957: for(i=0;i<sbbs->cfg.total_phubs;i++) {
1958: sbbs->cfg.phub[i]->last=0;
1959: if(filelength(file)<(long)(sizeof(time_t)*(i+1)))
1960: write(file,&sbbs->cfg.phub[i]->last,sizeof(time_t));
1961: else
1962: read(file,&sbbs->cfg.phub[i]->last,sizeof(time_t));
1963: }
1964: close(file);
1965: }
1966:
1967: while(!sbbs->terminated && !terminate_server) {
1968:
1969: if(startup->options&BBS_OPT_NO_EVENTS) {
1970: SLEEP(1000);
1971: continue;
1972: }
1973:
1974: now=time(NULL);
1975: localtime_r(&now,&now_tm);
1976:
1977: if(now-lastsemchk>=sbbs->cfg.node_sem_check) {
1978: check_semaphores=true;
1979: lastsemchk=now;
1980: } else
1981: check_semaphores=false;
1982:
1983: sbbs->online=FALSE; /* reset this from ON_LOCAL */
1984:
1985: /* QWK events */
1986: if(check_semaphores && !(startup->options&BBS_OPT_NO_QWK_EVENTS)) {
1987: /* Import any REP files that have magically appeared (via FTP perhaps) */
1988: SAFEPRINTF(str,"%sfile/",sbbs->cfg.data_dir);
1989: offset=strlen(str);
1990: strcat(str,"*.rep");
1991: glob(str,0,NULL,&g);
1992: for(i=0;i<(int)g.gl_pathc;i++) {
1993: sbbs->useron.number=atoi(g.gl_pathv[i]+offset);
1994: getuserdat(&sbbs->cfg,&sbbs->useron);
1995: if(sbbs->useron.number && flength(g.gl_pathv[i])>0) {
1996: SAFEPRINTF(semfile,"%s.lock",g.gl_pathv[i]);
1997: if(!fmutex(semfile,startup->host_name,24*60*60))
1998: continue;
1999: sbbs->online=ON_LOCAL;
2000: eprintf(LOG_INFO,"Un-packing QWK Reply packet from %s",sbbs->useron.alias);
2001: sbbs->getusrsubs();
2002: sbbs->unpack_rep(g.gl_pathv[i]);
2003: sbbs->batch_create_list(); /* FREQs? */
2004: sbbs->batdn_total=0;
2005:
2006: /* putuserdat? */
2007: remove(g.gl_pathv[i]);
2008: remove(semfile);
2009: }
2010: }
2011: globfree(&g);
2012:
2013: /* Create any QWK files that have magically appeared (via FTP perhaps) */
2014: SAFEPRINTF(str,"%spack*.now",sbbs->cfg.data_dir);
2015: offset=strlen(sbbs->cfg.data_dir)+4;
2016: glob(str,0,NULL,&g);
2017: for(i=0;i<(int)g.gl_pathc;i++) {
2018: eprintf(LOG_DEBUG,"QWK pack semaphore signaled: %s", g.gl_pathv[i]);
2019: sbbs->useron.number=atoi(g.gl_pathv[i]+offset);
2020: SAFEPRINTF2(semfile,"%spack%04u.lock",sbbs->cfg.data_dir,sbbs->useron.number);
2021: if(!fmutex(semfile,startup->host_name,24*60*60)) {
2022: eprintf(LOG_WARNING,"%s exists (already being packed?)", semfile);
2023: continue;
2024: }
2025: getuserdat(&sbbs->cfg,&sbbs->useron);
2026: if(sbbs->useron.number && !(sbbs->useron.misc&(DELETED|INACTIVE))) {
2027: eprintf(LOG_INFO,"Packing QWK Message Packet for %s",sbbs->useron.alias);
2028: sbbs->online=ON_LOCAL;
2029: delfiles(sbbs->cfg.temp_dir,ALLFILES);
2030: sbbs->getmsgptrs();
2031: sbbs->getusrsubs();
2032: sbbs->batdn_total=0;
2033:
2034: sbbs->last_ns_time=sbbs->ns_time=sbbs->useron.ns_time;
2035: SAFEPRINTF2(bat_list,"%sfile/%04u.dwn",sbbs->cfg.data_dir,sbbs->useron.number);
2036: sbbs->batch_add_list(bat_list);
2037:
2038: SAFEPRINTF3(str,"%sfile%c%04u.qwk"
2039: ,sbbs->cfg.data_dir,PATH_DELIM,sbbs->useron.number);
2040: if(sbbs->pack_qwk(str,&l,true /* pre-pack/off-line */)) {
2041: eprintf(LOG_INFO,"Packing completed");
2042: sbbs->qwk_success(l,0,1);
2043: sbbs->putmsgptrs();
2044: remove(bat_list);
2045: } else
2046: eprintf(LOG_INFO,"No packet created (no new messages)");
2047: delfiles(sbbs->cfg.temp_dir,ALLFILES);
2048: sbbs->online=FALSE;
2049: }
2050: remove(g.gl_pathv[i]);
2051: remove(semfile);
2052: }
2053: globfree(&g);
2054:
2055: /* Create (pre-pack) QWK files for users configured as such */
2056: SAFEPRINTF(semfile,"%sprepack.now",sbbs->cfg.data_dir);
2057: if(sbbs->cfg.preqwk_ar[0]
2058: && (fexistcase(semfile) || (now-lastprepack)/60>(60*24))) {
2059: j=lastuser(&sbbs->cfg);
2060: eprintf(LOG_INFO,"Pre-packing QWK Message packets...");
2061: for(i=1;i<=j;i++) {
2062:
2063: SAFEPRINTF2(str,"%5u of %-5u",i,j);
2064: //status(str);
2065: sbbs->useron.number=i;
2066: getuserdat(&sbbs->cfg,&sbbs->useron);
2067:
2068: if(sbbs->useron.number
2069: && !(sbbs->useron.misc&(DELETED|INACTIVE)) /* Pre-QWK */
2070: && sbbs->chk_ar(sbbs->cfg.preqwk_ar,&sbbs->useron)) {
2071: for(k=1;k<=sbbs->cfg.sys_nodes;k++) {
2072: if(sbbs->getnodedat(k,&node,0)!=0)
2073: continue;
2074: if((node.status==NODE_INUSE || node.status==NODE_QUIET
2075: || node.status==NODE_LOGON) && node.useron==i)
2076: break;
2077: }
2078: if(k<=sbbs->cfg.sys_nodes) /* Don't pre-pack with user online */
2079: continue;
2080: eprintf(LOG_INFO,"Pre-packing QWK for %s",sbbs->useron.alias);
2081: sbbs->online=ON_LOCAL;
2082: delfiles(sbbs->cfg.temp_dir,ALLFILES);
2083: sbbs->getmsgptrs();
2084: sbbs->getusrsubs();
2085: sbbs->batdn_total=0;
2086: SAFEPRINTF3(str,"%sfile%c%04u.qwk"
2087: ,sbbs->cfg.data_dir,PATH_DELIM,sbbs->useron.number);
2088: if(sbbs->pack_qwk(str,&l,true /* pre-pack */)) {
2089: sbbs->qwk_success(l,0,1);
2090: sbbs->putmsgptrs();
2091: }
2092: delfiles(sbbs->cfg.temp_dir,ALLFILES);
2093: sbbs->online=FALSE;
2094: }
2095: }
2096: lastprepack=now;
2097: SAFEPRINTF(str,"%stime.dab",sbbs->cfg.ctrl_dir);
2098: if((file=sbbs->nopen(str,O_WRONLY))==-1) {
2099: sbbs->errormsg(WHERE,ERR_OPEN,str,O_WRONLY);
2100: break;
2101: }
2102: lseek(file,(long)sbbs->cfg.total_events*4L,SEEK_SET);
2103: write(file,&lastprepack,sizeof(time_t));
2104: close(file);
2105:
2106: remove(semfile);
2107: //status(STATUS_WFC);
2108: }
2109: }
2110:
2111: if(check_semaphores) {
2112:
2113: /* Run daily maintenance? */
2114: sbbs->cfg.node_num=0;
2115: sbbs->logonstats();
2116: if(sbbs->sys_status&SS_DAILY)
2117: sbbs->daily_maint();
2118:
2119: /* Node Daily Events */
2120: for(i=first_node;i<=last_node;i++) {
2121: // Node Daily Event
2122: node.status=NODE_INVALID_STATUS;
2123: if(sbbs->getnodedat(i,&node,0)!=0)
2124: continue;
2125: if(node.misc&NODE_EVENT && node.status==NODE_WFC) {
2126: sbbs->getnodedat(i,&node,1);
2127: node.status=NODE_EVENT_RUNNING;
2128: sbbs->putnodedat(i,&node);
2129: if(sbbs->cfg.node_daily[0]) {
2130: sbbs->cfg.node_num=i;
2131: strcpy(sbbs->cfg.node_dir, sbbs->cfg.node_path[i-1]);
2132:
2133: eprintf(LOG_INFO,"Running node %d daily event",i);
2134: sbbs->online=ON_LOCAL;
2135: sbbs->logentry("!:","Run node daily event");
2136: sbbs->external(
2137: sbbs->cmdstr(sbbs->cfg.node_daily,nulstr,nulstr,NULL)
2138: ,EX_OFFLINE);
2139: }
2140: sbbs->getnodedat(i,&node,1);
2141: node.misc&=~NODE_EVENT;
2142: node.status=NODE_WFC;
2143: node.useron=0;
2144: sbbs->putnodedat(i,&node);
2145: }
2146: }
2147:
2148: /* QWK Networking Call-out sempahores */
2149: for(i=0;i<sbbs->cfg.total_qhubs;i++) {
2150: if(sbbs->cfg.qhub[i]->node<first_node
2151: || sbbs->cfg.qhub[i]->node>last_node)
2152: continue;
2153: if(sbbs->cfg.qhub[i]->last==-1) // already signaled
2154: continue;
2155: SAFEPRINTF2(str,"%sqnet/%s.now",sbbs->cfg.data_dir,sbbs->cfg.qhub[i]->id);
2156: if(fexistcase(str)) {
2157: strcpy(str,sbbs->cfg.qhub[i]->id);
2158: eprintf(LOG_INFO,"Semaphore signaled for QWK Network Hub: %s",strupr(str));
2159: sbbs->cfg.qhub[i]->last=-1;
2160: }
2161: }
2162:
2163: /* Timed Event sempahores */
2164: for(i=0;i<sbbs->cfg.total_events;i++) {
2165: if((sbbs->cfg.event[i]->node<first_node
2166: || sbbs->cfg.event[i]->node>last_node)
2167: && !(sbbs->cfg.event[i]->misc&EVENT_EXCL))
2168: continue; // ignore non-exclusive events for other instances
2169: if(sbbs->cfg.event[i]->misc&EVENT_DISABLED)
2170: continue;
2171: if(sbbs->cfg.event[i]->last==-1) // already signaled
2172: continue;
2173: SAFEPRINTF2(str,"%s%s.now",sbbs->cfg.data_dir,sbbs->cfg.event[i]->code);
2174: if(fexistcase(str)) {
2175: strcpy(str,sbbs->cfg.event[i]->code);
2176: eprintf(LOG_INFO,"Semaphore signaled for Timed Event: %s",strupr(str));
2177: sbbs->cfg.event[i]->last=-1;
2178: }
2179: }
2180: }
2181:
2182: /* QWK Networking Call-out Events */
2183: for(i=0;i<sbbs->cfg.total_qhubs;i++) {
2184: if(sbbs->cfg.qhub[i]->node<first_node ||
2185: sbbs->cfg.qhub[i]->node>last_node)
2186: continue;
2187:
2188: if(check_semaphores) {
2189: // See if any packets have come in
2190: for(j=0;j<101;j++) {
2191: SAFEPRINTF4(str,"%s%s.q%c%c",sbbs->cfg.data_dir,sbbs->cfg.qhub[i]->id
2192: ,j>10 ? ((j-1)/10)+'0' : 'w'
2193: ,j ? ((j-1)%10)+'0' : 'k');
2194: if(fexistcase(str) && flength(str)>0) { /* silently ignore 0-byte QWK packets */
2195: eprintf(LOG_DEBUG,"Inbound QWK Packet detected: %s", str);
2196: delfiles(sbbs->cfg.temp_dir,ALLFILES);
2197: sbbs->online=ON_LOCAL;
2198: sbbs->console|=CON_L_ECHO;
2199: if(sbbs->unpack_qwk(str,i)==false) {
2200: char newname[MAX_PATH+1];
2201: SAFEPRINTF2(newname,"%s.%lx.bad",str,(long)now);
2202: remove(newname);
2203: if(rename(str,newname)==0) {
2204: char logmsg[MAX_PATH*3];
2205: SAFEPRINTF2(logmsg,"%s renamed to %s",str,newname);
2206: sbbs->logline("Q!",logmsg);
2207: }
2208: }
2209: sbbs->console&=~CON_L_ECHO;
2210: sbbs->online=FALSE;
2211: remove(str);
2212: }
2213: }
2214: }
2215:
2216: /* Qnet call out based on time */
2217: if(localtime_r(&sbbs->cfg.qhub[i]->last,&tm)==NULL)
2218: memset(&tm,0,sizeof(tm));
2219: if((sbbs->cfg.qhub[i]->last==-1L /* or frequency */
2220: || ((sbbs->cfg.qhub[i]->freq
2221: && (now-sbbs->cfg.qhub[i]->last)/60>sbbs->cfg.qhub[i]->freq)
2222: || (sbbs->cfg.qhub[i]->time
2223: && (now_tm.tm_hour*60)+now_tm.tm_min>=sbbs->cfg.qhub[i]->time
2224: && (now_tm.tm_mday!=tm.tm_mday || now_tm.tm_mon!=tm.tm_mon)))
2225: && sbbs->cfg.qhub[i]->days&(1<<now_tm.tm_wday))) {
2226: SAFEPRINTF2(str,"%sqnet/%s.now"
2227: ,sbbs->cfg.data_dir,sbbs->cfg.qhub[i]->id);
2228: if(fexistcase(str))
2229: remove(str); /* Remove semaphore file */
2230: SAFEPRINTF2(str,"%sqnet/%s.ptr"
2231: ,sbbs->cfg.data_dir,sbbs->cfg.qhub[i]->id);
2232: file=sbbs->nopen(str,O_RDONLY);
2233: for(j=0;j<sbbs->cfg.qhub[i]->subs;j++) {
2234: sbbs->subscan[sbbs->cfg.qhub[i]->sub[j]].ptr=0;
2235: if(file!=-1) {
2236: lseek(file,sbbs->cfg.sub[sbbs->cfg.qhub[i]->sub[j]]->ptridx*sizeof(long),SEEK_SET);
2237: read(file,&sbbs->subscan[sbbs->cfg.qhub[i]->sub[j]].ptr,sizeof(long));
2238: }
2239: }
2240: if(file!=-1)
2241: close(file);
2242: sbbs->console|=CON_L_ECHO;
2243: packed_rep=sbbs->pack_rep(i);
2244: sbbs->console&=~CON_L_ECHO;
2245: if(packed_rep) {
2246: if((file=sbbs->nopen(str,O_WRONLY|O_CREAT))==-1)
2247: sbbs->errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_CREAT);
2248: else {
2249: for(j=l=0;j<sbbs->cfg.qhub[i]->subs;j++) {
2250: while(filelength(file)<
2251: sbbs->cfg.sub[sbbs->cfg.qhub[i]->sub[j]]->ptridx*4L)
2252: write(file,&l,4); /* initialize ptrs to null */
2253: lseek(file
2254: ,sbbs->cfg.sub[sbbs->cfg.qhub[i]->sub[j]]->ptridx*sizeof(long)
2255: ,SEEK_SET);
2256: write(file,&sbbs->subscan[sbbs->cfg.qhub[i]->sub[j]].ptr,sizeof(long));
2257: }
2258: close(file);
2259: }
2260: }
2261: delfiles(sbbs->cfg.temp_dir,ALLFILES);
2262:
2263: sbbs->cfg.qhub[i]->last=time(NULL);
2264: SAFEPRINTF(str,"%sqnet.dab",sbbs->cfg.ctrl_dir);
2265: if((file=sbbs->nopen(str,O_WRONLY))==-1) {
2266: sbbs->errormsg(WHERE,ERR_OPEN,str,O_WRONLY);
2267: break;
2268: }
2269: lseek(file,sizeof(time_t)*i,SEEK_SET);
2270: write(file,&sbbs->cfg.qhub[i]->last,sizeof(time_t));
2271: close(file);
2272:
2273: if(sbbs->cfg.qhub[i]->call[0]) {
2274: sbbs->cfg.node_num=sbbs->cfg.qhub[i]->node;
2275: if(sbbs->cfg.node_num<1)
2276: sbbs->cfg.node_num=1;
2277: strcpy(sbbs->cfg.node_dir, sbbs->cfg.node_path[sbbs->cfg.node_num-1]);
2278: eprintf(LOG_INFO,"QWK Network call-out: %s",sbbs->cfg.qhub[i]->id);
2279: sbbs->online=ON_LOCAL;
2280: sbbs->external(
2281: sbbs->cmdstr(sbbs->cfg.qhub[i]->call
2282: ,sbbs->cfg.qhub[i]->id,sbbs->cfg.qhub[i]->id,NULL)
2283: ,EX_OFFLINE|EX_SH); /* sh for Unix perl scripts */
2284: }
2285: }
2286: }
2287:
2288: /* PostLink Networking Call-out Events */
2289: for(i=0;i<sbbs->cfg.total_phubs;i++) {
2290: if(sbbs->cfg.phub[i]->node<first_node
2291: || sbbs->cfg.phub[i]->node>last_node)
2292: continue;
2293: /* PostLink call out based on time */
2294: if(localtime_r(&sbbs->cfg.phub[i]->last,&tm)==NULL)
2295: memset(&tm,0,sizeof(tm));
2296: if(sbbs->cfg.phub[i]->last==-1
2297: || (((sbbs->cfg.phub[i]->freq /* or frequency */
2298: && (now-sbbs->cfg.phub[i]->last)/60>sbbs->cfg.phub[i]->freq)
2299: || (sbbs->cfg.phub[i]->time
2300: && (now_tm.tm_hour*60)+now_tm.tm_min>=sbbs->cfg.phub[i]->time
2301: && (now_tm.tm_mday!=tm.tm_mday || now_tm.tm_mon!=tm.tm_mon)))
2302: && sbbs->cfg.phub[i]->days&(1<<now_tm.tm_wday))) {
2303:
2304: sbbs->cfg.phub[i]->last=time(NULL);
2305: SAFEPRINTF(str,"%spnet.dab",sbbs->cfg.ctrl_dir);
2306: if((file=sbbs->nopen(str,O_WRONLY))==-1) {
2307: sbbs->errormsg(WHERE,ERR_OPEN,str,O_WRONLY);
2308: break;
2309: }
2310: lseek(file,sizeof(time_t)*i,SEEK_SET);
2311: write(file,&sbbs->cfg.phub[i]->last,sizeof(time_t));
2312: close(file);
2313:
2314: if(sbbs->cfg.phub[i]->call[0]) {
2315: sbbs->cfg.node_num=sbbs->cfg.phub[i]->node;
2316: if(sbbs->cfg.node_num<1)
2317: sbbs->cfg.node_num=1;
2318: strcpy(sbbs->cfg.node_dir, sbbs->cfg.node_path[sbbs->cfg.node_num-1]);
2319: eprintf(LOG_INFO,"PostLink Network call-out: %s",sbbs->cfg.phub[i]->name);
2320: sbbs->online=ON_LOCAL;
2321: sbbs->external(
2322: sbbs->cmdstr(sbbs->cfg.phub[i]->call,nulstr,nulstr,NULL)
2323: ,EX_OFFLINE|EX_SH); /* sh for Unix perl scripts */
2324: }
2325: }
2326: }
2327:
2328: /* Timed Events */
2329: for(i=0;i<sbbs->cfg.total_events;i++) {
2330: if(!sbbs->cfg.event[i]->node
2331: || sbbs->cfg.event[i]->node>sbbs->cfg.sys_nodes)
2332: continue; // ignore events for invalid nodes
2333:
2334: if(sbbs->cfg.event[i]->misc&EVENT_DISABLED)
2335: continue;
2336:
2337: if((sbbs->cfg.event[i]->node<first_node
2338: || sbbs->cfg.event[i]->node>last_node)
2339: && !(sbbs->cfg.event[i]->misc&EVENT_EXCL))
2340: continue; // ignore non-exclusive events for other instances
2341:
2342: if(localtime_r(&sbbs->cfg.event[i]->last,&tm)==NULL)
2343: memset(&tm,0,sizeof(tm));
2344: if(sbbs->cfg.event[i]->last==-1 ||
2345: (((sbbs->cfg.event[i]->freq
2346: && (now-sbbs->cfg.event[i]->last)/60>sbbs->cfg.event[i]->freq)
2347: || (!sbbs->cfg.event[i]->freq
2348: && (now_tm.tm_hour*60)+now_tm.tm_min>=sbbs->cfg.event[i]->time
2349: && (now_tm.tm_mday!=tm.tm_mday || now_tm.tm_mon!=tm.tm_mon)))
2350: && sbbs->cfg.event[i]->days&(1<<now_tm.tm_wday)
2351: && (sbbs->cfg.event[i]->mdays==0
2352: || sbbs->cfg.event[i]->mdays&(1<<now_tm.tm_mday))))
2353: {
2354: if(sbbs->cfg.event[i]->misc&EVENT_EXCL) { /* exclusive event */
2355:
2356: if(sbbs->cfg.event[i]->node<first_node
2357: || sbbs->cfg.event[i]->node>last_node) {
2358: eprintf(LOG_INFO,"Waiting for node %d to run timed event: %s"
2359: ,sbbs->cfg.event[i]->node,sbbs->cfg.event[i]->code);
2360: eprintf(LOG_DEBUG,"%s event last run: %s (0x%08lx)"
2361: ,sbbs->cfg.event[i]->code
2362: ,timestr(&sbbs->cfg, &sbbs->cfg.event[i]->last, str)
2363: ,sbbs->cfg.event[i]->last);
2364: lastnodechk=0; /* really last event time check */
2365: start=time(NULL);
2366: while(!sbbs->terminated) {
2367: mswait(1000);
2368: now=time(NULL);
2369: if(now-start>10 && now-lastnodechk<10)
2370: continue;
2371: for(j=first_node;j<=last_node;j++) {
2372: if(sbbs->getnodedat(j,&node,1)!=0)
2373: continue;
2374: if(node.status==NODE_WFC)
2375: node.status=NODE_EVENT_LIMBO;
2376: node.aux=sbbs->cfg.event[i]->node;
2377: sbbs->putnodedat(j,&node);
2378: }
2379:
2380: lastnodechk=now;
2381: SAFEPRINTF(str,"%stime.dab",sbbs->cfg.ctrl_dir);
2382: if((file=sbbs->nopen(str,O_RDONLY))==-1) {
2383: sbbs->errormsg(WHERE,ERR_OPEN,str,O_RDONLY);
2384: sbbs->cfg.event[i]->last=now;
2385: continue;
2386: }
2387: lseek(file,(long)i*4L,SEEK_SET);
2388: read(file,&sbbs->cfg.event[i]->last,sizeof(time_t));
2389: close(file);
2390: if(now-sbbs->cfg.event[i]->last<(60*60)) /* event is done */
2391: break;
2392: if(now-start>(90*60)) {
2393: eprintf(LOG_WARNING,"!TIMEOUT waiting for event to complete");
2394: break;
2395: }
2396: }
2397: SAFEPRINTF2(str,"%s%s.now",sbbs->cfg.data_dir,sbbs->cfg.event[i]->code);
2398: if(fexistcase(str))
2399: remove(str);
2400: sbbs->cfg.event[i]->last=now;
2401: } else { // Exclusive event to run on a node under our control
2402: eprintf(LOG_INFO,"Waiting for all nodes to become inactive before "
2403: "running timed event: %s",sbbs->cfg.event[i]->code);
2404: lastnodechk=0;
2405: start=time(NULL);
2406: while(!sbbs->terminated) {
2407: mswait(1000);
2408: now=time(NULL);
2409: if(now-start>10 && now-lastnodechk<10)
2410: continue;
2411: lastnodechk=now;
2412: // Check/change the status of the nodes that we're in control of
2413: for(j=first_node;j<=last_node;j++) {
2414: if(sbbs->getnodedat(j,&node,1)!=0)
2415: continue;
2416: if(node.status==NODE_WFC) {
2417: if(j==sbbs->cfg.event[i]->node)
2418: node.status=NODE_EVENT_WAITING;
2419: else
2420: node.status=NODE_EVENT_LIMBO;
2421: node.aux=sbbs->cfg.event[i]->node;
2422: }
2423: sbbs->putnodedat(j,&node);
2424: }
2425:
2426: for(j=1;j<=sbbs->cfg.sys_nodes;j++) {
2427: if(sbbs->getnodedat(j,&node,0)!=0)
2428: continue;
2429: if(j==sbbs->cfg.event[i]->node) {
2430: if(node.status!=NODE_EVENT_WAITING)
2431: break;
2432: } else {
2433: if(node.status!=NODE_OFFLINE
2434: && node.status!=NODE_EVENT_LIMBO)
2435: break;
2436: }
2437: }
2438: if(j>sbbs->cfg.sys_nodes) /* all nodes either offline or in limbo */
2439: break;
2440: eprintf(LOG_DEBUG,"Waiting for node %d (status=%d)",j,node.status);
2441: if(now-start>(90*60)) {
2442: eprintf(LOG_WARNING,"!TIMEOUT waiting for node %d to become inactive",j);
2443: break;
2444: }
2445: }
2446: }
2447: }
2448: #if 0 // removed Jun-23-2002
2449: else { /* non-exclusive */
2450: sbbs->getnodedat(sbbs->cfg.event[i]->node,&node,0);
2451: if(node.status!=NODE_WFC)
2452: continue;
2453: }
2454: #endif
2455: if(sbbs->cfg.event[i]->node<first_node
2456: || sbbs->cfg.event[i]->node>last_node) {
2457: eprintf(LOG_NOTICE,"Changing node status for nodes %d through %d to WFC"
2458: ,first_node,last_node);
2459: sbbs->cfg.event[i]->last=now;
2460: for(j=first_node;j<=last_node;j++) {
2461: node.status=NODE_INVALID_STATUS;
2462: if(sbbs->getnodedat(j,&node,1)!=0)
2463: continue;
2464: node.status=NODE_WFC;
2465: sbbs->putnodedat(j,&node);
2466: }
2467: }
2468: else {
2469: sbbs->cfg.node_num=sbbs->cfg.event[i]->node;
2470: if(sbbs->cfg.node_num<1)
2471: sbbs->cfg.node_num=1;
2472: strcpy(sbbs->cfg.node_dir, sbbs->cfg.node_path[sbbs->cfg.node_num-1]);
2473:
2474: SAFEPRINTF2(str,"%s%s.now",sbbs->cfg.data_dir,sbbs->cfg.event[i]->code);
2475: if(fexistcase(str))
2476: remove(str);
2477: if(sbbs->cfg.event[i]->misc&EVENT_EXCL) {
2478: sbbs->getnodedat(sbbs->cfg.event[i]->node,&node,1);
2479: node.status=NODE_EVENT_RUNNING;
2480: sbbs->putnodedat(sbbs->cfg.event[i]->node,&node);
2481: }
2482: strcpy(str,sbbs->cfg.event[i]->code);
2483: eprintf(LOG_INFO,"Running timed event: %s",strupr(str));
2484: int ex_mode = EX_OFFLINE;
2485: if(!(sbbs->cfg.event[i]->misc&EVENT_EXCL)
2486: && sbbs->cfg.event[i]->misc&EX_BG)
2487: ex_mode |= EX_BG;
2488: if(sbbs->cfg.event[i]->misc&XTRN_SH)
2489: ex_mode |= EX_SH;
2490: ex_mode|=(sbbs->cfg.event[i]->misc&EX_NATIVE);
2491: sbbs->online=ON_LOCAL;
2492: sbbs->external(
2493: sbbs->cmdstr(sbbs->cfg.event[i]->cmd,nulstr,sbbs->cfg.event[i]->dir,NULL)
2494: ,ex_mode
2495: ,sbbs->cfg.event[i]->dir);
2496: sbbs->cfg.event[i]->last=time(NULL);
2497: SAFEPRINTF(str,"%stime.dab",sbbs->cfg.ctrl_dir);
2498: if((file=sbbs->nopen(str,O_WRONLY))==-1) {
2499: sbbs->errormsg(WHERE,ERR_OPEN,str,O_WRONLY);
2500: break;
2501: }
2502: lseek(file,(long)i*4L,SEEK_SET);
2503: write(file,&sbbs->cfg.event[i]->last,sizeof(time_t));
2504: close(file);
2505:
2506: if(sbbs->cfg.event[i]->misc&EVENT_EXCL) { /* exclusive event */
2507: // Check/change the status of the nodes that we're in control of
2508: for(j=first_node;j<=last_node;j++) {
2509: node.status=NODE_INVALID_STATUS;
2510: if(sbbs->getnodedat(j,&node,1)!=0)
2511: continue;
2512: node.status=NODE_WFC;
2513: sbbs->putnodedat(j,&node);
2514: }
2515: }
2516: }
2517: }
2518: }
2519: mswait(1000);
2520: }
2521: sbbs->cfg.node_num=0;
2522: sbbs->event_thread_running = false;
2523:
2524: thread_down();
2525: eprintf(LOG_DEBUG,"BBS Event thread terminated (%u threads remain)", thread_count);
2526: }
2527:
2528:
2529: //****************************************************************************
2530: sbbs_t::sbbs_t(ushort node_num, DWORD addr, char* name, SOCKET sd,
2531: scfg_t* global_cfg, char* global_text[], client_t* client_info)
2532: {
2533: char nodestr[32];
2534: char path[MAX_PATH+1];
2535: uint i;
2536:
2537: if(node_num)
2538: SAFEPRINTF(nodestr,"Node %d",node_num);
2539: else
2540: strcpy(nodestr,name);
2541:
2542: lprintf(LOG_DEBUG,"%s constructor using socket %d (settings=%lx)"
2543: ,nodestr, sd, global_cfg->node_misc);
2544:
2545: startup = ::startup; // Convert from global to class member
2546:
2547: memcpy(&cfg, global_cfg, sizeof(cfg));
2548:
2549: cfg.node_num=node_num;
2550: if(node_num>0) {
2551: strcpy(cfg.node_dir, cfg.node_path[node_num-1]);
2552: prep_dir(cfg.node_dir, cfg.temp_dir, sizeof(cfg.temp_dir));
2553: } else { /* event thread needs exclusive-use temp_dir */
2554: if(startup->temp_dir[0])
2555: SAFECOPY(cfg.temp_dir,startup->temp_dir);
2556: else
2557: SAFECOPY(cfg.temp_dir,"../temp");
2558: prep_dir(cfg.ctrl_dir, cfg.temp_dir, sizeof(cfg.temp_dir));
2559: md(cfg.temp_dir);
2560: if(sd==INVALID_SOCKET) { /* events thread */
2561: if(startup->first_node==1)
2562: SAFEPRINTF(path,"%sevent",cfg.temp_dir);
2563: else
2564: SAFEPRINTF2(path,"%sevent%u",cfg.temp_dir,startup->first_node);
2565: backslash(path);
2566: SAFECOPY(cfg.temp_dir,path);
2567: }
2568: }
2569: lprintf(LOG_DEBUG,"%s temporary file directory: %s", nodestr, cfg.temp_dir);
2570:
2571: terminated = false;
2572: event_thread_running = false;
2573: input_thread_running = false;
2574: output_thread_running = false;
2575: input_thread_mutex_locked = false;
2576:
2577: if(client_info==NULL)
2578: memset(&client,0,sizeof(client));
2579: else
2580: memcpy(&client,client_info,sizeof(client));
2581: client_addr = addr;
2582: client_socket = sd;
2583: SAFECOPY(client_name, name);
2584: client_socket_dup=INVALID_SOCKET;
2585: client_ident[0]=0;
2586:
2587: terminal[0]=0;
2588: rlogin_name[0]=0;
2589: rlogin_pass[0]=0;
2590:
2591: /* Init some important variables */
2592:
2593: #ifdef USE_CRYPTLIB
2594: ssh_mode=false;
2595: passthru_input_thread_running = false;
2596: passthru_output_thread_running = false;
2597: #endif
2598:
2599: rio_abortable=false;
2600:
2601: console = 0;
2602: online = 0;
2603: outchar_esc = 0;
2604: nodemsg_inside = 0; /* allows single nest */
2605: hotkey_inside = 0; /* allows single nest */
2606: event_time = 0;
2607: event_code = nulstr;
2608: nodesync_inside = false;
2609: errorlog_inside = false;
2610: errormsg_inside = false;
2611: gettimeleft_inside = false;
2612: timeleft = 60*10; /* just incase this is being used for calling gettimeleft() */
2613: uselect_total = 0;
2614: lbuflen = 0;
2615: keybufbot=keybuftop=0; /* initialize [unget]keybuf pointers */
2616: connection="Telnet";
2617:
2618: ZERO_VAR(telnet_local_option);
2619: ZERO_VAR(telnet_remote_option);
2620:
2621: telnet_cmdlen=0;
2622: telnet_mode=0;
2623: telnet_last_rxch=0;
2624:
2625: sys_status=lncntr=tos=criterrs=slcnt=0L;
2626: curatr=LIGHTGRAY;
2627: attr_sp=0; /* attribute stack pointer */
2628: errorlevel=0;
2629: logcol=1;
2630: logfile_fp=NULL;
2631: nodefile=-1;
2632: node_ext=-1;
2633: nodefile_fp=NULL;
2634: node_ext_fp=NULL;
2635: current_msg=NULL;
2636: mnestr=NULL;
2637:
2638: #ifdef JAVASCRIPT
2639: js_runtime=NULL; /* runtime */
2640: js_cx=NULL; /* context */
2641: #endif
2642:
2643: for(i=0;i<TOTAL_TEXT;i++)
2644: text[i]=text_sav[i]=global_text[i];
2645:
2646: ZERO_VAR(main_csi);
2647: ZERO_VAR(thisnode);
2648: ZERO_VAR(useron);
2649: ZERO_VAR(inbuf);
2650: ZERO_VAR(outbuf);
2651: ZERO_VAR(smb);
2652: ZERO_VAR(nodesync_user);
2653:
2654: action=NODE_MAIN;
2655: global_str_vars=0;
2656: global_str_var=NULL;
2657: global_str_var_name=NULL;
2658: global_int_vars=0;
2659: global_int_var=NULL;
2660: global_int_var_name=NULL;
2661: sysvar_li=0;
2662: sysvar_pi=0;
2663:
2664: cursub=NULL;
2665: usrgrp=NULL;
2666: usrsubs=NULL;
2667: usrsub=NULL;
2668: usrgrp_total=0;
2669:
2670: subscan=NULL;
2671:
2672: curdir=NULL;
2673: usrlib=NULL;
2674: usrdirs=NULL;
2675: usrdir=NULL;
2676: usrlib_total=0;
2677:
2678: batup_desc=NULL;
2679: batup_name=NULL;
2680: batup_misc=NULL;
2681: batup_dir=NULL;
2682: batup_alt=NULL;
2683:
2684: batdn_name=NULL;
2685: batdn_dir=NULL;
2686: batdn_offset=NULL;
2687: batdn_size=NULL;
2688: batdn_alt=NULL;
2689: batdn_cdt=NULL;
2690:
2691: spymsg("Connected");
2692: }
2693:
2694: //****************************************************************************
2695: bool sbbs_t::init()
2696: {
2697: char str[MAX_PATH+1];
2698: char tmp[128];
2699: int result;
2700: uint i,j,k,l;
2701: node_t node;
2702: socklen_t addr_len;
2703: SOCKADDR_IN addr;
2704:
2705: if(cfg.node_num>0) {
2706: RingBufInit(&inbuf, IO_THREAD_BUF_SIZE);
2707: node_inbuf[cfg.node_num-1]=&inbuf;
2708: }
2709:
2710: RingBufInit(&outbuf, IO_THREAD_BUF_SIZE);
2711: outbuf.highwater_mark=startup->outbuf_highwater_mark;
2712:
2713: if(cfg.node_num && client_socket!=INVALID_SOCKET) {
2714:
2715: #ifdef _WIN32
2716: if(!DuplicateHandle(GetCurrentProcess(),
2717: (HANDLE)client_socket,
2718: GetCurrentProcess(),
2719: (HANDLE*)&client_socket_dup,
2720: 0,
2721: TRUE, // Inheritable
2722: DUPLICATE_SAME_ACCESS)) {
2723: errormsg(WHERE,ERR_CREATE,"duplicate socket handle",client_socket);
2724: return(false);
2725: }
2726: #else
2727: client_socket_dup = client_socket;
2728: #endif
2729:
2730: addr_len=sizeof(addr);
2731: if((result=getsockname(client_socket, (struct sockaddr *)&addr,&addr_len))!=0) {
2732: lprintf(LOG_ERR,"Node %d !ERROR %d (%d) getting address/port"
2733: ,cfg.node_num, result, ERROR_VALUE);
2734: return(false);
2735: }
2736: lprintf(LOG_INFO,"Node %d attached to local interface %s port %d"
2737: ,cfg.node_num, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
2738:
2739: local_addr=addr.sin_addr.s_addr;
2740: }
2741:
2742: if((comspec=os_cmdshell())==NULL) {
2743: errormsg(WHERE, ERR_CHK, OS_CMD_SHELL_ENV_VAR" environment variable", 0);
2744: return(false);
2745: }
2746:
2747: md(cfg.temp_dir);
2748:
2749: /* Shared NODE files */
2750: SAFEPRINTF2(str,"%s%s",cfg.ctrl_dir,"node.dab");
2751: if((nodefile=nopen(str,O_DENYNONE|O_RDWR|O_CREAT))==-1) {
2752: errormsg(WHERE, ERR_OPEN, str, cfg.node_num);
2753: return(false);
2754: }
2755: memset(&node,0,sizeof(node_t)); /* write NULL to node struct */
2756: node.status=NODE_OFFLINE;
2757: while(filelength(nodefile)<(long)(cfg.sys_nodes*sizeof(node_t))) {
2758: lseek(nodefile,0L,SEEK_END);
2759: if(write(nodefile,&node,sizeof(node_t))!=sizeof(node_t)) {
2760: errormsg(WHERE,ERR_WRITE,str,sizeof(node_t));
2761: break;
2762: }
2763: }
2764: for(i=0; cfg.node_num>0 && i<LOOP_NODEDAB; i++) {
2765: if(lock(nodefile,(cfg.node_num-1)*sizeof(node_t),sizeof(node_t))==0) {
2766: unlock(nodefile,(cfg.node_num-1)*sizeof(node_t),sizeof(node_t));
2767: break;
2768: }
2769: mswait(100);
2770: }
2771: if(cfg.node_misc&NM_CLOSENODEDAB) {
2772: close(nodefile);
2773: nodefile=-1;
2774: }
2775:
2776: if(i>=LOOP_NODEDAB) {
2777: errormsg(WHERE, ERR_LOCK, str, cfg.node_num);
2778: return(false);
2779: }
2780:
2781: if(cfg.node_num) {
2782: SAFEPRINTF(str,"%snode.log",cfg.node_dir);
2783: if((logfile_fp=fopen(str,"a+b"))==NULL) {
2784: errormsg(WHERE, ERR_OPEN, str, 0);
2785: lprintf(LOG_ERR,"Perhaps this node is already running");
2786: return(false);
2787: }
2788:
2789: if(filelength(fileno(logfile_fp))) {
2790: log(crlf);
2791: now=time(NULL);
2792: struct tm tm;
2793: localtime_r(&now,&tm);
2794: sprintf(str,"%s %s %s %02d %u "
2795: "End of preexisting log entry (possible crash)"
2796: ,hhmmtostr(&cfg,&tm,tmp)
2797: ,wday[tm.tm_wday]
2798: ,mon[tm.tm_mon],tm.tm_mday,tm.tm_year+1900);
2799: logline("L!",str);
2800: log(crlf);
2801: catsyslog(1);
2802: }
2803:
2804: getnodedat(cfg.node_num,&thisnode,1);
2805: /* thisnode.status=0; */
2806: thisnode.action=0;
2807: thisnode.useron=0;
2808: thisnode.aux=0;
2809: thisnode.misc&=(NODE_EVENT|NODE_LOCK|NODE_RRUN);
2810: criterrs=thisnode.errors;
2811: putnodedat(cfg.node_num,&thisnode);
2812: }
2813:
2814: /** Put in if(cfg.node_num) ? (not needed for server and event threads) */
2815: backout();
2816:
2817: /* Reset COMMAND SHELL */
2818:
2819: main_csi.str=(char *)malloc(1024);
2820: if(main_csi.str==NULL) {
2821: errormsg(WHERE,ERR_ALLOC,"main_csi.str",1024);
2822: return(false);
2823: }
2824: memset(main_csi.str,0,1024);
2825: /***/
2826:
2827: if(cfg.total_grps) {
2828:
2829: usrgrp_total = cfg.total_grps;
2830:
2831: if((cursub=(uint *)malloc(sizeof(uint)*usrgrp_total))==NULL) {
2832: errormsg(WHERE, ERR_ALLOC, "cursub", sizeof(uint)*usrgrp_total);
2833: return(false);
2834: }
2835:
2836: if((usrgrp=(uint *)malloc(sizeof(uint)*usrgrp_total))==NULL) {
2837: errormsg(WHERE, ERR_ALLOC, "usrgrp", sizeof(uint)*usrgrp_total);
2838: return(false);
2839: }
2840:
2841: if((usrsubs=(uint *)malloc(sizeof(uint)*usrgrp_total))==NULL) {
2842: errormsg(WHERE, ERR_ALLOC, "usrsubs", sizeof(uint)*usrgrp_total);
2843: return(false);
2844: }
2845:
2846: if((usrsub=(uint **)calloc(usrgrp_total,sizeof(uint *)))==NULL) {
2847: errormsg(WHERE, ERR_ALLOC, "usrsub", sizeof(uint)*usrgrp_total);
2848: return(false);
2849: }
2850:
2851: if((subscan=(subscan_t *)malloc(sizeof(subscan_t)*cfg.total_subs))==NULL) {
2852: errormsg(WHERE, ERR_ALLOC, "subscan", sizeof(subscan_t)*cfg.total_subs);
2853: return(false);
2854: }
2855: }
2856:
2857: for(i=l=0;i<(uint)cfg.total_grps;i++) {
2858: for(j=k=0;j<cfg.total_subs;j++)
2859: if(cfg.sub[j]->grp==i)
2860: k++; /* k = number of subs per grp[i] */
2861: if(k>l) l=k; /* l = the largest number of subs per grp */
2862: }
2863: if(l)
2864: for(i=0;i<cfg.total_grps;i++)
2865: if((usrsub[i]=(uint *)malloc(sizeof(uint)*l))==NULL) {
2866: errormsg(WHERE, ERR_ALLOC, "usrsub[x]", sizeof(uint)*l);
2867: return(false);
2868: }
2869:
2870: if(cfg.total_libs) {
2871:
2872: usrlib_total = cfg.total_libs;
2873:
2874: if((curdir=(uint *)malloc(sizeof(uint)*usrlib_total))==NULL) {
2875: errormsg(WHERE, ERR_ALLOC, "curdir", sizeof(uint)*usrlib_total);
2876: return(false);
2877: }
2878:
2879: if((usrlib=(uint *)malloc(sizeof(uint)*usrlib_total))==NULL) {
2880: errormsg(WHERE, ERR_ALLOC, "usrlib", sizeof(uint)*usrlib_total);
2881: return(false);
2882: }
2883:
2884: if((usrdirs=(uint *)malloc(sizeof(uint)*usrlib_total))==NULL) {
2885: errormsg(WHERE, ERR_ALLOC, "usrdirs", sizeof(uint)*usrlib_total);
2886: return(false);
2887: }
2888:
2889: if((usrdir=(uint **)calloc(usrlib_total,sizeof(uint *)))==NULL) {
2890: errormsg(WHERE, ERR_ALLOC, "usrdir", sizeof(uint)*usrlib_total);
2891: return(false);
2892: }
2893: }
2894:
2895: for(i=l=0;i<cfg.total_libs;i++) {
2896: for(j=k=0;j<cfg.total_dirs;j++)
2897: if(cfg.dir[j]->lib==i)
2898: k++;
2899: if(k>l) l=k; /* l = largest number of dirs in a lib */
2900: }
2901: if(l) {
2902: l++; /* for temp dir */
2903: for(i=0;i<cfg.total_libs;i++)
2904: if((usrdir[i]=(uint *)malloc(sizeof(uint)*l))==NULL) {
2905: errormsg(WHERE, ERR_ALLOC, "usrdir[x]", sizeof(uint)*l);
2906: return(false);
2907: }
2908: }
2909:
2910: if(cfg.max_batup) {
2911:
2912: if((batup_desc=(char **)malloc(sizeof(char *)*cfg.max_batup))==NULL) {
2913: errormsg(WHERE, ERR_ALLOC, "batup_desc", sizeof(char *)*cfg.max_batup);
2914: return(false);
2915: }
2916: if((batup_name=(char **)malloc(sizeof(char *)*cfg.max_batup))==NULL) {
2917: errormsg(WHERE, ERR_ALLOC, "batup_name", sizeof(char *)*cfg.max_batup);
2918: return(false);
2919: }
2920: if((batup_misc=(long *)malloc(sizeof(long)*cfg.max_batup))==NULL) {
2921: errormsg(WHERE, ERR_ALLOC, "batup_misc", sizeof(char *)*cfg.max_batup);
2922: return(false);
2923: }
2924: if((batup_dir=(uint *)malloc(sizeof(uint)*cfg.max_batup))==NULL) {
2925: errormsg(WHERE, ERR_ALLOC, "batup_dir", sizeof(char *)*cfg.max_batup);
2926: return(false);
2927: }
2928: if((batup_alt=(ushort *)malloc(sizeof(ushort)*cfg.max_batup))==NULL) {
2929: errormsg(WHERE, ERR_ALLOC, "batup_alt", sizeof(char *)*cfg.max_batup);
2930: return(false);
2931: }
2932: for(i=0;i<cfg.max_batup;i++) {
2933: if((batup_desc[i]=(char *)malloc(59))==NULL) {
2934: errormsg(WHERE, ERR_ALLOC, "batup_desc[x]", 59);
2935: return(false);
2936: }
2937: if((batup_name[i]=(char *)malloc(13))==NULL) {
2938: errormsg(WHERE, ERR_ALLOC, "batup_name[x]", 13);
2939: return(false);
2940: }
2941: }
2942: }
2943:
2944: if(cfg.max_batdn) {
2945:
2946: if((batdn_name=(char **)malloc(sizeof(char *)*cfg.max_batdn))==NULL) {
2947: errormsg(WHERE, ERR_ALLOC, "batdn_name", sizeof(char *)*cfg.max_batdn);
2948: return(false);
2949: }
2950: if((batdn_dir=(uint *)malloc(sizeof(uint)*cfg.max_batdn))==NULL) {
2951: errormsg(WHERE, ERR_ALLOC, "batdn_dir", sizeof(uint)*cfg.max_batdn);
2952: return(false);
2953: }
2954: if((batdn_offset=(long *)malloc(sizeof(long)*cfg.max_batdn))==NULL) {
2955: errormsg(WHERE, ERR_ALLOC, "batdn_offset", sizeof(long)*cfg.max_batdn);
2956: return(false);
2957: }
2958: if((batdn_size=(ulong *)malloc(sizeof(ulong)*cfg.max_batdn))==NULL) {
2959: errormsg(WHERE, ERR_ALLOC, "batdn_size", sizeof(ulong)*cfg.max_batdn);
2960: return(false);
2961: }
2962: if((batdn_cdt=(ulong *)malloc(sizeof(ulong)*cfg.max_batdn))==NULL) {
2963: errormsg(WHERE, ERR_ALLOC, "batdn_cdt", sizeof(long)*cfg.max_batdn);
2964: return(false);
2965: }
2966: if((batdn_alt=(ushort *)malloc(sizeof(ushort)*cfg.max_batdn))==NULL) {
2967: errormsg(WHERE, ERR_ALLOC, "batdn_alt", sizeof(ushort)*cfg.max_batdn);
2968: return(false);
2969: }
2970: for(i=0;i<cfg.max_batdn;i++)
2971: if((batdn_name[i]=(char *)malloc(13))==NULL) {
2972: errormsg(WHERE, ERR_ALLOC, "batdn_name[x]", 13);
2973: return(false);
2974: }
2975: }
2976:
2977: reset_logon_vars();
2978:
2979: online=ON_REMOTE;
2980:
2981: return(true);
2982: }
2983:
2984: //****************************************************************************
2985: sbbs_t::~sbbs_t()
2986: {
2987: uint i;
2988: char node[32];
2989:
2990: if(cfg.node_num)
2991: SAFEPRINTF(node,"Node %d", cfg.node_num);
2992: else
2993: SAFECOPY(node,client_name);
2994: #ifdef _DEBUG
2995: lprintf(LOG_DEBUG,"%s destructor begin", node);
2996: #endif
2997:
2998: // if(!cfg.node_num)
2999: // rmdir(cfg.temp_dir);
3000:
3001: if(client_socket_dup!=INVALID_SOCKET && client_socket_dup!=client_socket)
3002: closesocket(client_socket_dup); /* close duplicate handle */
3003:
3004: if(cfg.node_num>0)
3005: node_inbuf[cfg.node_num-1]=NULL;
3006: if(cfg.node_num>0 && !input_thread_running)
3007: RingBufDispose(&inbuf);
3008: if(!output_thread_running)
3009: RingBufDispose(&outbuf);
3010:
3011: /* Close all open files */
3012: if(nodefile!=-1) {
3013: close(nodefile);
3014: nodefile=-1;
3015: }
3016: if(node_ext!=-1) {
3017: close(node_ext);
3018: node_ext=-1;
3019: }
3020: if(logfile_fp!=NULL) {
3021: fclose(logfile_fp);
3022: logfile_fp=NULL;
3023: }
3024:
3025: /********************************/
3026: /* Free allocated class members */
3027: /********************************/
3028:
3029: #ifdef JAVASCRIPT
3030: /* Free Context */
3031: if(js_cx!=NULL) {
3032: lprintf(LOG_DEBUG,"%s JavaScript: Destroying context",node);
3033: JS_DestroyContext(js_cx);
3034: js_cx=NULL;
3035: }
3036:
3037: if(js_runtime!=NULL) {
3038: lprintf(LOG_DEBUG,"%s JavaScript: Destroying runtime",node);
3039: JS_DestroyRuntime(js_runtime);
3040: js_runtime=NULL;
3041: }
3042: #endif
3043:
3044: /* Reset text.dat */
3045:
3046: for(i=0;i<TOTAL_TEXT && text!=NULL;i++)
3047: if(text[i]!=text_sav[i]) {
3048: if(text[i]!=nulstr)
3049: free(text[i]);
3050: }
3051:
3052: /* Global command shell vars */
3053:
3054: freevars(&main_csi);
3055: clearvars(&main_csi);
3056: FREE_AND_NULL(main_csi.str); /* crash */
3057: FREE_AND_NULL(main_csi.cs);
3058:
3059: for(i=0;i<global_str_vars && global_str_var!=NULL;i++)
3060: FREE_AND_NULL(global_str_var[i]);
3061:
3062: FREE_AND_NULL(global_str_var);
3063: FREE_AND_NULL(global_str_var_name);
3064: global_str_vars=0;
3065:
3066: FREE_AND_NULL(global_int_var);
3067: FREE_AND_NULL(global_int_var_name);
3068: global_int_vars=0;
3069:
3070: /* Sub-board variables */
3071: for(i=0;i<usrgrp_total && usrsub!=NULL;i++)
3072: FREE_AND_NULL(usrsub[i]); /* exception here (ptr=0xfdfdfdfd) on exit July-10-2002 */
3073:
3074: FREE_AND_NULL(cursub);
3075: FREE_AND_NULL(usrgrp);
3076: FREE_AND_NULL(usrsubs);
3077: FREE_AND_NULL(usrsub);
3078: FREE_AND_NULL(subscan);
3079:
3080: /* File Directory variables */
3081: for(i=0;i<usrlib_total && usrdir!=NULL;i++)
3082: FREE_AND_NULL(usrdir[i]);
3083:
3084: FREE_AND_NULL(curdir);
3085: FREE_AND_NULL(usrlib);
3086: FREE_AND_NULL(usrdirs);
3087: FREE_AND_NULL(usrdir);
3088:
3089: /* Batch upload vars */
3090: for(i=0;i<cfg.max_batup && batup_desc!=NULL && batup_name!=NULL;i++) {
3091: FREE_AND_NULL(batup_desc[i]);
3092: FREE_AND_NULL(batup_name[i]);
3093: }
3094:
3095: FREE_AND_NULL(batup_desc);
3096: FREE_AND_NULL(batup_name);
3097: FREE_AND_NULL(batup_misc);
3098: FREE_AND_NULL(batup_dir);
3099: FREE_AND_NULL(batup_alt);
3100:
3101: /* Batch download vars */
3102: for(i=0;i<cfg.max_batdn && batdn_name!=NULL;i++)
3103: FREE_AND_NULL(batdn_name[i]);
3104:
3105: FREE_AND_NULL(batdn_name);
3106: FREE_AND_NULL(batdn_dir);
3107: FREE_AND_NULL(batdn_offset);
3108: FREE_AND_NULL(batdn_size);
3109: FREE_AND_NULL(batdn_cdt);
3110: FREE_AND_NULL(batdn_alt);
3111:
3112: #if 0 && defined(_WIN32) && defined(_DEBUG) && defined(_MSC_VER)
3113: if(!_CrtCheckMemory())
3114: lprintf(LOG_ERR,"!MEMORY ERRORS REPORTED IN DATA/DEBUG.LOG!");
3115: #endif
3116:
3117: #ifdef _DEBUG
3118: lprintf(LOG_DEBUG,"%s destructor end", node);
3119: #endif
3120: }
3121:
3122: /****************************************************************************/
3123: /* Network open function. Opens all files DENYALL and retries LOOP_NOPEN */
3124: /* number of times if the attempted file is already open or denying access */
3125: /* for some other reason. */
3126: /* All files are opened in BINARY mode, unless O_TEXT access bit is set. */
3127: /****************************************************************************/
3128: int sbbs_t::nopen(char *str, int access)
3129: {
3130: char logstr[256];
3131: int file,share,count=0;
3132:
3133: if(access&O_DENYNONE) {
3134: share=SH_DENYNO;
3135: access&=~O_DENYNONE;
3136: }
3137: else if(access==O_RDONLY) share=SH_DENYWR;
3138: else share=SH_DENYRW;
3139: if(!(access&O_TEXT))
3140: access|=O_BINARY;
3141: while(((file=sopen(str,access,share,S_IREAD|S_IWRITE))==-1)
3142: && (errno==EACCES || errno==EAGAIN) && count++<LOOP_NOPEN)
3143: mswait(100);
3144: if(count>(LOOP_NOPEN/2) && count<=LOOP_NOPEN) {
3145: SAFEPRINTF2(logstr,"NOPEN COLLISION - File: \"%s\" Count: %d"
3146: ,str,count);
3147: logline("!!",logstr);
3148: }
3149: if(file==-1 && (errno==EACCES || errno==EAGAIN)) {
3150: SAFEPRINTF2(logstr,"NOPEN ACCESS DENIED - File: \"%s\" errno: %d"
3151: ,str,errno);
3152: logline("!!",logstr);
3153: bputs("\7\r\nNOPEN: ACCESS DENIED\r\n\7");
3154: }
3155: return(file);
3156: }
3157:
3158: void sbbs_t::spymsg(char* msg)
3159: {
3160: char str[512];
3161: struct in_addr addr;
3162:
3163: if(cfg.node_num<1)
3164: return;
3165:
3166: addr.s_addr=client_addr;
3167: SAFEPRINTF4(str,"\r\n\r\n*** Spy Message ***\r\nNode %d: %s [%s]\r\n*** %s ***\r\n\r\n"
3168: ,cfg.node_num,client_name,inet_ntoa(addr),msg);
3169: if(startup->node_spybuf!=NULL
3170: && startup->node_spybuf[cfg.node_num-1]!=NULL) {
3171: RingBufWrite(startup->node_spybuf[cfg.node_num-1],(uchar*)str,strlen(str));
3172: /* Signal spy output semaphore? */
3173: if(startup->node_spysem!=NULL
3174: && startup->node_spysem[sbbs->cfg.node_num-1]!=NULL)
3175: sem_post(startup->node_spysem[sbbs->cfg.node_num-1]);
3176: }
3177:
3178: if(cfg.node_num && spy_socket[cfg.node_num-1]!=INVALID_SOCKET)
3179: sendsocket(spy_socket[cfg.node_num-1],str,strlen(str));
3180: #ifdef __unix__
3181: if(cfg.node_num && uspy_socket[cfg.node_num-1]!=INVALID_SOCKET)
3182: sendsocket(uspy_socket[cfg.node_num-1],str,strlen(str));
3183: #endif
3184: }
3185:
3186: #define MV_BUFLEN 4096
3187:
3188: /****************************************************************************/
3189: /* Moves or copies a file from one dir to another */
3190: /* both 'src' and 'dest' must contain full path and filename */
3191: /* returns 0 if successful, -1 if error */
3192: /****************************************************************************/
3193: int sbbs_t::mv(char *src, char *dest, char copy)
3194: {
3195: char str[MAX_PATH+1],*buf,atr=curatr;
3196: int ind,outd;
3197: uint chunk=MV_BUFLEN;
3198: ulong length,l;
3199: time_t ftime;
3200: FILE *inp,*outp;
3201:
3202: if(!stricmp(src,dest)) /* source and destination are the same! */
3203: return(0);
3204: if(!fexistcase(src)) {
3205: bprintf("\r\n\7MV ERROR: Source doesn't exist\r\n'%s'\r\n"
3206: ,src);
3207: return(-1);
3208: }
3209: if(!copy && fexistcase(dest)) {
3210: bprintf("\r\n\7MV ERROR: Destination already exists\r\n'%s'\r\n"
3211: ,dest);
3212: return(-1);
3213: }
3214: #ifndef __unix__ /* need to determine if on same mount device */
3215: if(!copy && ((src[1]!=':' && dest[1]!=':')
3216: || (src[1]==':' && dest[1]==':' && toupper(src[0])==toupper(dest[0])))) {
3217: if(rename(src,dest)) { /* same drive, so move */
3218: bprintf("\r\nMV ERROR: Error renaming '%s'"
3219: "\r\n to '%s'\r\n\7",src,dest);
3220: return(-1);
3221: }
3222: return(0);
3223: }
3224: #endif
3225: attr(WHITE);
3226: if((ind=nopen(src,O_RDONLY))==-1) {
3227: errormsg(WHERE,ERR_OPEN,src,O_RDONLY);
3228: return(-1);
3229: }
3230: if((inp=fdopen(ind,"rb"))==NULL) {
3231: close(ind);
3232: errormsg(WHERE,ERR_FDOPEN,str,O_RDONLY);
3233: return(-1);
3234: }
3235: setvbuf(inp,NULL,_IOFBF,32*1024);
3236: if((outd=nopen(dest,O_WRONLY|O_CREAT|O_TRUNC))==-1) {
3237: fclose(inp);
3238: errormsg(WHERE,ERR_OPEN,dest,O_WRONLY|O_CREAT|O_TRUNC);
3239: return(-1);
3240: }
3241: if((outp=fdopen(outd,"wb"))==NULL) {
3242: close(outd);
3243: fclose(inp);
3244: errormsg(WHERE,ERR_FDOPEN,dest,O_WRONLY|O_CREAT|O_TRUNC);
3245: return(-1);
3246: }
3247: setvbuf(outp,NULL,_IOFBF,8*1024);
3248: ftime=filetime(ind);
3249: length=filelength(ind);
3250: if(length) { /* Something to copy */
3251: if((buf=(char *)malloc(MV_BUFLEN))==NULL) {
3252: fclose(inp);
3253: fclose(outp);
3254: errormsg(WHERE,ERR_ALLOC,nulstr,MV_BUFLEN);
3255: return(-1);
3256: }
3257: l=0L;
3258: while(l<length) {
3259: bprintf("%2lu%%",l ? (long)(100.0/((float)length/l)) : 0L);
3260: if(l+chunk>length)
3261: chunk=length-l;
3262: if(fread(buf,1,chunk,inp)!=chunk) {
3263: free(buf);
3264: fclose(inp);
3265: fclose(outp);
3266: errormsg(WHERE,ERR_READ,src,chunk);
3267: return(-1);
3268: }
3269: if(fwrite(buf,1,chunk,outp)!=chunk) {
3270: free(buf);
3271: fclose(inp);
3272: fclose(outp);
3273: errormsg(WHERE,ERR_WRITE,dest,chunk);
3274: return(-1);
3275: }
3276: l+=chunk;
3277: bputs("\b\b\b");
3278: }
3279: bputs(" \b\b\b"); /* erase it */
3280: attr(atr);
3281: free(buf);
3282: }
3283: fclose(inp);
3284: fclose(outp);
3285: setfdate(dest,ftime); /* Would be nice if we could use futime() instead */
3286: if(!copy && remove(src)) {
3287: errormsg(WHERE,ERR_REMOVE,src,0);
3288: return(-1);
3289: }
3290: return(0);
3291: }
3292:
3293: void sbbs_t::hangup(void)
3294: {
3295: if(client_socket_dup!=INVALID_SOCKET && client_socket_dup!=client_socket)
3296: closesocket(client_socket_dup);
3297: client_socket_dup=INVALID_SOCKET;
3298:
3299: if(client_socket!=INVALID_SOCKET) {
3300: mswait(1000); /* Give socket output buffer time to flush */
3301: client_off(client_socket);
3302: close_socket(client_socket);
3303: client_socket=INVALID_SOCKET;
3304: }
3305: sem_post(&outbuf.sem);
3306: online=FALSE;
3307: }
3308:
3309: int sbbs_t::incom(unsigned long timeout)
3310: {
3311: uchar ch;
3312:
3313: #if 0 /* looping version */
3314: while(!RingBufRead(&inbuf, &ch, 1))
3315: if(sem_trywait_block(&inbuf.sem,timeout)!=0 || sys_status&SS_ABORT)
3316: return(NOINP);
3317: #else
3318: if(!RingBufRead(&inbuf, &ch, 1)) {
3319: if(sem_trywait_block(&inbuf.sem,timeout)!=0)
3320: return(NOINP);
3321: if(!RingBufRead(&inbuf, &ch, 1))
3322: return(NOINP);
3323: }
3324: #endif
3325: return(ch);
3326: }
3327:
3328: int sbbs_t::outcom(uchar ch)
3329: {
3330: if(!RingBufFree(&outbuf))
3331: return(TXBOF);
3332: if(!RingBufWrite(&outbuf, &ch, 1))
3333: return(TXBOF);
3334: return(0);
3335: }
3336:
3337: void sbbs_t::putcom(char *str, int len)
3338: {
3339: int i;
3340:
3341: if(!len)
3342: len=strlen(str);
3343: for(i=0;i<len && online; i++)
3344: outcom(str[i]);
3345: }
3346:
3347: /* Legacy Remote I/O Control Interface */
3348: int sbbs_t::rioctl(ushort action)
3349: {
3350: int mode;
3351: int state;
3352:
3353: switch(action) {
3354: case GVERS: /* Get version */
3355: return(0x200);
3356: case GUART: /* Get UART I/O address, not available */
3357: return(0xffff);
3358: case GIRQN: /* Get IRQ number, not available */
3359: return((int)client_socket);
3360: case GBAUD: /* Get current bit rate */
3361: return(0xffff);
3362: case RXBC: /* Get receive buffer count */
3363: // ulong cnt;
3364: // ioctlsocket (client_socket,FIONREAD,&cnt);
3365: return(/* cnt+ */RingBufFull(&inbuf));
3366: case RXBS: /* Get receive buffer size */
3367: return(inbuf.size);
3368: case TXBC: /* Get transmit buffer count */
3369: return(RingBufFull(&outbuf));
3370: case TXBS: /* Get transmit buffer size */
3371: return(outbuf.size);
3372: case TXBF: /* Get transmit buffer free space */
3373: return(RingBufFree(&outbuf));
3374: case IOMODE:
3375: mode=0;
3376: if(rio_abortable)
3377: mode|=ABORT;
3378: return(mode);
3379: case IOSTATE:
3380: state=0;
3381: if(sys_status&SS_ABORT)
3382: state|=ABORT;
3383: return(state);
3384: case IOFI: /* Flush input buffer */
3385: RingBufReInit(&inbuf);
3386: break;
3387: case IOFO: /* Flush output buffer */
3388: RingBufReInit(&outbuf);
3389: break;
3390: case IOFB: /* Flush both buffers */
3391: RingBufReInit(&inbuf);
3392: RingBufReInit(&outbuf);
3393: break;
3394: case LFN81:
3395: case LFE71:
3396: case FIFOCTL:
3397: return(0);
3398: }
3399:
3400: if((action&0xff)==IOSM) { /* Get/Set/Clear mode */
3401: if(action&ABORT)
3402: rio_abortable=true;
3403: return(0);
3404: }
3405:
3406: if((action&0xff)==IOCM) { /* Get/Set/Clear mode */
3407: if(action&ABORT)
3408: rio_abortable=false;
3409: return(0);
3410: }
3411:
3412: if((action&0xff)==IOSS) { /* Set state */
3413: if(action&ABORT)
3414: sys_status|=SS_ABORT;
3415: return(0);
3416: }
3417:
3418: if((action&0xff)==IOCS) { /* Clear state */
3419: if(action&ABORT)
3420: sys_status&=~SS_ABORT;
3421: return(0);
3422: }
3423:
3424: return(0);
3425: }
3426:
3427: void sbbs_t::reset_logon_vars(void)
3428: {
3429: int i;
3430:
3431: /* bools */
3432: qwklogon=false;
3433:
3434: sys_status&=~(SS_USERON|SS_TMPSYSOP|SS_LCHAT|SS_ABORT
3435: |SS_PAUSEON|SS_PAUSEOFF|SS_EVENT|SS_NEWUSER|SS_NEWDAY);
3436: cid[0]=0;
3437: wordwrap[0]=0;
3438: question[0]=0;
3439: menu_dir[0]=0;
3440: menu_file[0]=0;
3441: rows=24;
3442: cols=80;
3443: lncntr=0;
3444: autoterm=0;
3445: lbuflen=0;
3446: slcnt=0;
3447: altul=0;
3448: timeleft_warn=0;
3449: keybufbot=keybuftop=0;
3450: logon_uls=logon_ulb=logon_dls=logon_dlb=0;
3451: logon_posts=logon_emails=logon_fbacks=0;
3452: batdn_total=batup_total=0;
3453: usrgrps=usrlibs=0;
3454: curgrp=curlib=0;
3455: for(i=0;i<cfg.total_libs;i++)
3456: curdir[i]=0;
3457: for(i=0;i<cfg.total_grps;i++)
3458: cursub[i]=0;
3459: cur_cps=3000;
3460: cur_rate=30000;
3461: dte_rate=38400;
3462: main_cmds=xfer_cmds=posts_read=0;
3463: lastnodemsg=0;
3464: lastnodemsguser[0]=0;
3465: }
3466:
3467: /****************************************************************************/
3468: /* Writes NODE.LOG at end of SYSTEM.LOG */
3469: /****************************************************************************/
3470: void sbbs_t::catsyslog(int crash)
3471: {
3472: char str[MAX_PATH+1];
3473: char *buf;
3474: int i,file;
3475: long length;
3476: struct tm tm;
3477:
3478: if(logfile_fp==NULL) {
3479: SAFEPRINTF(str,"%snode.log",cfg.node_dir);
3480: if((logfile_fp=fopen(str,"rb"))==NULL) {
3481: errormsg(WHERE,ERR_OPEN,str,O_RDONLY);
3482: return;
3483: }
3484: }
3485: length=ftell(logfile_fp);
3486: if(length) {
3487: if((buf=(char *)malloc(length))==NULL) {
3488: errormsg(WHERE,ERR_ALLOC,str,length);
3489: return;
3490: }
3491: rewind(logfile_fp);
3492: if(fread(buf,1,length,logfile_fp)!=(size_t)length) {
3493: errormsg(WHERE,ERR_READ,"log file",length);
3494: free((char *)buf);
3495: return;
3496: }
3497: now=time(NULL);
3498: localtime_r(&now,&tm);
3499: SAFEPRINTF4(str,"%slogs/%2.2d%2.2d%2.2d.log",cfg.logs_dir,tm.tm_mon+1,tm.tm_mday
3500: ,TM_YEAR(tm.tm_year));
3501: if((file=nopen(str,O_WRONLY|O_APPEND|O_CREAT))==-1) {
3502: errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_APPEND|O_CREAT);
3503: free((char *)buf);
3504: return;
3505: }
3506: if(lwrite(file,buf,length)!=length) {
3507: close(file);
3508: errormsg(WHERE,ERR_WRITE,str,length);
3509: free((char *)buf);
3510: return;
3511: }
3512: close(file);
3513: if(crash) {
3514: for(i=0;i<2;i++) {
3515: SAFEPRINTF(str,"%scrash.log",i ? cfg.logs_dir : cfg.node_dir);
3516: if((file=nopen(str,O_WRONLY|O_APPEND|O_CREAT))==-1) {
3517: errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_APPEND|O_CREAT);
3518: free((char *)buf);
3519: return;
3520: }
3521: if(lwrite(file,buf,length)!=length) {
3522: close(file);
3523: errormsg(WHERE,ERR_WRITE,str,length);
3524: free((char *)buf);
3525: return;
3526: }
3527: close(file);
3528: }
3529: }
3530: free((char *)buf);
3531: }
3532:
3533: fclose(logfile_fp);
3534:
3535: SAFEPRINTF(str,"%snode.log",cfg.node_dir);
3536: if((logfile_fp=fopen(str,"w+b"))==NULL) /* Truncate NODE.LOG */
3537: errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_TRUNC);
3538: }
3539:
3540:
3541: void sbbs_t::logoffstats()
3542: {
3543: char str[MAX_PATH+1];
3544: int i,file;
3545: stats_t stats;
3546:
3547: if(REALSYSOP && !(cfg.sys_misc&SM_SYSSTAT))
3548: return;
3549:
3550: for(i=0;i<2;i++) {
3551: SAFEPRINTF(str,"%sdsts.dab",i ? cfg.ctrl_dir : cfg.node_dir);
3552: if((file=nopen(str,O_RDWR))==-1) {
3553: errormsg(WHERE,ERR_OPEN,str,O_RDWR);
3554: return;
3555: }
3556: memset(&stats,0,sizeof(stats));
3557: lseek(file,4L,SEEK_SET); /* Skip timestamp, logons and logons today */
3558: read(file,&stats,sizeof(stats));
3559:
3560: if(!(useron.rest&FLAG('Q'))) { /* Don't count QWKnet nodes */
3561: stats.timeon+=(now-logontime)/60;
3562: stats.ttoday+=(now-logontime)/60;
3563: stats.ptoday+=logon_posts;
3564: }
3565: stats.uls+=logon_uls;
3566: stats.ulb+=logon_ulb;
3567: stats.dls+=logon_dls;
3568: stats.dlb+=logon_dlb;
3569: stats.etoday+=logon_emails;
3570: stats.ftoday+=logon_fbacks;
3571:
3572: #if 0 // This is now handled in newuserdat()
3573: if(sys_status&SS_NEWUSER)
3574: stats.nusers++;
3575: #endif
3576:
3577: lseek(file,4L,SEEK_SET);
3578: write(file,&stats,sizeof(stats));
3579: close(file);
3580: }
3581: }
3582:
3583: void node_thread(void* arg)
3584: {
3585: ulong stack_frame;
3586: char str[128];
3587: int file;
3588: uint curshell=0;
3589: node_t node;
3590: sbbs_t* sbbs = (sbbs_t*) arg;
3591:
3592: update_clients();
3593: thread_up(TRUE /* setuid */);
3594:
3595: #ifdef _DEBUG
3596: lprintf(LOG_DEBUG,"Node %d thread started",sbbs->cfg.node_num);
3597: #endif
3598:
3599: sbbs_srand(); /* Seed random number generator */
3600:
3601: #ifdef JAVASCRIPT
3602: if(!(startup->options&BBS_OPT_NO_JAVASCRIPT)) {
3603: if(!sbbs->js_init(&stack_frame)) /* This must be done in the context of the node thread */
3604: lprintf(LOG_ERR,"Node %d !JavaScript Initialization FAILURE",sbbs->cfg.node_num);
3605: }
3606: #endif
3607:
3608: if(sbbs->answer()) {
3609:
3610: if(sbbs->qwklogon) {
3611: sbbs->getsmsg(sbbs->useron.number);
3612: sbbs->qwk_sec();
3613: } else while(sbbs->useron.number
3614: && (sbbs->main_csi.misc&CS_OFFLINE_EXEC || sbbs->online)) {
3615:
3616: if(!sbbs->main_csi.cs || curshell!=sbbs->useron.shell) {
3617: if(sbbs->useron.shell>=sbbs->cfg.total_shells)
3618: sbbs->useron.shell=0;
3619: SAFEPRINTF2(str,"%s%s.bin",sbbs->cfg.mods_dir
3620: ,sbbs->cfg.shell[sbbs->useron.shell]->code);
3621: if(sbbs->cfg.mods_dir[0]==0 || !fexistcase(str))
3622: SAFEPRINTF2(str,"%s%s.bin",sbbs->cfg.exec_dir
3623: ,sbbs->cfg.shell[sbbs->useron.shell]->code);
3624: if((file=sbbs->nopen(str,O_RDONLY))==-1) {
3625: sbbs->errormsg(WHERE,ERR_OPEN,str,O_RDONLY);
3626: sbbs->hangup();
3627: break;
3628: }
3629: FREE_AND_NULL(sbbs->main_csi.cs);
3630: sbbs->freevars(&sbbs->main_csi);
3631: sbbs->clearvars(&sbbs->main_csi);
3632:
3633: sbbs->main_csi.length=filelength(file);
3634: if((sbbs->main_csi.cs=(uchar *)malloc(sbbs->main_csi.length))==NULL) {
3635: close(file);
3636: sbbs->errormsg(WHERE,ERR_ALLOC,str,sbbs->main_csi.length);
3637: sbbs->hangup();
3638: break;
3639: }
3640:
3641: if(lread(file,sbbs->main_csi.cs,sbbs->main_csi.length)
3642: !=(int)sbbs->main_csi.length) {
3643: sbbs->errormsg(WHERE,ERR_READ,str,sbbs->main_csi.length);
3644: close(file);
3645: free(sbbs->main_csi.cs);
3646: sbbs->main_csi.cs=NULL;
3647: sbbs->hangup();
3648: break;
3649: }
3650: close(file);
3651:
3652: curshell=sbbs->useron.shell;
3653: sbbs->main_csi.ip=sbbs->main_csi.cs;
3654: sbbs->menu_dir[0]=0;
3655: sbbs->menu_file[0]=0;
3656: }
3657: if(sbbs->exec(&sbbs->main_csi))
3658: break;
3659: }
3660: }
3661:
3662: #ifdef _WIN32
3663: if(startup->hangup_sound[0] && !(startup->options&BBS_OPT_MUTE))
3664: PlaySound(startup->hangup_sound, NULL, SND_ASYNC|SND_FILENAME);
3665: #endif
3666:
3667: sbbs->hangup(); /* closes sockets, calls client_off, and shuts down the output_thread */
3668: node_socket[sbbs->cfg.node_num-1]=INVALID_SOCKET;
3669:
3670: sbbs->logout();
3671: sbbs->logoffstats(); /* Updates both system and node dsts.dab files */
3672:
3673: if(sbbs->sys_status&SS_DAILY) { // New day, run daily events/maintenance
3674: sbbs->daily_maint();
3675: }
3676:
3677: #if 0 /* this is handled in the event_thread now */
3678: // Node Daily Event
3679: sbbs->getnodedat(sbbs->cfg.node_num,&node,0);
3680: if(node.misc&NODE_EVENT) {
3681: sbbs->getnodedat(sbbs->cfg.node_num,&node,1);
3682: node.status=NODE_EVENT_RUNNING;
3683: sbbs->putnodedat(sbbs->cfg.node_num,&node);
3684: if(sbbs->cfg.node_daily[0]) {
3685: sbbs->logentry("!:","Run node daily event");
3686: sbbs->external(
3687: sbbs->cmdstr(sbbs->cfg.node_daily,nulstr,nulstr,NULL)
3688: ,EX_OFFLINE);
3689: }
3690: sbbs->getnodedat(sbbs->cfg.node_num,&node,1);
3691: node.misc&=~NODE_EVENT;
3692: sbbs->putnodedat(sbbs->cfg.node_num,&node);
3693: }
3694: #endif
3695:
3696: // Wait for all node threads to terminate
3697: if(sbbs->input_thread_running || sbbs->output_thread_running
3698: #ifdef USE_CRYPTLIB
3699: || sbbs->passthru_input_thread_running || sbbs->passthru_output_thread_running
3700: #endif
3701: ) {
3702: lprintf(LOG_INFO,"Node %d Waiting for %s to terminate..."
3703: ,sbbs->cfg.node_num
3704: ,(sbbs->input_thread_running && sbbs->output_thread_running) ?
3705: "I/O threads" : sbbs->input_thread_running
3706: ? "input thread" : "output thread");
3707: time_t start=time(NULL);
3708: while(sbbs->input_thread_running
3709: || sbbs->output_thread_running
3710: #ifdef USE_CRYPTLIB
3711: || sbbs->passthru_input_thread_running || sbbs->passthru_output_thread_running
3712: #endif
3713: ) {
3714: if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
3715: lprintf(LOG_NOTICE,"Node %d !TIMEOUT waiting for %s to terminate"
3716: , sbbs->cfg.node_num
3717: ,(sbbs->input_thread_running && sbbs->output_thread_running) ?
3718: "I/O threads"
3719: : sbbs->input_thread_running
3720: ? "input thread" : "output thread");
3721: break;
3722: }
3723: mswait(100);
3724: }
3725: }
3726:
3727: sbbs->catsyslog(0);
3728:
3729: status(STATUS_WFC);
3730:
3731: sbbs->getnodedat(sbbs->cfg.node_num,&node,1);
3732: if(node.misc&NODE_DOWN)
3733: node.status=NODE_OFFLINE;
3734: else
3735: node.status=NODE_WFC;
3736: node.misc&=~(NODE_DOWN|NODE_INTR|NODE_MSGW|NODE_NMSG
3737: |NODE_UDAT|NODE_POFF|NODE_AOFF|NODE_EXT);
3738: /* node.useron=0; needed for hang-ups while in multinode chat */
3739: sbbs->putnodedat(sbbs->cfg.node_num,&node);
3740:
3741: if(node_threads_running>0)
3742: node_threads_running--;
3743: lprintf(LOG_DEBUG,"Node %d thread terminated (%u node threads remain, %lu clients served)"
3744: ,sbbs->cfg.node_num, node_threads_running, served);
3745: if(!sbbs->input_thread_running && !sbbs->output_thread_running)
3746: delete sbbs;
3747: else
3748: lprintf(LOG_WARNING,"Node %d !ORPHANED I/O THREAD(s)",sbbs->cfg.node_num);
3749:
3750: update_clients();
3751: thread_down();
3752: }
3753:
3754: void sbbs_t::daily_maint(void)
3755: {
3756: char str[128];
3757: char uname[LEN_ALIAS+1];
3758: uint i;
3759: uint usernum;
3760: uint lastusernum;
3761: node_t node;
3762: user_t user;
3763:
3764: now=time(NULL);
3765:
3766: sbbs->getnodedat(sbbs->cfg.node_num,&node,1);
3767: node.status=NODE_EVENT_RUNNING;
3768: sbbs->putnodedat(sbbs->cfg.node_num,&node);
3769:
3770: sbbs->logentry("!:","Ran system daily maintenance");
3771:
3772: if(sbbs->cfg.user_backup_level) {
3773: lputs(LOG_INFO,"Backing-up user data...");
3774: SAFEPRINTF(str,"%suser/user.dat",sbbs->cfg.data_dir);
3775: backup(str,sbbs->cfg.user_backup_level,FALSE);
3776: SAFEPRINTF(str,"%suser/name.dat",sbbs->cfg.data_dir);
3777: backup(str,sbbs->cfg.user_backup_level,FALSE);
3778: }
3779:
3780: if(sbbs->cfg.mail_backup_level) {
3781: lputs(LOG_INFO,"Backing-up mail data...");
3782: SAFEPRINTF(str,"%smail.shd",sbbs->cfg.data_dir);
3783: backup(str,sbbs->cfg.mail_backup_level,FALSE);
3784: SAFEPRINTF(str,"%smail.sha",sbbs->cfg.data_dir);
3785: backup(str,sbbs->cfg.mail_backup_level,FALSE);
3786: SAFEPRINTF(str,"%smail.sdt",sbbs->cfg.data_dir);
3787: backup(str,sbbs->cfg.mail_backup_level,FALSE);
3788: SAFEPRINTF(str,"%smail.sda",sbbs->cfg.data_dir);
3789: backup(str,sbbs->cfg.mail_backup_level,FALSE);
3790: SAFEPRINTF(str,"%smail.sid",sbbs->cfg.data_dir);
3791: backup(str,sbbs->cfg.mail_backup_level,FALSE);
3792: SAFEPRINTF(str,"%smail.sch",sbbs->cfg.data_dir);
3793: backup(str,sbbs->cfg.mail_backup_level,FALSE);
3794: }
3795:
3796: lputs(LOG_INFO,"Checking for inactive/expired user records...");
3797: lastusernum=lastuser(&sbbs->cfg);
3798: for(usernum=1;usernum<=lastusernum;usernum++) {
3799:
3800: SAFEPRINTF2(str,"%5u of %-5u",usernum,lastusernum);
3801: status(str);
3802: user.number=usernum;
3803: if((i=getuserdat(&sbbs->cfg,&user))!=0) {
3804: SAFEPRINTF(str,"user record %u",usernum);
3805: sbbs->errormsg(WHERE,ERR_READ,str,i);
3806: continue;
3807: }
3808:
3809: /***********************************************/
3810: /* Fix name (name.dat and user.dat) mismatches */
3811: /***********************************************/
3812: username(&sbbs->cfg,user.number,uname);
3813: if(user.misc&DELETED) {
3814: if(strcmp(uname,"DELETED USER"))
3815: putusername(&sbbs->cfg,user.number,nulstr);
3816: continue;
3817: }
3818:
3819: if(strcmp(user.alias,uname))
3820: putusername(&sbbs->cfg,user.number,user.alias);
3821:
3822: if(user.number==1)
3823: continue; /* skip expiration/inactivity checks for user #1 */
3824:
3825: if(!(user.misc&(DELETED|INACTIVE))
3826: && user.expire && (ulong)user.expire<=(ulong)now) {
3827: putsmsg(&sbbs->cfg,user.number,sbbs->text[AccountHasExpired]);
3828: SAFEPRINTF2(str,"%s #%u Expired",user.alias,user.number);
3829: sbbs->logentry("!%",str);
3830: if(sbbs->cfg.level_misc[user.level]&LEVEL_EXPTOVAL
3831: && sbbs->cfg.level_expireto[user.level]<10) {
3832: user.flags1=sbbs->cfg.val_flags1[sbbs->cfg.level_expireto[user.level]];
3833: user.flags2=sbbs->cfg.val_flags2[sbbs->cfg.level_expireto[user.level]];
3834: user.flags3=sbbs->cfg.val_flags3[sbbs->cfg.level_expireto[user.level]];
3835: user.flags4=sbbs->cfg.val_flags4[sbbs->cfg.level_expireto[user.level]];
3836: user.exempt=sbbs->cfg.val_exempt[sbbs->cfg.level_expireto[user.level]];
3837: user.rest=sbbs->cfg.val_rest[sbbs->cfg.level_expireto[user.level]];
3838: if(sbbs->cfg.val_expire[sbbs->cfg.level_expireto[user.level]])
3839: user.expire=now
3840: +(sbbs->cfg.val_expire[sbbs->cfg.level_expireto[user.level]]*24*60*60);
3841: else
3842: user.expire=0;
3843: user.level=sbbs->cfg.val_level[sbbs->cfg.level_expireto[user.level]];
3844: }
3845: else {
3846: if(sbbs->cfg.level_misc[user.level]&LEVEL_EXPTOLVL)
3847: user.level=sbbs->cfg.level_expireto[user.level];
3848: else
3849: user.level=sbbs->cfg.expired_level;
3850: user.flags1&=~sbbs->cfg.expired_flags1; /* expired status */
3851: user.flags2&=~sbbs->cfg.expired_flags2; /* expired status */
3852: user.flags3&=~sbbs->cfg.expired_flags3; /* expired status */
3853: user.flags4&=~sbbs->cfg.expired_flags4; /* expired status */
3854: user.exempt&=~sbbs->cfg.expired_exempt;
3855: user.rest|=sbbs->cfg.expired_rest;
3856: user.expire=0;
3857: }
3858: putuserrec(&sbbs->cfg,user.number,U_LEVEL,2,ultoa(user.level,str,10));
3859: putuserrec(&sbbs->cfg,user.number,U_FLAGS1,8,ultoa(user.flags1,str,16));
3860: putuserrec(&sbbs->cfg,user.number,U_FLAGS2,8,ultoa(user.flags2,str,16));
3861: putuserrec(&sbbs->cfg,user.number,U_FLAGS3,8,ultoa(user.flags3,str,16));
3862: putuserrec(&sbbs->cfg,user.number,U_FLAGS4,8,ultoa(user.flags4,str,16));
3863: putuserrec(&sbbs->cfg,user.number,U_EXPIRE,8,ultoa(user.expire,str,16));
3864: putuserrec(&sbbs->cfg,user.number,U_EXEMPT,8,ultoa(user.exempt,str,16));
3865: putuserrec(&sbbs->cfg,user.number,U_REST,8,ultoa(user.rest,str,16));
3866: if(sbbs->cfg.expire_mod[0]) {
3867: sbbs->useron=user;
3868: sbbs->online=ON_LOCAL;
3869: sbbs->exec_bin(sbbs->cfg.expire_mod,&sbbs->main_csi);
3870: sbbs->online=FALSE;
3871: }
3872: }
3873:
3874: /***********************************************************/
3875: /* Auto deletion based on expiration date or days inactive */
3876: /***********************************************************/
3877: if(!(user.exempt&FLAG('P')) /* Not a permanent account */
3878: && !(user.misc&(DELETED|INACTIVE)) /* alive */
3879: && (sbbs->cfg.sys_autodel && (now-user.laston)/(long)(24L*60L*60L)
3880: > sbbs->cfg.sys_autodel)) { /* Inactive too long */
3881: SAFEPRINTF2(str,"Auto-Deleted %s #%u",user.alias,user.number);
3882: sbbs->logentry("!*",str);
3883: sbbs->delallmail(user.number);
3884: putusername(&sbbs->cfg,user.number,nulstr);
3885: putuserrec(&sbbs->cfg,user.number,U_MISC,8,ultoa(user.misc|DELETED,str,16));
3886: }
3887: }
3888:
3889: lputs(LOG_INFO,"Purging deleted/expired e-mail");
3890: SAFEPRINTF(sbbs->smb.file,"%smail",sbbs->cfg.data_dir);
3891: sbbs->smb.retry_time=sbbs->cfg.smb_retry_time;
3892: sbbs->smb.subnum=INVALID_SUB;
3893: if((i=smb_open(&sbbs->smb))!=0)
3894: sbbs->errormsg(WHERE,ERR_OPEN,sbbs->smb.file,i,sbbs->smb.last_error);
3895: else {
3896: if(filelength(fileno(sbbs->smb.shd_fp))>0) {
3897: if((i=smb_locksmbhdr(&sbbs->smb))!=0)
3898: sbbs->errormsg(WHERE,ERR_LOCK,sbbs->smb.file,i,sbbs->smb.last_error);
3899: else
3900: sbbs->delmail(0,MAIL_ALL);
3901: }
3902: smb_close(&sbbs->smb);
3903: }
3904:
3905: sbbs->sys_status&=~SS_DAILY;
3906: if(sbbs->cfg.sys_daily[0]) {
3907: // status("Running system daily event");
3908: sbbs->logentry("!:","Ran system daily event");
3909: sbbs->external(sbbs->cmdstr(sbbs->cfg.sys_daily,nulstr,nulstr,NULL)
3910: ,EX_OFFLINE);
3911: }
3912: }
3913:
3914: time_t checktime(void)
3915: {
3916: struct tm tm;
3917:
3918: memset(&tm,0,sizeof(tm));
3919: tm.tm_year=94;
3920: tm.tm_mday=1;
3921: return(mktime(&tm)-0x2D24BD00L);
3922: }
3923:
3924: const char* DLLCALL js_ver(void)
3925: {
3926: #ifdef JAVASCRIPT
3927: return(JS_GetImplementationVersion());
3928: #else
3929: return("");
3930: #endif
3931: }
3932:
3933: /* Returns char string of version and revision */
3934: const char* DLLCALL bbs_ver(void)
3935: {
3936: static char ver[256];
3937: char compiler[32];
3938:
3939: if(ver[0]==0) { /* uninitialized */
3940: DESCRIBE_COMPILER(compiler);
3941:
3942: sprintf(ver,"%s %s%c%s SMBLIB %s Compiled %s %s with %s"
3943: ,TELNET_SERVER
3944: ,VERSION, REVISION
3945: #ifdef _DEBUG
3946: ," Debug"
3947: #else
3948: ,""
3949: #endif
3950: ,smb_lib_ver()
3951: ,__DATE__, __TIME__, compiler
3952: );
3953: }
3954: return(ver);
3955: }
3956:
3957: /* Returns binary-coded version and revision (e.g. 0x31000 == 3.10a) */
3958: long DLLCALL bbs_ver_num(void)
3959: {
3960: return(VERSION_HEX);
3961: }
3962:
3963: void DLLCALL bbs_terminate(void)
3964: {
3965: lprintf(LOG_DEBUG,"BBS Server terminate");
3966: terminate_server=true;
3967: }
3968:
3969: static void cleanup(int code)
3970: {
3971: lputs(LOG_INFO,"BBS System thread terminating");
3972:
3973: if(telnet_socket!=INVALID_SOCKET) {
3974: close_socket(telnet_socket);
3975: telnet_socket=INVALID_SOCKET;
3976: }
3977: if(rlogin_socket!=INVALID_SOCKET) {
3978: close_socket(rlogin_socket);
3979: rlogin_socket=INVALID_SOCKET;
3980: }
3981: #ifdef USE_CRYPTLIB
3982: if(ssh_socket!=INVALID_SOCKET) {
3983: close_socket(ssh_socket);
3984: ssh_socket=INVALID_SOCKET;
3985: }
3986: #endif
3987:
3988:
3989: #ifdef _WINSOCKAPI_
3990: if(WSAInitialized && WSACleanup()!=0)
3991: lprintf(LOG_ERR,"!WSACleanup ERROR %d",ERROR_VALUE);
3992: #endif
3993:
3994: free_cfg(&scfg);
3995: free_text(text);
3996:
3997: semfile_list_free(&recycle_semfiles);
3998: semfile_list_free(&shutdown_semfiles);
3999:
4000: #ifdef _WIN32
4001: if(exec_mutex!=NULL) {
4002: CloseHandle(exec_mutex);
4003: exec_mutex=NULL;
4004: }
4005:
4006: if(hK32!=NULL) {
4007: FreeLibrary(hK32);
4008: hK32=NULL;
4009: }
4010:
4011: #if 0 && defined(_DEBUG) && defined(_MSC_VER)
4012: _CrtMemDumpAllObjectsSince(&mem_chkpoint);
4013:
4014: if(debug_log!=INVALID_HANDLE_VALUE) {
4015: CloseHandle(debug_log);
4016: debug_log=INVALID_HANDLE_VALUE;
4017: }
4018: #endif // _DEBUG && _MSC_VER
4019: #endif // _WIN32
4020:
4021: status("Down");
4022: thread_down();
4023: if(terminate_server || code)
4024: lprintf(LOG_INFO,"BBS System thread terminated (%u threads remain, %lu clients served)"
4025: ,thread_count, served);
4026: if(startup->terminated!=NULL)
4027: startup->terminated(startup->cbdata,code);
4028: }
4029:
4030: void DLLCALL bbs_thread(void* arg)
4031: {
4032: char* host_name;
4033: char* identity;
4034: char* p;
4035: char str[MAX_PATH+1];
4036: char logstr[256];
4037: SOCKADDR_IN server_addr={0};
4038: SOCKADDR_IN client_addr;
4039: socklen_t client_addr_len;
4040: SOCKET client_socket=INVALID_SOCKET;
4041: fd_set socket_set;
4042: SOCKET high_socket_set;
4043: int i;
4044: int file;
4045: int result;
4046: time_t t;
4047: time_t start;
4048: time_t initialized=0;
4049: node_t node;
4050: sbbs_t* events=NULL;
4051: client_t client;
4052: startup=(bbs_startup_t*)arg;
4053: BOOL is_client=FALSE;
4054: #ifdef __unix__
4055: SOCKET uspy_listen_socket[MAX_NODES];
4056: struct sockaddr_un uspy_addr;
4057: socklen_t uspy_addr_len;
4058: #endif
4059: #ifdef USE_CRYPTLIB
4060: CRYPT_CONTEXT ssh_context;
4061: #endif
4062:
4063: if(startup==NULL) {
4064: sbbs_beep(100,500);
4065: fprintf(stderr, "No startup structure passed!\n");
4066: return;
4067: }
4068:
4069: if(startup->size!=sizeof(bbs_startup_t)) { // verify size
4070: sbbs_beep(100,500);
4071: sbbs_beep(300,500);
4072: sbbs_beep(100,500);
4073: fprintf(stderr, "Invalid startup structure!\n");
4074: return;
4075: }
4076:
4077: #ifdef _THREAD_SUID_BROKEN
4078: if(thread_suid_broken)
4079: startup->seteuid(TRUE);
4080: #endif
4081:
4082: /* Setup intelligent defaults */
4083: if(startup->telnet_port==0) startup->telnet_port=IPPORT_TELNET;
4084: if(startup->rlogin_port==0) startup->rlogin_port=513;
4085: #ifdef USE_CRYPTLIB
4086: if(startup->ssh_port==0) startup->ssh_port=22;
4087: #endif
4088: if(startup->outbuf_drain_timeout==0) startup->outbuf_drain_timeout=10;
4089: if(startup->sem_chk_freq==0) startup->sem_chk_freq=2;
4090: if(startup->temp_dir[0]) backslash(startup->temp_dir);
4091:
4092: ZERO_VAR(js_server_props);
4093: SAFEPRINTF3(js_server_props.version,"%s %s%c",TELNET_SERVER,VERSION,REVISION);
4094: js_server_props.version_detail=bbs_ver();
4095: js_server_props.clients=&node_threads_running;
4096: js_server_props.options=&startup->options;
4097: js_server_props.interface_addr=&startup->telnet_interface;
4098:
4099: uptime=0;
4100: served=0;
4101: startup->recycle_now=FALSE;
4102: startup->shutdown_now=FALSE;
4103: terminate_server=false;
4104:
4105: do {
4106:
4107: thread_up(FALSE /* setuid */);
4108:
4109: status("Initializing");
4110:
4111: /* Defeat the lameo hex0rs - the name and copyright must remain intact */
4112: if(crc32(COPYRIGHT_NOTICE,0)!=COPYRIGHT_CRC
4113: || crc32(VERSION_NOTICE,10)!=SYNCHRONET_CRC) {
4114: lprintf(LOG_ERR,"!CORRUPTED LIBRARY FILE");
4115: cleanup(1);
4116: return;
4117: }
4118:
4119: memset(text, 0, sizeof(text));
4120: memset(&scfg, 0, sizeof(scfg));
4121:
4122: node_threads_running=0;
4123: lastuseron[0]=0;
4124:
4125: char compiler[32];
4126: DESCRIBE_COMPILER(compiler);
4127:
4128: lprintf(LOG_INFO,"%s Version %s Revision %c%s"
4129: ,TELNET_SERVER
4130: ,VERSION
4131: ,toupper(REVISION)
4132: #ifdef _DEBUG
4133: ," Debug"
4134: #else
4135: ,""
4136: #endif
4137: );
4138: lprintf(LOG_INFO,"Compiled %s %s with %s", __DATE__, __TIME__, compiler);
4139: lprintf(LOG_INFO,"SMBLIB %s (format %x.%02x)",smb_lib_ver(),smb_ver()>>8,smb_ver()&0xff);
4140:
4141: if(startup->first_node<1 || startup->first_node>startup->last_node) {
4142: lprintf(LOG_ERR,"!ILLEGAL node configuration (first: %d, last: %d)"
4143: ,startup->first_node, startup->last_node);
4144: cleanup(1);
4145: return;
4146: }
4147:
4148: #ifdef __BORLANDC__
4149: #pragma warn -8008 /* Disable "Condition always false" warning */
4150: #pragma warn -8066 /* Disable "Unreachable code" warning */
4151: #endif
4152: if(sizeof(node_t)!=SIZEOF_NODE_T) {
4153: lprintf(LOG_ERR,"!COMPILER ERROR: sizeof(node_t)=%d instead of %d"
4154: ,sizeof(node_t),SIZEOF_NODE_T);
4155: cleanup(1);
4156: return;
4157: }
4158:
4159: #ifdef _WIN32
4160: if((exec_mutex=CreateMutex(NULL,false,NULL))==NULL) {
4161: lprintf(LOG_ERR,"!ERROR %d creating exec_mutex", GetLastError());
4162: cleanup(1);
4163: return;
4164: }
4165: hK32 = LoadLibrary("KERNEL32");
4166: #endif // _WIN32
4167:
4168: if(!winsock_startup()) {
4169: cleanup(1);
4170: return;
4171: }
4172:
4173: t=time(NULL);
4174: lprintf(LOG_INFO,"Initializing on %.24s with options: %lx"
4175: ,CTIME_R(&t,str),startup->options);
4176:
4177: if(chdir(startup->ctrl_dir)!=0)
4178: lprintf(LOG_ERR,"!ERROR %d changing directory to: %s", errno, startup->ctrl_dir);
4179:
4180: /* Initial configuration and load from CNF files */
4181: SAFECOPY(scfg.ctrl_dir,startup->ctrl_dir);
4182: lprintf(LOG_INFO,"Loading configuration files from %s", scfg.ctrl_dir);
4183: scfg.size=sizeof(scfg);
4184: scfg.node_num=startup->first_node;
4185: SAFECOPY(logstr,UNKNOWN_LOAD_ERROR);
4186: if(!load_cfg(&scfg, text, TRUE, logstr)) {
4187: lprintf(LOG_ERR,"!ERROR %s",logstr);
4188: lprintf(LOG_ERR,"!FAILED to load configuration files");
4189: cleanup(1);
4190: return;
4191: }
4192:
4193: if(startup->host_name[0]==0)
4194: SAFECOPY(startup->host_name,scfg.sys_inetaddr);
4195:
4196: if(!(scfg.sys_misc&SM_LOCAL_TZ) && !(startup->options&BBS_OPT_LOCAL_TIMEZONE)) {
4197: if(putenv("TZ=UTC0"))
4198: lprintf(LOG_ERR,"!putenv() FAILED");
4199: tzset();
4200:
4201: if((t=checktime())!=0) { /* Check binary time */
4202: lprintf(LOG_ERR,"!TIME PROBLEM (%ld)",t);
4203: cleanup(1);
4204: return;
4205: }
4206: }
4207:
4208: if(uptime==0)
4209: uptime=time(NULL); /* this must be done *after* setting the timezone */
4210:
4211: if(startup->last_node>scfg.sys_nodes) {
4212: lprintf(LOG_NOTICE,"Specified last_node (%d) > sys_nodes (%d), auto-corrected"
4213: ,startup->last_node, scfg.sys_nodes);
4214: startup->last_node=scfg.sys_nodes;
4215: }
4216:
4217: /* Create missing directories */
4218: lprintf(LOG_INFO,"Verifying/creating data directories");
4219: make_data_dirs(&scfg);
4220:
4221: /* Create missing node directories and dsts.dab files */
4222: lprintf(LOG_INFO,"Verifying/creating node directories");
4223: for(i=0;i<=scfg.sys_nodes;i++) {
4224: if(i)
4225: md(scfg.node_path[i-1]);
4226: SAFEPRINTF(str,"%sdsts.dab",i ? scfg.node_path[i-1] : scfg.ctrl_dir);
4227: if(flength(str)<DSTSDABLEN) {
4228: if((file=sopen(str,O_WRONLY|O_CREAT|O_APPEND, SH_DENYNO, S_IREAD|S_IWRITE))==-1) {
4229: lprintf(LOG_ERR,"!ERROR %d creating %s",errno, str);
4230: cleanup(1);
4231: return;
4232: }
4233: while(filelength(file)<DSTSDABLEN)
4234: if(write(file,"\0",1)!=1)
4235: break; /* Create NULL system dsts.dab */
4236: close(file);
4237: }
4238: }
4239:
4240: /* Initial global node variables */
4241: for(i=0;i<MAX_NODES;i++) {
4242: node_inbuf[i]=NULL;
4243: node_socket[i]=INVALID_SOCKET;
4244: spy_socket[i]=INVALID_SOCKET;
4245: #ifdef __unix__
4246: uspy_socket[i]=INVALID_SOCKET;
4247: uspy_listen_socket[i]=INVALID_SOCKET;
4248: #endif
4249: }
4250:
4251: startup->node_inbuf=node_inbuf;
4252:
4253: /* open a socket and wait for a client */
4254:
4255: telnet_socket = open_socket(SOCK_STREAM, "telnet");
4256:
4257: if(telnet_socket == INVALID_SOCKET) {
4258: lprintf(LOG_ERR,"!ERROR %d creating Telnet socket", ERROR_VALUE);
4259: cleanup(1);
4260: return;
4261: }
4262:
4263: lprintf(LOG_INFO,"Telnet socket %d opened",telnet_socket);
4264:
4265: /*****************************/
4266: /* Listen for incoming calls */
4267: /*****************************/
4268: memset(&server_addr, 0, sizeof(server_addr));
4269:
4270: server_addr.sin_addr.s_addr = htonl(startup->telnet_interface);
4271: server_addr.sin_family = AF_INET;
4272: server_addr.sin_port = htons(startup->telnet_port);
4273:
4274: if(startup->seteuid!=NULL)
4275: startup->seteuid(FALSE);
4276: result = retry_bind(telnet_socket,(struct sockaddr *)&server_addr,sizeof(server_addr)
4277: ,startup->bind_retry_count,startup->bind_retry_delay,"Telnet Server",lprintf);
4278: if(startup->seteuid!=NULL)
4279: startup->seteuid(TRUE);
4280: if(result != 0) {
4281: lprintf(LOG_NOTICE,"%s",BIND_FAILURE_HELP);
4282: cleanup(1);
4283: return;
4284: }
4285:
4286: result = listen(telnet_socket, 1);
4287:
4288: if(result != 0) {
4289: lprintf(LOG_ERR,"!ERROR %d (%d) listening on Telnet socket", result, ERROR_VALUE);
4290: cleanup(1);
4291: return;
4292: }
4293: lprintf(LOG_INFO,"Telnet server listening on port %d",startup->telnet_port);
4294:
4295: if(startup->options&BBS_OPT_ALLOW_RLOGIN) {
4296:
4297: /* open a socket and wait for a client */
4298:
4299: rlogin_socket = open_socket(SOCK_STREAM, "rlogin");
4300:
4301: if(rlogin_socket == INVALID_SOCKET) {
4302: lprintf(LOG_ERR,"!ERROR %d creating RLogin socket", ERROR_VALUE);
4303: cleanup(1);
4304: return;
4305: }
4306:
4307: lprintf(LOG_INFO,"RLogin socket %d opened",rlogin_socket);
4308:
4309: /*****************************/
4310: /* Listen for incoming calls */
4311: /*****************************/
4312: memset(&server_addr, 0, sizeof(server_addr));
4313:
4314: server_addr.sin_addr.s_addr = htonl(startup->rlogin_interface);
4315: server_addr.sin_family = AF_INET;
4316: server_addr.sin_port = htons(startup->rlogin_port);
4317:
4318: if(startup->seteuid!=NULL)
4319: startup->seteuid(FALSE);
4320: result = retry_bind(rlogin_socket,(struct sockaddr *)&server_addr,sizeof(server_addr)
4321: ,startup->bind_retry_count,startup->bind_retry_delay,"RLogin Server",lprintf);
4322: if(startup->seteuid!=NULL)
4323: startup->seteuid(TRUE);
4324: if(result != 0) {
4325: lprintf(LOG_NOTICE,"%s",BIND_FAILURE_HELP);
4326: cleanup(1);
4327: return;
4328: }
4329:
4330: result = listen(rlogin_socket, 1);
4331:
4332: if(result != 0) {
4333: lprintf(LOG_ERR,"!ERROR %d (%d) listening on RLogin socket", result, ERROR_VALUE);
4334: cleanup(1);
4335: return;
4336: }
4337: lprintf(LOG_INFO,"RLogin server listening on port %d",startup->rlogin_port);
4338: }
4339:
4340: #ifdef USE_CRYPTLIB
4341: #if CRYPTLIB_VERSION < 3300
4342: #warning This version of Cryptlib is known to crash Synchronet. Upgrade to at least version 3.3 or do not build with Cryptlib support.
4343: #endif
4344: if(startup->options&BBS_OPT_ALLOW_SSH) {
4345: bool loaded_key=false;
4346:
4347: CRYPT_KEYSET ssh_keyset;
4348:
4349: cryptInit();
4350: cryptAddRandom(NULL,CRYPT_RANDOM_SLOWPOLL);
4351: /* Get the private key... first try loading it from a file... */
4352: SAFEPRINTF2(str,"%s%s",scfg.ctrl_dir,"cryptlib.key");
4353: if(cryptStatusOK(cryptKeysetOpen(&ssh_keyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, str, CRYPT_KEYOPT_NONE))) {
4354: if(cryptStatusOK(cryptGetPrivateKey(ssh_keyset, &ssh_context, CRYPT_KEYID_NAME, "ssh_server", scfg.sys_pass)))
4355: loaded_key=true;
4356: cryptKeysetClose(ssh_keyset);
4357: /* Failed to load the key... delete the keyfile and create a new one */
4358: if(!loaded_key)
4359: remove(str);
4360: }
4361:
4362: if(!loaded_key) {
4363: /* Couldn't do that... create a new context and use the key from there... */
4364:
4365: if(!cryptStatusOK(i=cryptCreateContext(&ssh_context, CRYPT_UNUSED, CRYPT_ALGO_RSA))) {
4366: lprintf(LOG_ERR,"Cryptlib error %d creating context",i);
4367: goto NO_SSH;
4368: }
4369: if(!cryptStatusOK(i=cryptSetAttributeString(ssh_context, CRYPT_CTXINFO_LABEL, "ssh_server", 10))) {
4370: lprintf(LOG_ERR,"Cryptlib error %d setting key label",i);
4371: goto NO_SSH;
4372: }
4373: if(!cryptStatusOK(i=cryptGenerateKey(ssh_context))) {
4374: lprintf(LOG_ERR,"Cryptlib error %d generating key",i);
4375: goto NO_SSH;
4376: }
4377:
4378: /* Ok, now try saving this one... use the syspass to enctrpy it. */
4379: if(cryptStatusOK(cryptKeysetOpen(&ssh_keyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, str, CRYPT_KEYOPT_CREATE))) {
4380: cryptAddPrivateKey(ssh_keyset, ssh_context, scfg.sys_pass);
4381: cryptKeysetClose(ssh_keyset);
4382: }
4383: }
4384:
4385: /* open a socket and wait for a client */
4386:
4387: ssh_socket = open_socket(SOCK_STREAM, "ssh");
4388:
4389: if(ssh_socket == INVALID_SOCKET) {
4390: lprintf(LOG_ERR,"!ERROR %d creating SSH socket", ERROR_VALUE);
4391: cleanup(1);
4392: return;
4393: }
4394:
4395: lprintf(LOG_INFO,"SSH socket %d opened",ssh_socket);
4396:
4397: /*****************************/
4398: /* Listen for incoming calls */
4399: /*****************************/
4400: memset(&server_addr, 0, sizeof(server_addr));
4401:
4402: server_addr.sin_addr.s_addr = htonl(startup->ssh_interface);
4403: server_addr.sin_family = AF_INET;
4404: server_addr.sin_port = htons(startup->ssh_port);
4405:
4406: if(startup->seteuid!=NULL)
4407: startup->seteuid(FALSE);
4408: result = retry_bind(ssh_socket,(struct sockaddr *)&server_addr,sizeof(server_addr)
4409: ,startup->bind_retry_count,startup->bind_retry_delay,"SSH Server",lprintf);
4410: if(startup->seteuid!=NULL)
4411: startup->seteuid(TRUE);
4412: if(result != 0) {
4413: lprintf(LOG_NOTICE,"%s",BIND_FAILURE_HELP);
4414: cleanup(1);
4415: return;
4416: }
4417:
4418: result = listen(ssh_socket, 1);
4419:
4420: if(result != 0) {
4421: lprintf(LOG_ERR,"!ERROR %d (%d) listening on SSH socket", result, ERROR_VALUE);
4422: cleanup(1);
4423: return;
4424: }
4425: lprintf(LOG_INFO,"SSH server listening on port %d",startup->ssh_port);
4426: }
4427: NO_SSH:
4428: #endif
4429:
4430: sbbs = new sbbs_t(0, server_addr.sin_addr.s_addr
4431: ,"BBS System", telnet_socket, &scfg, text, NULL);
4432: sbbs->online = 0;
4433: if(sbbs->init()==false) {
4434: lputs(LOG_ERR,"!BBS initialization failed");
4435: cleanup(1);
4436: return;
4437: }
4438: _beginthread(output_thread, 0, sbbs);
4439:
4440: if(!(startup->options&BBS_OPT_NO_EVENTS)) {
4441: events = new sbbs_t(0, server_addr.sin_addr.s_addr
4442: ,"BBS Events", INVALID_SOCKET, &scfg, text, NULL);
4443: events->online = 0;
4444: if(events->init()==false) {
4445: lputs(LOG_ERR,"!Events initialization failed");
4446: cleanup(1);
4447: return;
4448: }
4449: _beginthread(event_thread, 0, events);
4450: }
4451:
4452: /* Save these values incase they're changed dynamically */
4453: first_node=startup->first_node;
4454: last_node=startup->last_node;
4455:
4456: for(i=first_node;i<=last_node;i++) {
4457: sbbs->getnodedat(i,&node,1);
4458: node.status=NODE_WFC;
4459: node.misc&=NODE_EVENT;
4460: node.action=0;
4461: sbbs->putnodedat(i,&node);
4462: }
4463:
4464: lprintf(LOG_INFO,"BBS System thread started for nodes %d through %d", first_node, last_node);
4465: status(STATUS_WFC);
4466:
4467: #if defined(_WIN32) && defined(_DEBUG) && defined(_MSC_VER)
4468:
4469: SAFEPRINTF(str,"%sDEBUG.LOG",scfg.logs_dir);
4470: if((debug_log=CreateFile(
4471: str, // pointer to name of the file
4472: GENERIC_READ|GENERIC_WRITE,
4473: FILE_SHARE_READ|FILE_SHARE_WRITE,
4474: NULL, // pointer to security attributes
4475: OPEN_ALWAYS, // how to create
4476: FILE_ATTRIBUTE_NORMAL, // file attributes
4477: NULL // handle to file with attributes to
4478: ))==INVALID_HANDLE_VALUE) {
4479: lprintf(LOG_ERR,"!ERROR %ld creating %s",GetLastError(),str);
4480: cleanup(1);
4481: return;
4482: }
4483:
4484: _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
4485: _CrtSetReportFile(_CRT_WARN, debug_log);
4486: _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE|_CRTDBG_MODE_WNDW);
4487: _CrtSetReportFile(_CRT_ERROR, debug_log);
4488: _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE|_CRTDBG_MODE_WNDW);
4489: _CrtSetReportFile(_CRT_ASSERT, debug_log);
4490:
4491: /* Turns on memory leak checking during program termination */
4492: // _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
4493:
4494: /* Save this allocation point for comparison */
4495: _CrtMemCheckpoint(&mem_chkpoint);
4496:
4497: #endif // _WIN32 && _DEBUG && _MSC_VER
4498:
4499: /* Setup recycle/shutdown semaphore file lists */
4500: shutdown_semfiles=semfile_list_init(scfg.ctrl_dir,"shutdown","telnet");
4501: recycle_semfiles=semfile_list_init(scfg.ctrl_dir,"recycle","telnet");
4502: SAFEPRINTF(str,"%stelnet.rec",scfg.ctrl_dir); /* legacy */
4503: semfile_list_add(&recycle_semfiles,str);
4504: if(!initialized) {
4505: semfile_list_check(&initialized,recycle_semfiles);
4506: semfile_list_check(&initialized,shutdown_semfiles);
4507: }
4508:
4509: #ifdef __unix__ // unix-domain spy sockets
4510: for(i=first_node;i<=last_node && !(startup->options&BBS_OPT_NO_SPY_SOCKETS);i++) {
4511: if((uspy_listen_socket[i-1]=socket(PF_UNIX,SOCK_STREAM,0))==INVALID_SOCKET)
4512: lprintf(LOG_ERR,"Node %d !ERROR %d creating local spy socket"
4513: , i, errno);
4514: else {
4515: lprintf(LOG_INFO,"Node %d local spy using socket %d", i, uspy_listen_socket[i-1]);
4516: if(startup!=NULL && startup->socket_open!=NULL)
4517: startup->socket_open(startup->cbdata,TRUE);
4518: }
4519:
4520: uspy_addr.sun_family=AF_UNIX;
4521: if((unsigned int)snprintf(str,sizeof(uspy_addr.sun_path),
4522: "%slocalspy%d.sock", startup->temp_dir, i)
4523: >=sizeof(uspy_addr.sun_path))
4524: uspy_listen_socket[i-1]=INVALID_SOCKET;
4525: else {
4526: strcpy(uspy_addr.sun_path,str);
4527: if(fexist(str))
4528: unlink(str);
4529: }
4530: if(uspy_listen_socket[i-1]!=INVALID_SOCKET) {
4531: uspy_addr_len=SUN_LEN(&uspy_addr);
4532: if(bind(uspy_listen_socket[i-1], (struct sockaddr *) &uspy_addr, uspy_addr_len)) {
4533: lprintf(LOG_ERR,"Node %d !ERROR %d binding local spy socket %d to %s"
4534: , i, errno, uspy_listen_socket[i-1], uspy_addr.sun_path);
4535: close_socket(uspy_listen_socket[i-1]);
4536: uspy_listen_socket[i-1]=INVALID_SOCKET;
4537: continue;
4538: }
4539: lprintf(LOG_INFO,"Node %d local spy socket %d bound to %s"
4540: , i, uspy_listen_socket[i-1], uspy_addr.sun_path);
4541: if(listen(uspy_listen_socket[i-1],1)) {
4542: lprintf(LOG_ERR,"Node %d !ERROR %d listening local spy socket %d"
4543: ,i, errno, uspy_listen_socket[i-1]);
4544: close_socket(uspy_listen_socket[i-1]);
4545: uspy_listen_socket[i-1]=INVALID_SOCKET;
4546: continue;
4547: }
4548: uspy_addr_len=sizeof(uspy_addr);
4549: }
4550: }
4551: #endif // __unix__ (unix-domain spy sockets)
4552:
4553: /* signal caller that we've started up successfully */
4554: if(startup->started!=NULL)
4555: startup->started(startup->cbdata);
4556:
4557:
4558: while(!terminate_server) {
4559:
4560: if(node_threads_running==0) { /* check for re-run flags */
4561: bool rerun=false;
4562: for(i=first_node;i<=last_node;i++) {
4563: if(sbbs->getnodedat(i,&node,0)!=0)
4564: continue;
4565: if(node.misc&NODE_RRUN) {
4566: sbbs->getnodedat(i,&node,1);
4567: if(!rerun)
4568: lprintf(LOG_INFO,"Node %d flagged for re-run",i);
4569: rerun=true;
4570: node.misc&=~NODE_RRUN;
4571: sbbs->putnodedat(i,&node);
4572: }
4573: }
4574: if(rerun)
4575: break;
4576: if(!(startup->options&BBS_OPT_NO_RECYCLE)) {
4577: if((p=semfile_list_check(&initialized,recycle_semfiles))!=NULL) {
4578: lprintf(LOG_INFO,"%04d Recycle semaphore file (%s) detected"
4579: ,telnet_socket,p);
4580: break;
4581: }
4582: #if 0 /* unused */
4583: if(startup->recycle_sem!=NULL && sem_trywait(&startup->recycle_sem)==0)
4584: startup->recycle_now=TRUE;
4585: #endif
4586: if(startup->recycle_now==TRUE) {
4587: lprintf(LOG_INFO,"%04d Recycle semaphore signaled",telnet_socket);
4588: startup->recycle_now=FALSE;
4589: break;
4590: }
4591: }
4592: if(((p=semfile_list_check(&initialized,shutdown_semfiles))!=NULL
4593: && lprintf(LOG_INFO,"%04d Shutdown semaphore file (%s) detected"
4594: ,telnet_socket,p))
4595: || (startup->shutdown_now==TRUE
4596: && lprintf(LOG_INFO,"%04d Shutdown semaphore signaled"
4597: ,telnet_socket))) {
4598: startup->shutdown_now=FALSE;
4599: terminate_server=TRUE;
4600: break;
4601: }
4602: }
4603:
4604: sbbs->online=FALSE;
4605: #ifdef USE_CRYPTLIB
4606: sbbs->ssh_mode=false;
4607: #endif
4608:
4609: /* now wait for connection */
4610:
4611: FD_ZERO(&socket_set);
4612: high_socket_set=0;
4613: if(telnet_socket!=INVALID_SOCKET) {
4614: FD_SET(telnet_socket,&socket_set);
4615: high_socket_set=telnet_socket+1;
4616: }
4617: if(startup->options&BBS_OPT_ALLOW_RLOGIN
4618: && rlogin_socket!=INVALID_SOCKET) {
4619: FD_SET(rlogin_socket,&socket_set);
4620: if(rlogin_socket+1>high_socket_set)
4621: high_socket_set=rlogin_socket+1;
4622: }
4623: #ifdef USE_CRYPTLIB
4624: if(startup->options&BBS_OPT_ALLOW_SSH
4625: && ssh_socket!=INVALID_SOCKET) {
4626: FD_SET(ssh_socket,&socket_set);
4627: if(ssh_socket+1>high_socket_set)
4628: high_socket_set=ssh_socket+1;
4629: }
4630: #endif
4631: #ifdef __unix__
4632: for(i=first_node;i<=last_node;i++) {
4633: if(uspy_listen_socket[i-1]!=INVALID_SOCKET) {
4634: FD_SET(uspy_listen_socket[i-1],&socket_set);
4635: if(uspy_listen_socket[i-1]+1>high_socket_set)
4636: high_socket_set=uspy_listen_socket[i-1]+1;
4637: }
4638: if(uspy_socket[i-1]!=INVALID_SOCKET) {
4639: FD_SET(uspy_socket[i-1],&socket_set);
4640: if(uspy_socket[i-1]+1>high_socket_set)
4641: high_socket_set=uspy_listen_socket[i-1]+1;
4642: }
4643: }
4644: #endif
4645:
4646: struct timeval tv;
4647: tv.tv_sec=startup->sem_chk_freq;
4648: tv.tv_usec=0;
4649:
4650: if((i=select(high_socket_set,&socket_set,NULL,NULL,&tv))<1) {
4651: if(i==0)
4652: continue;
4653: if(ERROR_VALUE==EINTR)
4654: lprintf(LOG_DEBUG,"Telnet Server listening interrupted");
4655: else if(ERROR_VALUE == ENOTSOCK)
4656: lprintf(LOG_NOTICE,"Telnet Server sockets closed");
4657: else
4658: lprintf(LOG_WARNING,"!ERROR %d selecting sockets",ERROR_VALUE);
4659: continue;
4660: }
4661:
4662: if(terminate_server) /* terminated */
4663: break;
4664:
4665: client_addr_len = sizeof(client_addr);
4666:
4667: bool rlogin = false;
4668: #ifdef USE_CRYPTLIB
4669: bool ssh = false;
4670: #endif
4671:
4672: is_client=FALSE;
4673: if(telnet_socket!=INVALID_SOCKET
4674: && FD_ISSET(telnet_socket,&socket_set)) {
4675: client_socket = accept_socket(telnet_socket, (struct sockaddr *)&client_addr
4676: ,&client_addr_len);
4677: is_client=TRUE;
4678: } else if(rlogin_socket!=INVALID_SOCKET
4679: && FD_ISSET(rlogin_socket,&socket_set)) {
4680: client_socket = accept_socket(rlogin_socket, (struct sockaddr *)&client_addr
4681: ,&client_addr_len);
4682: rlogin = true;
4683: is_client=TRUE;
4684: #ifdef USE_CRYPTLIB
4685: } else if(ssh_socket!=INVALID_SOCKET
4686: && FD_ISSET(ssh_socket,&socket_set)) {
4687:
4688: client_socket = accept_socket(ssh_socket, (struct sockaddr *)&client_addr
4689: ,&client_addr_len);
4690: ssh = true;
4691: is_client=TRUE;
4692: sbbs->ssh_mode=true;
4693: #endif
4694: } else {
4695: #ifdef __unix__
4696: for(i=first_node;i<=last_node;i++) {
4697: if(uspy_socket[i-1]!=INVALID_SOCKET
4698: && FD_ISSET(uspy_socket[i-1],&socket_set)) {
4699: if(node_socket[i-1]==INVALID_SOCKET)
4700: read(uspy_socket[i-1],str,sizeof(str));
4701: if(!socket_check(uspy_socket[i-1],NULL,NULL,0)) {
4702: lprintf(LOG_NOTICE,"Spy socket for node %d disconnected",i);
4703: close_socket(uspy_socket[i-1]);
4704: uspy_socket[i-1]=INVALID_SOCKET;
4705: }
4706: }
4707: if(uspy_listen_socket[i-1]!=INVALID_SOCKET
4708: && FD_ISSET(uspy_listen_socket[i-1],&socket_set)) {
4709: BOOL already_connected=(uspy_socket[i-1]!=INVALID_SOCKET);
4710: SOCKET new_socket=INVALID_SOCKET;
4711: new_socket = accept(uspy_listen_socket[i-1], (struct sockaddr *)&uspy_addr
4712: ,&uspy_addr_len);
4713: if(new_socket < 0) {
4714: lprintf(LOG_ERR,"!ERROR Spy socket for node %d unable to accept()",i);
4715: close_socket(uspy_listen_socket[i-1]);
4716: uspy_listen_socket[i-1]=INVALID_SOCKET;
4717: }
4718: fcntl(new_socket,F_SETFL,fcntl(new_socket,F_GETFL)|O_NONBLOCK);
4719: if(already_connected) {
4720: lprintf(LOG_ERR,"!ERROR Spy socket %s already in use",uspy_addr.sun_path);
4721: send(new_socket,"Spy socket already in use.\r\n",27,0);
4722: close_socket(new_socket);
4723: }
4724: else {
4725: lprintf(LOG_ERR,"!Spy socket %s (%d) connected",uspy_addr.sun_path,new_socket);
4726: uspy_socket[i-1]=new_socket;
4727: SAFEPRINTF(str,"Spy connection established to node %d\r\n",i);
4728: send(uspy_socket[i-1],str,strlen(str),0);
4729: }
4730: }
4731: }
4732: #else
4733: lprintf(LOG_ERR,"!NO SOCKETS set by select");
4734: #endif
4735: }
4736:
4737: if(!is_client) {
4738: /* Do not need to close_socket(client_socket) here */
4739: continue;
4740: }
4741:
4742: if(client_socket == INVALID_SOCKET) {
4743: #if 0 /* is this necessary still? */
4744: if(ERROR_VALUE == ENOTSOCK || ERROR_VALUE == EINTR || ERROR_VALUE == EINVAL) {
4745: lputs(LOG_NOTICE,"BBS socket closed");
4746: break;
4747: }
4748: #endif
4749: lprintf(LOG_ERR,"!ERROR %d accepting connection", ERROR_VALUE);
4750: #ifdef _WIN32
4751: if(WSAGetLastError()==WSAENOBUFS) /* recycle (re-init WinSock) on this error */
4752: break;
4753: #endif
4754: SSH_END();
4755: continue;
4756: }
4757: char host_ip[32];
4758:
4759: strcpy(host_ip,inet_ntoa(client_addr.sin_addr));
4760:
4761: if(trashcan(&scfg,host_ip,"ip-silent")) {
4762: SSH_END();
4763: close_socket(client_socket);
4764: continue;
4765: }
4766:
4767: lprintf(LOG_INFO,"%04d %s connection accepted from: %s port %u"
4768: ,client_socket
4769: #ifdef USE_CRYPTLIB
4770: ,rlogin ? "RLogin" : (ssh ? "SSH" : "Telnet")
4771: #else
4772: ,rlogin ? "RLogin" : "Telnet"
4773: #endif
4774: , host_ip, ntohs(client_addr.sin_port));
4775:
4776: #ifdef _WIN32
4777: if(startup->answer_sound[0] && !(startup->options&BBS_OPT_MUTE))
4778: PlaySound(startup->answer_sound, NULL, SND_ASYNC|SND_FILENAME);
4779: #endif
4780:
4781: /* Do SSH stuff here */
4782:
4783: if(ssh) {
4784: if(!cryptStatusOK(i=cryptCreateSession(&sbbs->ssh_session, CRYPT_UNUSED, CRYPT_SESSION_SSH_SERVER))) {
4785: lprintf(LOG_ERR,"%04d Cryptlib error %d creating session", client_socket, i);
4786: close_socket(client_socket);
4787: continue;
4788: }
4789: if(!cryptStatusOK(i=cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_PRIVATEKEY, ssh_context))) {
4790: lprintf(LOG_ERR,"%04d Cryptlib error %d setting private key",client_socket, i);
4791: cryptDestroySession(sbbs->ssh_session);
4792: close_socket(client_socket);
4793: continue;
4794: }
4795: /* Accept any credentials */
4796: if(!cryptStatusOK(i=cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_AUTHRESPONSE, 1))) {
4797: lprintf(LOG_ERR,"%04d Cryptlib error %d setting AUTHRESPONSE",client_socket, i);
4798: cryptDestroySession(sbbs->ssh_session);
4799: close_socket(client_socket);
4800: continue;
4801: }
4802: if(!cryptStatusOK(i=cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_NETWORKSOCKET, client_socket))) {
4803: lprintf(LOG_ERR,"%04d Cryptlib error %d setting socket",client_socket, i);
4804: cryptDestroySession(sbbs->ssh_session);
4805: close_socket(client_socket);
4806: continue;
4807: }
4808: if(!cryptStatusOK(i=cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_ACTIVE, 1))) {
4809: lprintf(LOG_ERR,"%04d Cryptlib error %d setting session active",client_socket, i);
4810: cryptDestroySession(sbbs->ssh_session);
4811: close_socket(client_socket);
4812: continue;
4813: }
4814: cryptPopData(sbbs->ssh_session, str, sizeof(str), &i);
4815: }
4816:
4817: sbbs->client_socket=client_socket; // required for output to the user
4818: sbbs->online=ON_REMOTE;
4819:
4820: if(sbbs->trashcan(host_ip,"ip")) {
4821: SSH_END();
4822: close_socket(client_socket);
4823: lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in ip.can"
4824: ,client_socket);
4825: SAFEPRINTF(logstr, "Blocked IP: %s",host_ip);
4826: sbbs->syslog("@!",logstr);
4827: continue;
4828: }
4829:
4830: if(rlogin)
4831: sbbs->outcom(0); /* acknowledge RLogin per RFC 1282 */
4832:
4833: sbbs->putcom(crlf);
4834: sbbs->putcom(VERSION_NOTICE);
4835: sbbs->putcom(crlf);
4836:
4837: sbbs->bprintf("Connection from: %s\r\n", host_ip);
4838:
4839: struct hostent* h;
4840: if(startup->options&BBS_OPT_NO_HOST_LOOKUP)
4841: h=NULL;
4842: else {
4843: sbbs->bprintf("Resolving hostname...");
4844: h=gethostbyaddr((char *)&client_addr.sin_addr
4845: ,sizeof(client_addr.sin_addr),AF_INET);
4846: sbbs->putcom(crlf);
4847: }
4848: if(h!=NULL && h->h_name!=NULL)
4849: host_name=h->h_name;
4850: else
4851: host_name="<no name>";
4852:
4853: if(!(startup->options&BBS_OPT_NO_HOST_LOOKUP)) {
4854: lprintf(LOG_INFO,"%04d Hostname: %s", client_socket, host_name);
4855: for(i=0;h!=NULL && h->h_aliases!=NULL && h->h_aliases[i]!=NULL;i++)
4856: lprintf(LOG_INFO,"%04d HostAlias: %s", client_socket, h->h_aliases[i]);
4857: }
4858:
4859: if(sbbs->trashcan(host_name,"host")) {
4860: SSH_END();
4861: close_socket(client_socket);
4862: lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in host.can",client_socket);
4863: SAFEPRINTF(logstr, "Blocked Hostname: %s",host_name);
4864: sbbs->syslog("@!",logstr);
4865: continue;
4866: }
4867:
4868: identity=NULL;
4869: if(startup->options&BBS_OPT_GET_IDENT) {
4870: sbbs->bprintf("Resolving identity...");
4871: identify(&client_addr, startup->telnet_port, str, sizeof(str)-1,0);
4872: identity=strrchr(str,':');
4873: if(identity!=NULL) {
4874: identity++; /* skip colon */
4875: while(*identity && *identity<=' ') /* point to user name */
4876: identity++;
4877: lprintf(LOG_INFO,"%04d Identity: %s",client_socket, identity);
4878: }
4879: sbbs->putcom(crlf);
4880: }
4881: /* Initialize client display */
4882: client.size=sizeof(client);
4883: client.time=time(NULL);
4884: SAFECOPY(client.addr,host_ip);
4885: SAFECOPY(client.host,host_name);
4886: client.port=ntohs(client_addr.sin_port);
4887: #ifdef USE_CRYPTLIB
4888: client.protocol=rlogin ? "RLogin":(ssh ? "SSH" : "Telnet");
4889: #else
4890: client.protocol=rlogin ? "RLogin":"Telnet";
4891: #endif
4892: client.user="<unknown>";
4893: client_on(client_socket,&client,FALSE /* update */);
4894:
4895: for(i=first_node;i<=last_node;i++) {
4896: /* paranoia: make sure node.status!=NODE_WFC by default */
4897: node.status=NODE_INVALID_STATUS;
4898: if(sbbs->getnodedat(i,&node,1)!=0)
4899: continue;
4900: if(node.status==NODE_WFC) {
4901: node.status=NODE_LOGON;
4902: sbbs->putnodedat(i,&node);
4903: break;
4904: }
4905: sbbs->putnodedat(i,&node);
4906: }
4907:
4908: if(i>last_node) {
4909: lprintf(LOG_WARNING,"%04d !No nodes available for login.",client_socket);
4910: SAFEPRINTF(str,"%snonodes.txt",scfg.text_dir);
4911: if(fexist(str))
4912: sbbs->printfile(str,P_NOABORT);
4913: else {
4914: sbbs->putcom("\r\nSorry, all telnet nodes are in use or otherwise unavailable.\r\n");
4915: sbbs->putcom("Please try again later.\r\n");
4916: }
4917: mswait(3000);
4918: client_off(client_socket);
4919: SSH_END();
4920: close_socket(client_socket);
4921: continue;
4922: }
4923:
4924: node_socket[i-1]=client_socket;
4925:
4926: sbbs_t* new_node = new sbbs_t(i, client_addr.sin_addr.s_addr, host_name
4927: ,client_socket
4928: ,&scfg, text, &client);
4929:
4930: new_node->client=client;
4931: #ifdef USE_CRYPTLIB
4932: if(ssh) {
4933: new_node->ssh_session=sbbs->ssh_session;
4934: new_node->ssh_mode=true;
4935: }
4936: #endif
4937:
4938: /* copy the IDENT response, if any */
4939: if(identity!=NULL)
4940: SAFECOPY(new_node->client_ident,identity);
4941:
4942: if(new_node->init()==false) {
4943: lprintf(LOG_INFO,"%04d !Node %d Initialization failure"
4944: ,client_socket,new_node->cfg.node_num);
4945: SAFEPRINTF(str,"%snonodes.txt",scfg.text_dir);
4946: if(fexist(str))
4947: sbbs->printfile(str,P_NOABORT);
4948: else
4949: sbbs->putcom("\r\nSorry, initialization failed. Try again later.\r\n");
4950: mswait(3000);
4951: sbbs->getnodedat(new_node->cfg.node_num,&node,1);
4952: node.status=NODE_WFC;
4953: sbbs->putnodedat(new_node->cfg.node_num,&node);
4954: delete new_node;
4955: node_socket[i-1]=INVALID_SOCKET;
4956: client_off(client_socket);
4957: SSH_END();
4958: close_socket(client_socket);
4959: continue;
4960: }
4961:
4962: if(rlogin==true) {
4963: new_node->connection="RLogin";
4964: new_node->sys_status|=SS_RLOGIN;
4965: new_node->telnet_mode|=TELNET_MODE_OFF; // RLogin does not use Telnet commands
4966: }
4967: #ifdef USE_CRYPTLIB
4968: if(ssh) {
4969: SOCKET tmp_sock;
4970: SOCKADDR_IN tmp_addr={0};
4971: socklen_t tmp_addr_len;
4972:
4973: /* open a socket and connect to yourself */
4974:
4975: tmp_sock = open_socket(SOCK_STREAM, "passthru");
4976:
4977: if(tmp_sock == INVALID_SOCKET) {
4978: lprintf(LOG_ERR,"!ERROR %d creating passthru listen socket", ERROR_VALUE);
4979: goto NO_PASSTHRU;
4980: }
4981:
4982: lprintf(LOG_INFO,"passthru listen socket %d opened",tmp_sock);
4983:
4984: /*****************************/
4985: /* Listen for incoming calls */
4986: /*****************************/
4987: memset(&tmp_addr, 0, sizeof(tmp_addr));
4988:
4989: tmp_addr.sin_addr.s_addr = htonl(0x7f000001U);
4990: tmp_addr.sin_family = AF_INET;
4991: tmp_addr.sin_port = 0;
4992:
4993: result = bind(tmp_sock,(struct sockaddr *)&tmp_addr,sizeof(tmp_addr));
4994: if(result != 0) {
4995: lprintf(LOG_NOTICE,"%s",BIND_FAILURE_HELP);
4996: close_socket(tmp_sock);
4997: goto NO_PASSTHRU;
4998: }
4999:
5000: result = listen(tmp_sock, 1);
5001:
5002: if(result != 0) {
5003: lprintf(LOG_ERR,"!ERROR %d (%d) listening on passthru socket", result, ERROR_VALUE);
5004: close_socket(tmp_sock);
5005: goto NO_PASSTHRU;
5006: }
5007: lprintf(LOG_INFO,"Listening passthru socket listening on port %d",htons(tmp_addr.sin_port));
5008:
5009: new_node->passthru_socket = open_socket(SOCK_STREAM, "passthru");
5010:
5011: if(new_node->passthru_socket == INVALID_SOCKET) {
5012: lprintf(LOG_ERR,"!ERROR %d creating passthru connecting socket", ERROR_VALUE);
5013: close_socket(tmp_sock);
5014: goto NO_PASSTHRU;
5015: }
5016:
5017: lprintf(LOG_INFO,"passthru connect socket %d opened",new_node->passthru_socket);
5018:
5019: tmp_addr_len=sizeof(tmp_addr);
5020: if(getsockname(tmp_sock, (struct sockaddr *)&tmp_addr, &tmp_addr_len)) {
5021: lprintf(LOG_ERR,"!ERROR %d getting passthru listener address", ERROR_VALUE);
5022: close_socket(tmp_sock);
5023: close_socket(new_node->passthru_socket);
5024: new_node->passthru_socket=INVALID_SOCKET;
5025: goto NO_PASSTHRU;
5026: }
5027:
5028: result = connect(new_node->passthru_socket, (struct sockaddr *)&tmp_addr, tmp_addr_len);
5029:
5030: if(result != 0) {
5031: lprintf(LOG_ERR,"!ERROR %d (%d) connecting to passthru socket", result, ERROR_VALUE);
5032: close_socket(new_node->passthru_socket);
5033: new_node->passthru_socket=INVALID_SOCKET;
5034: close_socket(tmp_sock);
5035: goto NO_PASSTHRU;
5036: }
5037:
5038: new_node->client_socket_dup=accept(tmp_sock, (struct sockaddr *)&tmp_addr, &tmp_addr_len);
5039:
5040: if(new_node->client_socket_dup == INVALID_SOCKET) {
5041: lprintf(LOG_ERR,"!ERROR (%d) connecting accept()ing on passthru socket", ERROR_VALUE);
5042: lprintf(LOG_WARNING,"!WARNING native doors which use sockets will not function");
5043: close_socket(new_node->passthru_socket);
5044: new_node->passthru_socket=INVALID_SOCKET;
5045: close_socket(tmp_sock);
5046: goto NO_PASSTHRU;
5047: }
5048: close_socket(tmp_sock);
5049: _beginthread(passthru_output_thread, 0, new_node);
5050: _beginthread(passthru_input_thread, 0, new_node);
5051:
5052: NO_PASSTHRU:
5053: new_node->connection="SSH";
5054: new_node->sys_status|=SS_SSH;
5055: new_node->telnet_mode|=TELNET_MODE_OFF; // SSH does not use Telnet commands
5056: new_node->ssh_session=sbbs->ssh_session;
5057: /* Wait for pending data to be sent then turn off ssh_mode for uber-output */
5058: while(RingBufFull(&sbbs->outbuf))
5059: SLEEP(1);
5060: sbbs->ssh_mode=false;
5061: }
5062: #endif
5063:
5064: node_threads_running++;
5065: new_node->input_thread=(HANDLE)_beginthread(input_thread,0, new_node);
5066: _beginthread(output_thread, 0, new_node);
5067: _beginthread(node_thread, 0, new_node);
5068: served++;
5069: }
5070:
5071: // Close all open sockets
5072: for(i=0;i<MAX_NODES;i++) {
5073: if(node_socket[i]!=INVALID_SOCKET) {
5074: lprintf(LOG_INFO,"Closing node %d socket %d", i+1, node_socket[i]);
5075: close_socket(node_socket[i]);
5076: node_socket[i]=INVALID_SOCKET;
5077: }
5078: #ifdef __unix__
5079: if(uspy_listen_socket[i]!=INVALID_SOCKET) {
5080: close_socket(uspy_listen_socket[i]);
5081: uspy_listen_socket[i]=INVALID_SOCKET;
5082: snprintf(str,sizeof(uspy_addr.sun_path),"%slocalspy%d.sock", startup->temp_dir, i+1);
5083: if(fexist(str))
5084: unlink(str);
5085: }
5086: if(uspy_socket[i]!=INVALID_SOCKET) {
5087: close_socket(uspy_socket[i]);
5088: uspy_socket[i]=INVALID_SOCKET;
5089: }
5090: #endif
5091: }
5092:
5093: sbbs->client_socket=INVALID_SOCKET;
5094: if(events!=NULL)
5095: events->terminated=true;
5096: // Wake-up BBS output thread so it can terminate
5097: sem_post(&sbbs->outbuf.sem);
5098:
5099: // Wait for all node threads to terminate
5100: if(node_threads_running) {
5101: lprintf(LOG_INFO,"Waiting for %d node threads to terminate...", node_threads_running);
5102: start=time(NULL);
5103: while(node_threads_running) {
5104: if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
5105: lprintf(LOG_ERR,"!TIMEOUT waiting for %d node thread(s) to "
5106: "terminate", node_threads_running);
5107: break;
5108: }
5109: mswait(100);
5110: }
5111: }
5112:
5113: // Wait for Events thread to terminate
5114: if(events!=NULL && events->event_thread_running) {
5115: lprintf(LOG_INFO,"Waiting for event thread to terminate...");
5116: start=time(NULL);
5117: while(events->event_thread_running) {
5118: #if 0 /* the event thread can/will segfault if it continues to run and dereference sbbs->cfg */
5119: if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
5120: lprintf(LOG_ERR,"!TIMEOUT waiting for BBS event thread to "
5121: "terminate");
5122: break;
5123: }
5124: #endif
5125: mswait(100);
5126: }
5127: }
5128:
5129: // Wait for BBS output thread to terminate
5130: if(sbbs->output_thread_running) {
5131: lprintf(LOG_INFO,"Waiting for system output thread to terminate...");
5132: start=time(NULL);
5133: while(sbbs->output_thread_running) {
5134: if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
5135: lprintf(LOG_ERR,"!TIMEOUT waiting for BBS output thread to "
5136: "terminate");
5137: break;
5138: }
5139: mswait(100);
5140: }
5141: }
5142:
5143: // Set all nodes' status to OFFLINE
5144: for(i=first_node;i<=last_node;i++) {
5145: sbbs->getnodedat(i,&node,1);
5146: node.status=NODE_OFFLINE;
5147: sbbs->putnodedat(i,&node);
5148: }
5149:
5150: if(events!=NULL && !events->event_thread_running)
5151: delete events;
5152:
5153: if(!sbbs->output_thread_running)
5154: delete sbbs;
5155:
5156: cleanup(0);
5157:
5158: if(!terminate_server) {
5159: lprintf(LOG_INFO,"Recycling server...");
5160: mswait(2000);
5161: if(startup->recycle!=NULL)
5162: startup->recycle(startup->cbdata);
5163: }
5164:
5165: } while(!terminate_server);
5166:
5167: }
5168:
5169:
5170:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.