|
|
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.