|
|
1.1 ! root 1: /* xtrn.cpp */ ! 2: ! 3: /* Synchronet external program support routines */ ! 4: ! 5: /* $Id: xtrn.cpp,v 1.199 2006/11/16 20:41:06 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 "cmdshell.h" ! 40: #include "telnet.h" ! 41: ! 42: #include <signal.h> // kill() ! 43: ! 44: #ifdef __unix__ ! 45: #include <sys/wait.h> // WEXITSTATUS ! 46: ! 47: #define TTYDEFCHARS // needed for ttydefchars definition ! 48: ! 49: #if defined(__FreeBSD__) ! 50: #include <libutil.h> // forkpty() ! 51: #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DARWIN__) ! 52: #include <util.h> ! 53: #elif defined(__linux__) ! 54: #include <pty.h> ! 55: #elif defined(__QNX__) ! 56: #if 0 ! 57: #include <unix.h> ! 58: #else ! 59: #define NEEDS_FORKPTY ! 60: #endif ! 61: #endif ! 62: ! 63: #ifdef NEEDS_FORKPTY ! 64: #include <grp.h> ! 65: #endif ! 66: ! 67: #include <termios.h> ! 68: ! 69: /* ! 70: * Control Character Defaults ! 71: */ ! 72: #ifndef CTRL ! 73: #define CTRL(x) (x&037) ! 74: #endif ! 75: #ifndef CEOF ! 76: #define CEOF CTRL('d') ! 77: #endif ! 78: #ifndef CEOL ! 79: #define CEOL 0xff /* XXX avoid _POSIX_VDISABLE */ ! 80: #endif ! 81: #ifndef CERASE ! 82: #define CERASE 0177 ! 83: #endif ! 84: #ifndef CERASE2 ! 85: #define CERASE2 CTRL('h') ! 86: #endif ! 87: #ifndef CINTR ! 88: #define CINTR CTRL('c') ! 89: #endif ! 90: #ifndef CSTATUS ! 91: #define CSTATUS CTRL('t') ! 92: #endif ! 93: #ifndef CKILL ! 94: #define CKILL CTRL('u') ! 95: #endif ! 96: #ifndef CMIN ! 97: #define CMIN 1 ! 98: #endif ! 99: #ifndef CQUIT ! 100: #define CQUIT 034 /* FS, ^\ */ ! 101: #endif ! 102: #ifndef CSUSP ! 103: #define CSUSP CTRL('z') ! 104: #endif ! 105: #ifndef CTIME ! 106: #define CTIME 0 ! 107: #endif ! 108: #ifndef CDSUSP ! 109: #define CDSUSP CTRL('y') ! 110: #endif ! 111: #ifndef CSTART ! 112: #define CSTART CTRL('q') ! 113: #endif ! 114: #ifndef CSTOP ! 115: #define CSTOP CTRL('s') ! 116: #endif ! 117: #ifndef CLNEXT ! 118: #define CLNEXT CTRL('v') ! 119: #endif ! 120: #ifndef CDISCARD ! 121: #define CDISCARD CTRL('o') ! 122: #endif ! 123: #ifndef CWERASE ! 124: #define CWERASE CTRL('w') ! 125: #endif ! 126: #ifndef CREPRINT ! 127: #define CREPRINT CTRL('r') ! 128: #endif ! 129: #ifndef CEOT ! 130: #define CEOT CEOF ! 131: #endif ! 132: /* compat */ ! 133: #ifndef CBRK ! 134: #define CBRK CEOL ! 135: #endif ! 136: #ifndef CRPRNT ! 137: #define CRPRNT CREPRINT ! 138: #endif ! 139: #ifndef CFLUSH ! 140: #define CFLUSH CDISCARD ! 141: #endif ! 142: ! 143: #ifndef TTYDEF_IFLAG ! 144: #define TTYDEF_IFLAG (BRKINT | ICRNL | IMAXBEL | IXON | IXANY) ! 145: #endif ! 146: #ifndef TTYDEF_OFLAG ! 147: #define TTYDEF_OFLAG (OPOST | ONLCR) ! 148: #endif ! 149: #ifndef TTYDEF_LFLAG ! 150: #define TTYDEF_LFLAG (ECHO | ICANON | ISIG | IEXTEN | ECHOE|ECHOKE|ECHOCTL) ! 151: #endif ! 152: #ifndef TTYDEF_CFLAG ! 153: #define TTYDEF_CFLAG (CREAD | CS8 | HUPCL) ! 154: #endif ! 155: #if defined(__QNX__) || defined(__solaris__) || defined(__NetBSD__) ! 156: static cc_t ttydefchars[NCCS] = { ! 157: CEOF, CEOL, CEOL, CERASE, CWERASE, CKILL, CREPRINT, ! 158: CERASE2, CINTR, CQUIT, CSUSP, CDSUSP, CSTART, CSTOP, CLNEXT, ! 159: CDISCARD, CMIN, CTIME, CSTATUS ! 160: #ifndef __solaris__ ! 161: , _POSIX_VDISABLE ! 162: #endif ! 163: }; ! 164: #endif ! 165: ! 166: #endif /* __unix__ */ ! 167: ! 168: #define XTRN_IO_BUF_LEN 10000 /* 50% of IO_THREAD_BUF_SIZE */ ! 169: ! 170: /*****************************************************************************/ ! 171: /* Interrupt routine to expand WWIV Ctrl-C# codes into ANSI escape sequences */ ! 172: /*****************************************************************************/ ! 173: BYTE* wwiv_expand(BYTE* buf, ulong buflen, BYTE* outbuf, ulong& newlen ! 174: ,ulong user_misc, bool& ctrl_c) ! 175: { ! 176: char ansi_seq[32]; ! 177: ulong i,j,k; ! 178: ! 179: for(i=j=0;i<buflen;i++) { ! 180: if(buf[i]==CTRL_C) { /* WWIV color escape char */ ! 181: ctrl_c=true; ! 182: continue; ! 183: } ! 184: if(!ctrl_c) { ! 185: outbuf[j++]=buf[i]; ! 186: continue; ! 187: } ! 188: ctrl_c=false; ! 189: if(user_misc&ANSI) { ! 190: switch(buf[i]) { ! 191: default: ! 192: strcpy(ansi_seq,"\x1b[0m"); /* low grey */ ! 193: break; ! 194: case '1': ! 195: strcpy(ansi_seq,"\x1b[0;1;36m"); /* high cyan */ ! 196: break; ! 197: case '2': ! 198: strcpy(ansi_seq,"\x1b[0;1;33m"); /* high yellow */ ! 199: break; ! 200: case '3': ! 201: strcpy(ansi_seq,"\x1b[0;35m"); /* low magenta */ ! 202: break; ! 203: case '4': ! 204: strcpy(ansi_seq,"\x1b[0;1;44m"); /* white on blue */ ! 205: break; ! 206: case '5': ! 207: strcpy(ansi_seq,"\x1b[0;32m"); /* low green */ ! 208: break; ! 209: case '6': ! 210: strcpy(ansi_seq,"\x1b[0;1;5;31m"); /* high blinking red */ ! 211: break; ! 212: case '7': ! 213: strcpy(ansi_seq,"\x1b[0;1;34m"); /* high blue */ ! 214: break; ! 215: case '8': ! 216: strcpy(ansi_seq,"\x1b[0;34m"); /* low blue */ ! 217: break; ! 218: case '9': ! 219: strcpy(ansi_seq,"\x1b[0;36m"); /* low cyan */ ! 220: break; ! 221: } ! 222: for(k=0;ansi_seq[k];k++) ! 223: outbuf[j++]=ansi_seq[k]; ! 224: } ! 225: } ! 226: newlen=j; ! 227: return(outbuf); ! 228: } ! 229: ! 230: /*****************************************************************************/ ! 231: // Escapes Telnet IAC (255) by doubling the IAC char ! 232: /*****************************************************************************/ ! 233: BYTE* telnet_expand(BYTE* inbuf, ulong inlen, BYTE* outbuf, ulong& newlen) ! 234: { ! 235: BYTE* first_iac; ! 236: ulong i,outlen; ! 237: ! 238: first_iac=(BYTE*)memchr(inbuf, TELNET_IAC, inlen); ! 239: ! 240: if(first_iac==NULL) { /* Nothing to expand */ ! 241: newlen=inlen; ! 242: return(inbuf); ! 243: } ! 244: ! 245: outlen=first_iac-inbuf; ! 246: memcpy(outbuf, inbuf, outlen); ! 247: ! 248: for(i=outlen;i<inlen;i++) { ! 249: if(inbuf[i]==TELNET_IAC) ! 250: outbuf[outlen++]=TELNET_IAC; ! 251: outbuf[outlen++]=inbuf[i]; ! 252: } ! 253: newlen=outlen; ! 254: return(outbuf); ! 255: } ! 256: ! 257: static bool native_executable(scfg_t* cfg, const char* cmdline, long mode) ! 258: { ! 259: char* p; ! 260: char str[MAX_PATH+1]; ! 261: char name[64]; ! 262: char base[64]; ! 263: unsigned i; ! 264: ! 265: if(mode&EX_NATIVE) ! 266: return(TRUE); ! 267: ! 268: SAFECOPY(str,cmdline); /* Set str to program name only */ ! 269: truncstr(str," "); ! 270: SAFECOPY(name,getfname(str)); ! 271: SAFECOPY(base,name); ! 272: if((p=getfext(base))!=NULL) ! 273: *p=0; ! 274: ! 275: for(i=0;i<cfg->total_natvpgms;i++) ! 276: if(stricmp(name,cfg->natvpgm[i]->name)==0 ! 277: || stricmp(base,cfg->natvpgm[i]->name)==0) ! 278: break; ! 279: return(i<cfg->total_natvpgms); ! 280: } ! 281: ! 282: #define XTRN_LOADABLE_MODULE \ ! 283: if(cmdline[0]=='*') /* Baja module or JavaScript */ \ ! 284: return(exec_bin(cmdline+1,&main_csi)) ! 285: #ifdef JAVASCRIPT ! 286: #define XTRN_LOADABLE_JS_MODULE \ ! 287: if(cmdline[0]=='?') /* JavaScript */ \ ! 288: return(js_execfile(cmdline+1)) ! 289: #else ! 290: #define XTRN_LOADABLE_JS_MODULE ! 291: #endif ! 292: ! 293: #ifdef _WIN32 ! 294: ! 295: #include "execvxd.h" /* Win9X FOSSIL VxD API */ ! 296: ! 297: extern SOCKET node_socket[]; ! 298: ! 299: // ------------------------------------------------------------------------- ! 300: // GetAddressOfOpenVxDHandle ! 301: // ! 302: // This function returns the address of OpenVxDHandle. OpenVxDHandle is a ! 303: // KERNEL32 function that returns a ring 0 event handle that corresponds to a ! 304: // given ring 3 event handle. The ring 0 handle can be used by VxDs to ! 305: // synchronize with the Win32 app. ! 306: // ! 307: typedef HANDLE (WINAPI *OPENVXDHANDLE)(HANDLE); ! 308: ! 309: OPENVXDHANDLE GetAddressOfOpenVxDHandle(void) ! 310: { ! 311: return((OPENVXDHANDLE)GetProcAddress(hK32, "OpenVxDHandle")); ! 312: } ! 313: ! 314: /*****************************************************************************/ ! 315: // Expands Single CR to CRLF ! 316: /*****************************************************************************/ ! 317: BYTE* cr_expand(BYTE* inbuf, ulong inlen, BYTE* outbuf, ulong& newlen) ! 318: { ! 319: ulong i,j; ! 320: ! 321: for(i=j=0;i<inlen;i++) { ! 322: outbuf[j++]=inbuf[i]; ! 323: if(inbuf[i]=='\r') ! 324: outbuf[j++]='\n'; ! 325: } ! 326: newlen=j; ! 327: return(outbuf); ! 328: } ! 329: ! 330: static void add_env_var(str_list_t* list, const char* var, const char* val) ! 331: { ! 332: char str[MAX_PATH*2]; ! 333: SetEnvironmentVariable(var,NULL); /* Delete in current process env */ ! 334: SAFEPRINTF2(str,"%s=%s",var,val); ! 335: strListPush(list,str); ! 336: } ! 337: ! 338: /* Clean-up resources while preserving current LastError value */ ! 339: #define XTRN_CLEANUP \ ! 340: last_error=GetLastError(); \ ! 341: if(vxd!=INVALID_HANDLE_VALUE) CloseHandle(vxd); \ ! 342: if(rdslot!=INVALID_HANDLE_VALUE) CloseHandle(rdslot); \ ! 343: if(wrslot!=INVALID_HANDLE_VALUE) CloseHandle(wrslot); \ ! 344: if(start_event!=NULL) CloseHandle(start_event); \ ! 345: if(hungup_event!=NULL) CloseHandle(hungup_event); \ ! 346: if(hangup_event!=NULL) CloseHandle(hangup_event); \ ! 347: ReleaseMutex(exec_mutex); \ ! 348: SetLastError(last_error) ! 349: ! 350: /****************************************************************************/ ! 351: /* Runs an external program */ ! 352: /****************************************************************************/ ! 353: int sbbs_t::external(const char* cmdline, long mode, const char* startup_dir) ! 354: { ! 355: char str[MAX_PATH+1]; ! 356: char* env_block=NULL; ! 357: char* env_strings; ! 358: const char* p_startup_dir; ! 359: char path[MAX_PATH+1]; ! 360: char fullcmdline[MAX_PATH+1]; ! 361: char realcmdline[MAX_PATH+1]; ! 362: char comspec_str[MAX_PATH+1]; ! 363: char title[MAX_PATH+1]; ! 364: BYTE buf[XTRN_IO_BUF_LEN],*bp; ! 365: BYTE telnet_buf[XTRN_IO_BUF_LEN*2]; ! 366: BYTE output_buf[XTRN_IO_BUF_LEN*2]; ! 367: BYTE wwiv_buf[XTRN_IO_BUF_LEN*2]; ! 368: bool wwiv_flag=false; ! 369: bool native=false; // DOS program by default ! 370: bool nt=false; // WinNT/2K? ! 371: bool was_online=true; ! 372: bool rio_abortable_save=rio_abortable; ! 373: bool use_pipes=false; // NT-compatible console redirection ! 374: BOOL success; ! 375: BOOL processTerminated=false; ! 376: uint i; ! 377: time_t hungup=0; ! 378: HANDLE vxd=INVALID_HANDLE_VALUE; ! 379: HANDLE rdslot=INVALID_HANDLE_VALUE; ! 380: HANDLE wrslot=INVALID_HANDLE_VALUE; ! 381: HANDLE start_event=NULL; ! 382: HANDLE hungup_event=NULL; ! 383: HANDLE hangup_event=NULL; ! 384: HANDLE rdoutpipe; ! 385: HANDLE wrinpipe; ! 386: PROCESS_INFORMATION process_info; ! 387: DWORD hVM; ! 388: DWORD rd; ! 389: DWORD wr; ! 390: DWORD len; ! 391: DWORD avail; ! 392: DWORD dummy; ! 393: DWORD msglen; ! 394: DWORD retval; ! 395: DWORD last_error; ! 396: DWORD loop_since_io=0; ! 397: struct tm tm; ! 398: str_list_t env_list; ! 399: sbbsexec_start_t start; ! 400: OPENVXDHANDLE OpenVxDHandle; ! 401: ! 402: if(online==ON_LOCAL) ! 403: eprintf(LOG_INFO,"Executing external: %s",cmdline); ! 404: else ! 405: lprintf(LOG_INFO,"Node %d Executing external: %s",cfg.node_num,cmdline); ! 406: ! 407: XTRN_LOADABLE_MODULE; ! 408: XTRN_LOADABLE_JS_MODULE; ! 409: ! 410: attr(cfg.color[clr_external]); /* setup default attributes */ ! 411: ! 412: native = native_executable(&cfg, cmdline, mode); ! 413: ! 414: if(mode&EX_SH || strcspn(cmdline,"<>|")!=strlen(cmdline)) ! 415: sprintf(comspec_str,"%s /C ", comspec); ! 416: else ! 417: comspec_str[0]=0; ! 418: ! 419: if(startup_dir && cmdline[1]!=':' && cmdline[0]!='/' ! 420: && cmdline[0]!='\\' && cmdline[0]!='.') ! 421: sprintf(fullcmdline, "%s%s%s", comspec_str, startup_dir, cmdline); ! 422: else ! 423: sprintf(fullcmdline, "%s%s", comspec_str, cmdline); ! 424: ! 425: SAFECOPY(realcmdline, fullcmdline); // for errormsg if failed to execute ! 426: ! 427: now=time(NULL); ! 428: if(localtime_r(&now,&tm)==NULL) ! 429: memset(&tm,0,sizeof(tm)); ! 430: ! 431: OpenVxDHandle=GetAddressOfOpenVxDHandle(); ! 432: ! 433: if(OpenVxDHandle==NULL) ! 434: nt=true; // Windows NT/2000 ! 435: ! 436: if(!nt && !native && !(cfg.xtrn_misc&XTRN_NO_MUTEX) ! 437: && (retval=WaitForSingleObject(exec_mutex,5000))!=WAIT_OBJECT_0) { ! 438: errormsg(WHERE, ERR_TIMEOUT, "exec_mutex", retval); ! 439: return(GetLastError()); ! 440: } ! 441: ! 442: if(native && mode&EX_OUTR && !(mode&EX_OFFLINE)) ! 443: use_pipes=true; ! 444: ! 445: if(native) { // Native (32-bit) external ! 446: ! 447: if((env_list=strListInit())==NULL) { ! 448: XTRN_CLEANUP; ! 449: errormsg(WHERE, ERR_CREATE, "env_list", 0); ! 450: return(errno); ! 451: } ! 452: ! 453: // Current environment passed to child process ! 454: sprintf(str,"%sprotocol.log",cfg.node_dir); ! 455: add_env_var(&env_list,"DSZLOG",str); ! 456: add_env_var(&env_list,"SBBSNODE",cfg.node_dir); ! 457: add_env_var(&env_list,"SBBSCTRL",cfg.ctrl_dir); ! 458: add_env_var(&env_list,"SBBSDATA",cfg.data_dir); ! 459: add_env_var(&env_list,"SBBSEXEC",cfg.exec_dir); ! 460: sprintf(str,"%d",cfg.node_num); ! 461: add_env_var(&env_list,"SBBSNNUM",str); ! 462: /* date/time env vars */ ! 463: sprintf(str,"%02u",tm.tm_mday); ! 464: add_env_var(&env_list,"DAY",str); ! 465: add_env_var(&env_list,"WEEKDAY",wday[tm.tm_wday]); ! 466: add_env_var(&env_list,"MONTHNAME",mon[tm.tm_mon]); ! 467: sprintf(str,"%02u",tm.tm_mon+1); ! 468: add_env_var(&env_list,"MONTH",str); ! 469: sprintf(str,"%u",1900+tm.tm_year); ! 470: add_env_var(&env_list,"YEAR",str); ! 471: ! 472: env_strings=GetEnvironmentStrings(); ! 473: env_block=strListCopyBlock(env_strings); ! 474: if(env_strings!=NULL) ! 475: FreeEnvironmentStrings(env_strings); ! 476: env_block=strListAppendBlock(env_block,env_list); ! 477: strListFree(&env_list); ! 478: if(env_block==NULL) { ! 479: XTRN_CLEANUP; ! 480: errormsg(WHERE, ERR_CREATE, "env_block", 0); ! 481: return(errno); ! 482: } ! 483: ! 484: } else { // DOS external ! 485: ! 486: // DOS-compatible (short) paths ! 487: char node_dir[MAX_PATH+1]; ! 488: char ctrl_dir[MAX_PATH+1]; ! 489: char data_dir[MAX_PATH+1]; ! 490: char exec_dir[MAX_PATH+1]; ! 491: ! 492: // in case GetShortPathName fails ! 493: SAFECOPY(node_dir,cfg.node_dir); ! 494: SAFECOPY(ctrl_dir,cfg.ctrl_dir); ! 495: SAFECOPY(data_dir,cfg.data_dir); ! 496: SAFECOPY(exec_dir,cfg.exec_dir); ! 497: ! 498: GetShortPathName(cfg.node_dir,node_dir,sizeof(node_dir)); ! 499: GetShortPathName(cfg.ctrl_dir,ctrl_dir,sizeof(node_dir)); ! 500: GetShortPathName(cfg.data_dir,data_dir,sizeof(data_dir)); ! 501: GetShortPathName(cfg.exec_dir,exec_dir,sizeof(exec_dir)); ! 502: ! 503: sprintf(path,"%sDOSXTRN.RET", cfg.node_dir); ! 504: remove(path); ! 505: ! 506: // Create temporary environment file ! 507: sprintf(path,"%sDOSXTRN.ENV", node_dir); ! 508: FILE* fp=fopen(path,"w"); ! 509: if(fp==NULL) { ! 510: XTRN_CLEANUP; ! 511: errormsg(WHERE, ERR_CREATE, path, 0); ! 512: return(errno); ! 513: } ! 514: fprintf(fp, "%s\n", fullcmdline); ! 515: fprintf(fp, "DSZLOG=%sPROTOCOL.LOG\n", node_dir); ! 516: fprintf(fp, "SBBSNODE=%s\n", node_dir); ! 517: fprintf(fp, "SBBSCTRL=%s\n", ctrl_dir); ! 518: fprintf(fp, "SBBSDATA=%s\n", data_dir); ! 519: fprintf(fp, "SBBSEXEC=%s\n", exec_dir); ! 520: fprintf(fp, "SBBSNNUM=%d\n", cfg.node_num); ! 521: /* date/time env vars */ ! 522: fprintf(fp, "DAY=%02u\n", tm.tm_mday); ! 523: fprintf(fp, "WEEKDAY=%s\n",wday[tm.tm_wday]); ! 524: fprintf(fp, "MONTHNAME=%s\n",mon[tm.tm_mon]); ! 525: fprintf(fp, "MONTH=%02u\n",tm.tm_mon+1); ! 526: fprintf(fp, "YEAR=%u\n",1900+tm.tm_year); ! 527: fclose(fp); ! 528: ! 529: sprintf(fullcmdline, "%sDOSXTRN.EXE %s", cfg.exec_dir, path); ! 530: ! 531: if(!(mode&EX_OFFLINE) && nt) { // Windows NT/2000 ! 532: i=SBBSEXEC_MODE_FOSSIL; ! 533: if(mode&EX_INR) ! 534: i|=SBBSEXEC_MODE_DOS_IN; ! 535: if(mode&EX_OUTR) ! 536: i|=SBBSEXEC_MODE_DOS_OUT; ! 537: sprintf(str," NT %u %u" ! 538: ,cfg.node_num,i); ! 539: strcat(fullcmdline,str); ! 540: ! 541: sprintf(str,"sbbsexec_hungup%d",cfg.node_num); ! 542: if((hungup_event=CreateEvent( ! 543: NULL // pointer to security attributes ! 544: ,TRUE // flag for manual-reset event ! 545: ,FALSE // flag for initial state ! 546: ,str // pointer to event-object name ! 547: ))==NULL) { ! 548: XTRN_CLEANUP; ! 549: errormsg(WHERE, ERR_CREATE, str, 0); ! 550: return(GetLastError()); ! 551: } ! 552: ! 553: sprintf(str,"sbbsexec_hangup%d",cfg.node_num); ! 554: if((hangup_event=CreateEvent( ! 555: NULL // pointer to security attributes ! 556: ,TRUE // flag for manual-reset event ! 557: ,FALSE // flag for initial state ! 558: ,str // pointer to event-object name ! 559: ))==NULL) { ! 560: XTRN_CLEANUP; ! 561: errormsg(WHERE, ERR_CREATE, str, 0); ! 562: return(GetLastError()); ! 563: } ! 564: ! 565: sprintf(str,"\\\\.\\mailslot\\sbbsexec\\rd%d" ! 566: ,cfg.node_num); ! 567: rdslot=CreateMailslot(str ! 568: ,sizeof(buf)/2 // Maximum message size (0=unlimited) ! 569: ,0 // Read time-out ! 570: ,NULL); // Security ! 571: if(rdslot==INVALID_HANDLE_VALUE) { ! 572: XTRN_CLEANUP; ! 573: errormsg(WHERE, ERR_CREATE, str, 0); ! 574: return(GetLastError()); ! 575: } ! 576: } ! 577: else if(!(mode&EX_OFFLINE)) { ! 578: ! 579: // Load vxd to intercept interrupts ! 580: ! 581: sprintf(str,"\\\\.\\%s%s",cfg.exec_dir, SBBSEXEC_VXD); ! 582: if((vxd=CreateFile(str,0,0,0 ! 583: ,CREATE_NEW, FILE_FLAG_DELETE_ON_CLOSE,0)) ! 584: ==INVALID_HANDLE_VALUE) { ! 585: XTRN_CLEANUP; ! 586: errormsg(WHERE, ERR_OPEN, str, 0); ! 587: return(GetLastError()); ! 588: } ! 589: ! 590: if((start_event=CreateEvent( ! 591: NULL // pointer to security attributes ! 592: ,TRUE // flag for manual-reset event ! 593: ,FALSE // flag for initial state ! 594: ,NULL // pointer to event-object name ! 595: ))==NULL) { ! 596: XTRN_CLEANUP; ! 597: errormsg(WHERE, ERR_CREATE, "exec start event", 0); ! 598: return(GetLastError()); ! 599: } ! 600: ! 601: if(OpenVxDHandle!=NULL) ! 602: start.event=OpenVxDHandle(start_event); ! 603: else ! 604: start.event=start_event; ! 605: ! 606: start.mode=SBBSEXEC_MODE_FOSSIL; ! 607: if(mode&EX_INR) ! 608: start.mode|=SBBSEXEC_MODE_DOS_IN; ! 609: if(mode&EX_OUTR) ! 610: start.mode|=SBBSEXEC_MODE_DOS_OUT; ! 611: ! 612: sprintf(str," 95 %u %u" ! 613: ,cfg.node_num,start.mode); ! 614: strcat(fullcmdline,str); ! 615: ! 616: if(!DeviceIoControl( ! 617: vxd, // handle to device of interest ! 618: SBBSEXEC_IOCTL_START, // control code of operation to perform ! 619: &start, // pointer to buffer to supply input data ! 620: sizeof(start), // size of input buffer ! 621: NULL, // pointer to buffer to receive output data ! 622: 0, // size of output buffer ! 623: &rd, // pointer to variable to receive output byte count ! 624: NULL // Overlapped I/O ! 625: )) { ! 626: XTRN_CLEANUP; ! 627: errormsg(WHERE, ERR_IOCTL, SBBSEXEC_VXD, SBBSEXEC_IOCTL_START); ! 628: return(GetLastError()); ! 629: } ! 630: } ! 631: } ! 632: ! 633: if(startup_dir!=NULL && startup_dir[0]) ! 634: p_startup_dir=startup_dir; ! 635: else ! 636: p_startup_dir=NULL; ! 637: STARTUPINFO startup_info={0}; ! 638: startup_info.cb=sizeof(startup_info); ! 639: if(mode&EX_OFFLINE) ! 640: startup_info.lpTitle=NULL; ! 641: else { ! 642: sprintf(title,"%s running %s on node %d" ! 643: ,useron.number ? useron.alias : "Event" ! 644: ,realcmdline ! 645: ,cfg.node_num); ! 646: startup_info.lpTitle=title; ! 647: } ! 648: if(startup->options&BBS_OPT_XTRN_MINIMIZED) { ! 649: startup_info.wShowWindow=SW_SHOWMINNOACTIVE; ! 650: startup_info.dwFlags|=STARTF_USESHOWWINDOW; ! 651: } ! 652: if(use_pipes) { ! 653: // Set up the security attributes struct. ! 654: SECURITY_ATTRIBUTES sa; ! 655: memset(&sa,0,sizeof(sa)); ! 656: sa.nLength= sizeof(SECURITY_ATTRIBUTES); ! 657: sa.lpSecurityDescriptor = NULL; ! 658: sa.bInheritHandle = TRUE; ! 659: ! 660: // Create the child output pipe (override default 4K buffer size) ! 661: if(!CreatePipe(&rdoutpipe,&startup_info.hStdOutput,&sa,sizeof(buf))) { ! 662: errormsg(WHERE,ERR_CREATE,"stdout pipe",0); ! 663: return(GetLastError()); ! 664: } ! 665: startup_info.hStdError=startup_info.hStdOutput; ! 666: ! 667: // Create the child input pipe. ! 668: if(!CreatePipe(&startup_info.hStdInput,&wrinpipe,&sa,sizeof(buf))) { ! 669: errormsg(WHERE,ERR_CREATE,"stdin pipe",0); ! 670: return(GetLastError()); ! 671: } ! 672: ! 673: DuplicateHandle( ! 674: GetCurrentProcess(), rdoutpipe, ! 675: GetCurrentProcess(), &rdslot, 0, FALSE, DUPLICATE_SAME_ACCESS); ! 676: ! 677: DuplicateHandle( ! 678: GetCurrentProcess(), wrinpipe, ! 679: GetCurrentProcess(), &wrslot, 0, FALSE, DUPLICATE_SAME_ACCESS); ! 680: ! 681: CloseHandle(rdoutpipe); ! 682: CloseHandle(wrinpipe); ! 683: ! 684: startup_info.dwFlags|=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; ! 685: startup_info.wShowWindow=SW_HIDE; ! 686: } ! 687: if(native && !(mode&EX_OFFLINE)) { ! 688: ! 689: if(!(mode&EX_INR) && input_thread_running) { ! 690: pthread_mutex_lock(&input_thread_mutex); ! 691: input_thread_mutex_locked=true; ! 692: } ! 693: ! 694: if(!(mode&EX_OUTR)) { /* Native Socket I/O program */ ! 695: /* Enable the Nagle algorithm */ ! 696: BOOL nodelay=FALSE; ! 697: setsockopt(client_socket,IPPROTO_TCP,TCP_NODELAY,(char*)&nodelay,sizeof(nodelay)); ! 698: } ! 699: } ! 700: ! 701: success=CreateProcess( ! 702: NULL, // pointer to name of executable module ! 703: fullcmdline, // pointer to command line string ! 704: NULL, // process security attributes ! 705: NULL, // thread security attributes ! 706: native && !(mode&EX_OFFLINE), // handle inheritance flag ! 707: CREATE_NEW_CONSOLE/*|CREATE_SEPARATE_WOW_VDM*/, // creation flags ! 708: env_block, // pointer to new environment block ! 709: p_startup_dir, // pointer to current directory name ! 710: &startup_info, // pointer to STARTUPINFO ! 711: &process_info // pointer to PROCESS_INFORMATION ! 712: ); ! 713: ! 714: strListFreeBlock(env_block); ! 715: ! 716: if(!success) { ! 717: XTRN_CLEANUP; ! 718: if(input_thread_mutex_locked && input_thread_running) { ! 719: pthread_mutex_unlock(&input_thread_mutex); ! 720: input_thread_mutex_locked=false; ! 721: } ! 722: SetLastError(last_error); /* Restore LastError */ ! 723: errormsg(WHERE, ERR_EXEC, realcmdline, mode); ! 724: return(GetLastError()); ! 725: } ! 726: ! 727: #if 0 ! 728: char dbgstr[256]; ! 729: sprintf(dbgstr,"Node %d created: hProcess %X hThread %X processID %X threadID %X\n" ! 730: ,cfg.node_num ! 731: ,process_info.hProcess ! 732: ,process_info.hThread ! 733: ,process_info.dwProcessId ! 734: ,process_info.dwThreadId); ! 735: OutputDebugString(dbgstr); ! 736: #endif ! 737: ! 738: CloseHandle(process_info.hThread); ! 739: ! 740: if(!native) { ! 741: ! 742: if(!(mode&EX_OFFLINE) && !nt) { ! 743: // Wait for notification from VXD that new VM has started ! 744: if((retval=WaitForSingleObject(start_event, 5000))!=WAIT_OBJECT_0) { ! 745: XTRN_CLEANUP; ! 746: TerminateProcess(process_info.hProcess, __LINE__); ! 747: CloseHandle(process_info.hProcess); ! 748: errormsg(WHERE, ERR_TIMEOUT, "start_event", retval); ! 749: return(GetLastError()); ! 750: } ! 751: ! 752: CloseHandle(start_event); ! 753: start_event=NULL; /* Mark as closed */ ! 754: ! 755: if(!DeviceIoControl( ! 756: vxd, // handle to device of interest ! 757: SBBSEXEC_IOCTL_COMPLETE, // control code of operation to perform ! 758: NULL, // pointer to buffer to supply input data ! 759: 0, // size of input buffer ! 760: &hVM, // pointer to buffer to receive output data ! 761: sizeof(hVM), // size of output buffer ! 762: &rd, // pointer to variable to receive output byte count ! 763: NULL // Overlapped I/O ! 764: )) { ! 765: XTRN_CLEANUP; ! 766: TerminateProcess(process_info.hProcess, __LINE__); ! 767: CloseHandle(process_info.hProcess); ! 768: errormsg(WHERE, ERR_IOCTL, SBBSEXEC_VXD, SBBSEXEC_IOCTL_COMPLETE); ! 769: return(GetLastError()); ! 770: } ! 771: } ! 772: } ! 773: ReleaseMutex(exec_mutex); ! 774: ! 775: /* Disable Ctrl-C checking */ ! 776: if(!(mode&EX_OFFLINE)) ! 777: rio_abortable=false; ! 778: ! 779: // Executing app in foreground?, monitor ! 780: retval=STILL_ACTIVE; ! 781: while(!(mode&EX_BG)) { ! 782: if(mode&EX_CHKTIME) ! 783: gettimeleft(); ! 784: if(!online && !(mode&EX_OFFLINE)) { // Tell VXD/VDD and external that user hung-up ! 785: if(was_online) { ! 786: sprintf(str,"%s hung-up in external program",useron.alias); ! 787: logline("X!",str); ! 788: hungup=time(NULL); ! 789: if(!native) { ! 790: if(nt) ! 791: SetEvent(hungup_event); ! 792: else if(!DeviceIoControl( ! 793: vxd, // handle to device of interest ! 794: SBBSEXEC_IOCTL_DISCONNECT, // operation to perform ! 795: &hVM, // pointer to buffer to supply input data ! 796: sizeof(hVM),// size of input buffer ! 797: NULL, // pointer to buffer to receive output data ! 798: 0, // size of output buffer ! 799: &rd, // pointer to variable to receive output byte count ! 800: NULL // Overlapped I/O ! 801: )) { ! 802: errormsg(WHERE, ERR_IOCTL, SBBSEXEC_VXD, SBBSEXEC_IOCTL_DISCONNECT); ! 803: break; ! 804: } ! 805: } ! 806: was_online=false; ! 807: } ! 808: if(hungup && time(NULL)-hungup>5 && !processTerminated) { ! 809: lprintf(LOG_INFO,"Node %d Terminating process from line %d",cfg.node_num,__LINE__); ! 810: processTerminated=TerminateProcess(process_info.hProcess, 2112); ! 811: } ! 812: } ! 813: if((native && !use_pipes) || mode&EX_OFFLINE) { ! 814: /* Monitor for process termination only */ ! 815: if(WaitForSingleObject(process_info.hProcess,1000)==WAIT_OBJECT_0) ! 816: break; ! 817: } else { ! 818: ! 819: if(nt || use_pipes) { // Windows NT/2000 ! 820: ! 821: /* Write to VDD */ ! 822: ! 823: wr=RingBufPeek(&inbuf,buf,sizeof(buf)); ! 824: if(wr) { ! 825: if(!use_pipes && wrslot==INVALID_HANDLE_VALUE) { ! 826: sprintf(str,"\\\\.\\mailslot\\sbbsexec\\wr%d" ! 827: ,cfg.node_num); ! 828: wrslot=CreateFile(str ! 829: ,GENERIC_WRITE ! 830: ,FILE_SHARE_READ ! 831: ,NULL ! 832: ,OPEN_EXISTING ! 833: ,FILE_ATTRIBUTE_NORMAL ! 834: ,(HANDLE) NULL); ! 835: #if 0 ! 836: if(wrslot==INVALID_HANDLE_VALUE) { ! 837: errormsg(WHERE,ERR_OPEN,str,0); ! 838: break; ! 839: } ! 840: #endif ! 841: } ! 842: ! 843: /* CR expansion */ ! 844: if(use_pipes) ! 845: bp=cr_expand(buf,wr,output_buf,wr); ! 846: else ! 847: bp=buf; ! 848: ! 849: if(wrslot!=INVALID_HANDLE_VALUE ! 850: && WriteFile(wrslot,bp,wr,&len,NULL)==TRUE) { ! 851: RingBufRead(&inbuf, NULL, len); ! 852: wr=len; ! 853: if(use_pipes && !(mode&EX_NOECHO)) { ! 854: /* echo */ ! 855: RingBufWrite(&outbuf, bp, wr); ! 856: } ! 857: } else // VDD not loaded yet ! 858: wr=0; ! 859: } ! 860: ! 861: /* Read from VDD */ ! 862: ! 863: rd=0; ! 864: len=sizeof(buf); ! 865: avail=RingBufFree(&outbuf)/2; // leave room for wwiv/telnet expansion ! 866: #if 0 ! 867: if(avail==0) ! 868: lprintf("Node %d !output buffer full (%u bytes)" ! 869: ,cfg.node_num,RingBufFull(&outbuf)); ! 870: #endif ! 871: if(len>avail) ! 872: len=avail; ! 873: ! 874: while(rd<len) { ! 875: DWORD waiting=0; ! 876: ! 877: if(use_pipes) ! 878: PeekNamedPipe( ! 879: rdslot, // handle to pipe to copy from ! 880: NULL, // pointer to data buffer ! 881: 0, // size, in bytes, of data buffer ! 882: NULL, // pointer to number of bytes read ! 883: &waiting, // pointer to total number of bytes available ! 884: NULL // pointer to unread bytes in this message ! 885: ); ! 886: else ! 887: GetMailslotInfo( ! 888: rdslot, // mailslot handle ! 889: NULL, // address of maximum message size ! 890: NULL, // address of size of next message ! 891: &waiting, // address of number of messages ! 892: NULL // address of read time-out ! 893: ); ! 894: if(!waiting) ! 895: break; ! 896: if(ReadFile(rdslot,buf+rd,len-rd,&msglen,NULL)==FALSE || msglen<1) ! 897: break; ! 898: rd+=msglen; ! 899: } ! 900: ! 901: if(rd) { ! 902: if(mode&EX_WWIV) { ! 903: bp=wwiv_expand(buf, rd, wwiv_buf, rd, useron.misc, wwiv_flag); ! 904: if(rd>sizeof(wwiv_buf)) ! 905: errorlog("WWIV_BUF OVERRUN"); ! 906: } else if(telnet_mode&TELNET_MODE_OFF) { ! 907: bp=buf; ! 908: } else { ! 909: bp=telnet_expand(buf, rd, telnet_buf, rd); ! 910: if(rd>sizeof(telnet_buf)) ! 911: errorlog("TELNET_BUF OVERRUN"); ! 912: } ! 913: if(rd>RingBufFree(&outbuf)) { ! 914: errorlog("output buffer overflow"); ! 915: rd=RingBufFree(&outbuf); ! 916: } ! 917: RingBufWrite(&outbuf, bp, rd); ! 918: } ! 919: } else { // Windows 9x ! 920: ! 921: /* Write to VXD */ ! 922: ! 923: wr=RingBufPeek(&inbuf, buf+sizeof(hVM),sizeof(buf)-sizeof(hVM)); ! 924: if(wr) { ! 925: *(DWORD*)buf=hVM; ! 926: wr+=sizeof(hVM); ! 927: if(!DeviceIoControl( ! 928: vxd, // handle to device of interest ! 929: SBBSEXEC_IOCTL_WRITE, // control code of operation to perform ! 930: buf, // pointer to buffer to supply input data ! 931: wr, // size of input buffer ! 932: &rd, // pointer to buffer to receive output data ! 933: sizeof(rd), // size of output buffer ! 934: &dummy, // pointer to variable to receive output byte count ! 935: NULL // Overlapped I/O ! 936: )) { ! 937: errormsg(WHERE, ERR_IOCTL, SBBSEXEC_VXD, SBBSEXEC_IOCTL_READ); ! 938: break; ! 939: } ! 940: RingBufRead(&inbuf, NULL, rd); ! 941: wr=rd; ! 942: } ! 943: /* Read from VXD */ ! 944: rd=0; ! 945: len=sizeof(buf); ! 946: avail=RingBufFree(&outbuf)/2; // leave room for wwiv/telnet expansion ! 947: #if 0 ! 948: if(avail==0) ! 949: lprintf("Node %d !output buffer full (%u bytes)" ! 950: ,cfg.node_num,RingBufFull(&outbuf)); ! 951: #endif ! 952: ! 953: if(len>avail) ! 954: len=avail; ! 955: if(len) { ! 956: if(!DeviceIoControl( ! 957: vxd, // handle to device of interest ! 958: SBBSEXEC_IOCTL_READ, // control code of operation to perform ! 959: &hVM, // pointer to buffer to supply input data ! 960: sizeof(hVM), // size of input buffer ! 961: buf, // pointer to buffer to receive output data ! 962: len, // size of output buffer ! 963: &rd, // pointer to variable to receive output byte count ! 964: NULL // Overlapped I/O ! 965: )) { ! 966: errormsg(WHERE, ERR_IOCTL, SBBSEXEC_VXD, SBBSEXEC_IOCTL_READ); ! 967: break; ! 968: } ! 969: if(mode&EX_WWIV) { ! 970: bp=wwiv_expand(buf, rd, wwiv_buf, rd, useron.misc, wwiv_flag); ! 971: if(rd>sizeof(wwiv_buf)) ! 972: errorlog("WWIV_BUF OVERRUN"); ! 973: } else if(telnet_mode&TELNET_MODE_OFF) { ! 974: bp=buf; ! 975: } else { ! 976: bp=telnet_expand(buf, rd, telnet_buf, rd); ! 977: if(rd>sizeof(telnet_buf)) ! 978: errorlog("TELNET_BUF OVERRUN"); ! 979: } ! 980: if(rd>RingBufFree(&outbuf)) { ! 981: errorlog("output buffer overflow"); ! 982: rd=RingBufFree(&outbuf); ! 983: } ! 984: RingBufWrite(&outbuf, bp, rd); ! 985: } ! 986: } ! 987: #if defined(_DEBUG) && 0 ! 988: if(rd>1) { ! 989: sprintf(str,"Node %d read %5d bytes from xtrn", cfg.node_num, rd); ! 990: OutputDebugString(str); ! 991: } ! 992: #endif ! 993: if((!rd && !wr) || hungup) { ! 994: ! 995: loop_since_io++; /* number of loop iterations with no I/O */ ! 996: ! 997: /* only check process termination after 300 milliseconds of no I/O */ ! 998: /* to allow for last minute reception of output from DOS programs */ ! 999: if(loop_since_io>=3) { ! 1000: ! 1001: if(online && hangup_event!=NULL ! 1002: && WaitForSingleObject(hangup_event,0)==WAIT_OBJECT_0) { ! 1003: lprintf(LOG_NOTICE,"Node %d External program requested hangup (dropped DTR)" ! 1004: ,cfg.node_num); ! 1005: hangup(); ! 1006: } ! 1007: ! 1008: if(WaitForSingleObject(process_info.hProcess,0)==WAIT_OBJECT_0) ! 1009: break; /* Process Terminated */ ! 1010: } ! 1011: ! 1012: /* only check node for interrupt flag every 3 seconds of no I/O */ ! 1013: if((loop_since_io%30)==0) { ! 1014: // Check if the node has been interrupted ! 1015: getnodedat(cfg.node_num,&thisnode,0); ! 1016: if(thisnode.misc&NODE_INTR) ! 1017: break; ! 1018: } ! 1019: ! 1020: /* only send telnet GA every 30 seconds of no I/O */ ! 1021: if((loop_since_io%300)==0) { ! 1022: #if defined(_DEBUG) ! 1023: sprintf(str,"Node %d xtrn idle\n",cfg.node_num); ! 1024: OutputDebugString(str); ! 1025: #endif ! 1026: // Let's make sure the socket is up ! 1027: // Sending will trigger a socket d/c detection ! 1028: if(!(startup->options&BBS_OPT_NO_TELNET_GA)) ! 1029: send_telnet_cmd(TELNET_GA,0); ! 1030: } ! 1031: sem_trywait_block(&inbuf.sem,100); ! 1032: } else ! 1033: loop_since_io=0; ! 1034: } ! 1035: } ! 1036: ! 1037: if(!native && !(mode&EX_OFFLINE) && !nt) { ! 1038: if(!DeviceIoControl( ! 1039: vxd, // handle to device of interest ! 1040: SBBSEXEC_IOCTL_STOP, // control code of operation to perform ! 1041: &hVM, // pointer to buffer to supply input data ! 1042: sizeof(hVM), // size of input buffer ! 1043: NULL, // pointer to buffer to receive output data ! 1044: 0, // size of output buffer ! 1045: &rd, // pointer to variable to receive output byte count ! 1046: NULL // Overlapped I/O ! 1047: )) { ! 1048: errormsg(WHERE, ERR_IOCTL, SBBSEXEC_VXD, SBBSEXEC_IOCTL_STOP); ! 1049: } ! 1050: } ! 1051: ! 1052: if(!(mode&EX_BG)) { /* !background execution */ ! 1053: ! 1054: if(GetExitCodeProcess(process_info.hProcess, &retval)==FALSE) ! 1055: errormsg(WHERE, ERR_CHK, "ExitCodeProcess",(DWORD)process_info.hProcess); ! 1056: ! 1057: if(retval==STILL_ACTIVE) { ! 1058: lprintf(LOG_INFO,"Node %d Terminating process from line %d",cfg.node_num,__LINE__); ! 1059: TerminateProcess(process_info.hProcess, GetLastError()); ! 1060: } ! 1061: ! 1062: // Get return value ! 1063: if(!native) { ! 1064: sprintf(str,"%sDOSXTRN.RET", cfg.node_dir); ! 1065: FILE* fp=fopen(str,"r"); ! 1066: if(fp!=NULL) { ! 1067: fscanf(fp,"%d",&retval); ! 1068: fclose(fp); ! 1069: } ! 1070: } ! 1071: } ! 1072: ! 1073: XTRN_CLEANUP; ! 1074: CloseHandle(process_info.hProcess); ! 1075: ! 1076: if(!(mode&EX_OFFLINE)) { /* !off-line execution */ ! 1077: ! 1078: if(native) { ! 1079: ! 1080: /* Re-enable blocking (incase disabled by xtrn program) */ ! 1081: ulong l=0; ! 1082: ioctlsocket(client_socket, FIONBIO, &l); ! 1083: ! 1084: /* Re-set socket options */ ! 1085: if(set_socket_options(&cfg, client_socket, client.protocol, str, sizeof(str))) ! 1086: lprintf(LOG_ERR,"%04d !ERROR %s",client_socket, str); ! 1087: ! 1088: if(input_thread_mutex_locked && input_thread_running) { ! 1089: pthread_mutex_unlock(&input_thread_mutex); ! 1090: input_thread_mutex_locked=false; ! 1091: } ! 1092: } ! 1093: ! 1094: curatr=~0; // Can't guarantee current attributes ! 1095: attr(LIGHTGRAY); // Force to "normal" ! 1096: ! 1097: rio_abortable=rio_abortable_save; // Restore abortable state ! 1098: ! 1099: /* Got back to Text/NVT mode */ ! 1100: request_telnet_opt(TELNET_DONT,TELNET_BINARY_TX); ! 1101: } ! 1102: ! 1103: // lprintf("%s returned %d",realcmdline, retval); ! 1104: ! 1105: errorlevel = retval; // Baja or JS retrievable error value ! 1106: ! 1107: return(retval); ! 1108: } ! 1109: ! 1110: #else /* !WIN32 */ ! 1111: ! 1112: /*****************************************************************************/ ! 1113: // Expands Unix LF to CRLF ! 1114: /*****************************************************************************/ ! 1115: BYTE* lf_expand(BYTE* inbuf, ulong inlen, BYTE* outbuf, ulong& newlen) ! 1116: { ! 1117: ulong i,j; ! 1118: ! 1119: for(i=j=0;i<inlen;i++) { ! 1120: if(inbuf[i]=='\n' && (!i || inbuf[i-1]!='\r')) ! 1121: outbuf[j++]='\r'; ! 1122: outbuf[j++]=inbuf[i]; ! 1123: } ! 1124: newlen=j; ! 1125: return(outbuf); ! 1126: } ! 1127: ! 1128: #define MAX_ARGS 1000 ! 1129: ! 1130: #ifdef NEEDS_SETENV ! 1131: static int setenv(const char *name, const char *value, int overwrite) ! 1132: { ! 1133: char *envstr; ! 1134: char *oldenv; ! 1135: if(overwrite || getenv(name)==NULL) { ! 1136: envstr=(char *)malloc(strlen(name)+strlen(value)+2); ! 1137: if(envstr==NULL) { ! 1138: errno=ENOMEM; ! 1139: return(-1); ! 1140: } ! 1141: /* Note, on some platforms, this can be free()d... */ ! 1142: sprintf(envstr,"%s=%s",name,value); ! 1143: putenv(envstr); ! 1144: } ! 1145: return(0); ! 1146: } ! 1147: #endif ! 1148: ! 1149: #ifdef NEEDS_CFMAKERAW ! 1150: void ! 1151: cfmakeraw(struct termios *t) ! 1152: { ! 1153: t->c_iflag &= ~(IMAXBEL|IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); ! 1154: t->c_oflag &= ~OPOST; ! 1155: t->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); ! 1156: t->c_cflag &= ~(CSIZE|PARENB); ! 1157: t->c_cflag |= CS8; ! 1158: } ! 1159: #endif ! 1160: ! 1161: #ifdef NEEDS_FORKPTY ! 1162: static int login_tty(int fd) ! 1163: { ! 1164: (void) setsid(); ! 1165: if (!isatty(fd)) ! 1166: return (-1); ! 1167: (void) dup2(fd, 0); ! 1168: (void) dup2(fd, 1); ! 1169: (void) dup2(fd, 2); ! 1170: if (fd > 2) ! 1171: (void) close(fd); ! 1172: return (0); ! 1173: } ! 1174: ! 1175: #ifdef NEEDS_DAEMON ! 1176: /****************************************************************************/ ! 1177: /* Daemonizes the process */ ! 1178: /****************************************************************************/ ! 1179: int ! 1180: daemon(int nochdir, int noclose) ! 1181: { ! 1182: int fd; ! 1183: ! 1184: switch (fork()) { ! 1185: case -1: ! 1186: return (-1); ! 1187: case 0: ! 1188: break; ! 1189: default: ! 1190: _exit(0); ! 1191: } ! 1192: ! 1193: if (setsid() == -1) ! 1194: return (-1); ! 1195: ! 1196: if (!nochdir) ! 1197: (void)chdir("/"); ! 1198: ! 1199: if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { ! 1200: (void)dup2(fd, STDIN_FILENO); ! 1201: (void)dup2(fd, STDOUT_FILENO); ! 1202: (void)dup2(fd, STDERR_FILENO); ! 1203: if (fd > 2) ! 1204: (void)close(fd); ! 1205: } ! 1206: return (0); ! 1207: } ! 1208: #endif ! 1209: ! 1210: static int openpty(int *amaster, int *aslave, char *name, struct termios *termp, winsize *winp) ! 1211: { ! 1212: char line[] = "/dev/ptyXX"; ! 1213: const char *cp1, *cp2; ! 1214: int master, slave, ttygid; ! 1215: struct group *gr; ! 1216: ! 1217: if ((gr = getgrnam("tty")) != NULL) ! 1218: ttygid = gr->gr_gid; ! 1219: else ! 1220: ttygid = -1; ! 1221: ! 1222: for (cp1 = "pqrsPQRS"; *cp1; cp1++) { ! 1223: line[8] = *cp1; ! 1224: for (cp2 = "0123456789abcdefghijklmnopqrstuv"; *cp2; cp2++) { ! 1225: line[5] = 'p'; ! 1226: line[9] = *cp2; ! 1227: if ((master = open(line, O_RDWR, 0)) == -1) { ! 1228: if (errno == ENOENT) ! 1229: break; /* try the next pty group */ ! 1230: } else { ! 1231: line[5] = 't'; ! 1232: (void) chown(line, getuid(), ttygid); ! 1233: (void) chmod(line, S_IRUSR|S_IWUSR|S_IWGRP); ! 1234: /* Hrm... SunOS doesn't seem to have revoke ! 1235: (void) revoke(line); */ ! 1236: if ((slave = open(line, O_RDWR, 0)) != -1) { ! 1237: *amaster = master; ! 1238: *aslave = slave; ! 1239: if (name) ! 1240: strcpy(name, line); ! 1241: if (termp) ! 1242: (void) tcsetattr(slave, ! 1243: TCSAFLUSH, termp); ! 1244: if (winp) ! 1245: (void) ioctl(slave, TIOCSWINSZ, ! 1246: (char *)winp); ! 1247: return (0); ! 1248: } ! 1249: (void) close(master); ! 1250: } ! 1251: } ! 1252: } ! 1253: errno = ENOENT; /* out of ptys */ ! 1254: return (-1); ! 1255: } ! 1256: ! 1257: static int forkpty(int *amaster, char *name, termios *termp, winsize *winp) ! 1258: { ! 1259: int master, slave, pid; ! 1260: ! 1261: if (openpty(&master, &slave, name, termp, winp) == -1) ! 1262: return (-1); ! 1263: switch (pid = FORK()) { ! 1264: case -1: ! 1265: return (-1); ! 1266: case 0: ! 1267: /* ! 1268: * child ! 1269: */ ! 1270: (void) close(master); ! 1271: login_tty(slave); ! 1272: return (0); ! 1273: } ! 1274: /* ! 1275: * parent ! 1276: */ ! 1277: *amaster = master; ! 1278: (void) close(slave); ! 1279: return (pid); ! 1280: } ! 1281: #endif /* NEED_FORKPTY */ ! 1282: ! 1283: int sbbs_t::external(const char* cmdline, long mode, const char* startup_dir) ! 1284: { ! 1285: char str[MAX_PATH+1]; ! 1286: char fname[MAX_PATH+1]; ! 1287: char fullpath[MAX_PATH+1]; ! 1288: char fullcmdline[MAX_PATH+1]; ! 1289: char* argv[MAX_ARGS]; ! 1290: char* p; ! 1291: BYTE* bp; ! 1292: BYTE buf[XTRN_IO_BUF_LEN]; ! 1293: BYTE output_buf[XTRN_IO_BUF_LEN*2]; ! 1294: ulong avail; ! 1295: ulong output_len; ! 1296: bool native=false; // DOS program by default ! 1297: bool rio_abortable_save=rio_abortable; ! 1298: int i; ! 1299: bool data_waiting; ! 1300: int rd; ! 1301: int wr; ! 1302: int argc; ! 1303: pid_t pid; ! 1304: int in_pipe[2]; ! 1305: int out_pipe[2]; ! 1306: int err_pipe[2]; ! 1307: fd_set ibits; ! 1308: int high_fd; ! 1309: struct timeval timeout; ! 1310: ! 1311: if(online==ON_LOCAL) ! 1312: eprintf(LOG_INFO,"Executing external: %s",cmdline); ! 1313: ! 1314: if(startup_dir==NULL) ! 1315: startup_dir=nulstr; ! 1316: ! 1317: XTRN_LOADABLE_MODULE; ! 1318: XTRN_LOADABLE_JS_MODULE; ! 1319: ! 1320: attr(cfg.color[clr_external]); /* setup default attributes */ ! 1321: ! 1322: native = native_executable(&cfg, cmdline, mode); ! 1323: ! 1324: SAFECOPY(str,cmdline); /* Set fname to program name only */ ! 1325: truncstr(str," "); ! 1326: SAFECOPY(fname,getfname(str)); ! 1327: ! 1328: sprintf(fullpath,"%s%s",startup_dir,fname); ! 1329: if(startup_dir!=NULL && cmdline[0]!='/' && cmdline[0]!='.' && fexist(fullpath)) ! 1330: sprintf(fullcmdline,"%s%s",startup_dir,cmdline); ! 1331: else ! 1332: SAFECOPY(fullcmdline,cmdline); ! 1333: ! 1334: if(native) { // Native (32-bit) external ! 1335: ! 1336: // Current environment passed to child process ! 1337: sprintf(dszlog,"%sPROTOCOL.LOG",cfg.node_dir); ! 1338: setenv("DSZLOG",dszlog,1); /* Makes the DSZ LOG active */ ! 1339: setenv("SBBSNODE",cfg.node_dir,1); ! 1340: setenv("SBBSCTRL",cfg.ctrl_dir,1); ! 1341: setenv("SBBSDATA",cfg.data_dir,1); ! 1342: setenv("SBBSEXEC",cfg.exec_dir,1); ! 1343: sprintf(str,"%u",cfg.node_num); ! 1344: if(setenv("SBBSNNUM",str,1)) ! 1345: errormsg(WHERE,ERR_WRITE,"environment",0); ! 1346: ! 1347: } else { ! 1348: #if defined(__FreeBSD__) ! 1349: /* ToDo: This seems to work for every door except Iron Ox ! 1350: ToDo: Iron Ox is unique in that it runs perfectly from ! 1351: ToDo: tcsh but not at all from anywhere else, complaining ! 1352: ToDo: about corrupt files. I've ruled out the possibilty ! 1353: ToDo: of it being a terminal mode issue... no other ideas ! 1354: ToDo: come to mind. */ ! 1355: ! 1356: FILE * doscmdrc; ! 1357: ! 1358: sprintf(str,"%s.doscmdrc",cfg.node_dir); ! 1359: if((doscmdrc=fopen(str,"w+"))==NULL) { ! 1360: errormsg(WHERE,ERR_CREATE,str,0); ! 1361: return(-1); ! 1362: } ! 1363: if(startup_dir!=NULL && startup_dir[0]) ! 1364: fprintf(doscmdrc,"assign C: %s\n",startup_dir); ! 1365: else ! 1366: fprintf(doscmdrc,"assign C: .\n"); ! 1367: ! 1368: fprintf(doscmdrc,"assign D: %s\n",cfg.node_dir); ! 1369: SAFECOPY(str,cfg.exec_dir); ! 1370: if((p=strrchr(str,'/'))!=NULL) ! 1371: *p=0; ! 1372: if((p=strrchr(str,'/'))!=NULL) ! 1373: *p=0; ! 1374: fprintf(doscmdrc,"assign E: %s\n",str); ! 1375: ! 1376: /* setup doscmd env here */ ! 1377: /* ToDo Note, this assumes that the BBS uses standard dir names */ ! 1378: fprintf(doscmdrc,"DSZLOG=E:\\node%d\\PROTOCOL.LOG\n",cfg.node_num); ! 1379: fprintf(doscmdrc,"SBBSNODE=D:\\\n"); ! 1380: fprintf(doscmdrc,"SBBSCTRL=E:\\ctrl\\\n"); ! 1381: fprintf(doscmdrc,"SBBSDATA=E:\\data\\\n"); ! 1382: fprintf(doscmdrc,"SBBSEXEC=E:\\exec\\\n"); ! 1383: fprintf(doscmdrc,"SBBSNNUM=%d\n",cfg.node_num); ! 1384: ! 1385: fclose(doscmdrc); ! 1386: SAFECOPY(str,fullcmdline); ! 1387: sprintf(fullcmdline,"%s -F %s",startup->dosemu_path,str); ! 1388: ! 1389: #elif defined(__linux__) && defined(USE_DOSEMU) ! 1390: ! 1391: /* dosemu integration -- Ryan Underwood, <nemesis @ icequake.net> */ ! 1392: ! 1393: FILE *dosemubat; ! 1394: int setup_override; ! 1395: char tok[MAX_PATH+1]; ! 1396: ! 1397: char dosemuconf[MAX_PATH+1]; ! 1398: char dosemubinloc[MAX_PATH+1]; ! 1399: char virtualconf[75]; ! 1400: char dosterm[15]; ! 1401: char log_external[MAX_PATH+1]; ! 1402: ! 1403: /* on the Unix side. xtrndir is the parent of the door's startup dir. */ ! 1404: char xtrndir[MAX_PATH+1]; ! 1405: ! 1406: /* on the DOS side. */ ! 1407: char xtrndir_dos[MAX_PATH+1]; ! 1408: char ctrldir_dos[MAX_PATH+1]; ! 1409: char datadir_dos[MAX_PATH+1]; ! 1410: char execdir_dos[MAX_PATH+1]; ! 1411: ! 1412: /* Default locations that can be overridden by ! 1413: * the sysop in emusetup.bat */ ! 1414: ! 1415: const char nodedrive[] = "D:"; ! 1416: const char xtrndrive[] = "E:"; ! 1417: const char ctrldrive[] = "F:"; ! 1418: const char datadrive[] = "G:"; ! 1419: const char execdrive[] = "H:"; ! 1420: ! 1421: SAFECOPY(str,startup_dir); ! 1422: if(*(p=lastchar(str))=='/') /* kill trailing slash */ ! 1423: *p=0; ! 1424: if((p=strrchr(str,'/'))!=NULL) /* kill the last element of the path */ ! 1425: *p=0; ! 1426: ! 1427: SAFECOPY(xtrndir,str); ! 1428: ! 1429: /* construct DOS equivalents for the unix directories */ ! 1430: ! 1431: SAFECOPY(ctrldir_dos,cfg.ctrl_dir); ! 1432: REPLACE_CHARS(ctrldir_dos,'/','\\',p); ! 1433: ! 1434: p=lastchar(ctrldir_dos); ! 1435: if (*p=='\\') *p=0; ! 1436: ! 1437: SAFECOPY(datadir_dos,cfg.data_dir); ! 1438: REPLACE_CHARS(datadir_dos,'/','\\',p); ! 1439: ! 1440: p=lastchar(datadir_dos); ! 1441: if (*p=='\\') *p=0; ! 1442: ! 1443: SAFECOPY(execdir_dos,cfg.exec_dir); ! 1444: REPLACE_CHARS(execdir_dos,'/','\\',p); ! 1445: ! 1446: p=lastchar(execdir_dos); ! 1447: if (*p=='\\') *p=0; ! 1448: ! 1449: SAFECOPY(xtrndir_dos,xtrndir); ! 1450: REPLACE_CHARS(xtrndir_dos,'/','\\',p); ! 1451: ! 1452: /* check for existence of a dosemu.conf in the door directory. ! 1453: * It is a good idea to be able to use separate configs for each ! 1454: * door. */ ! 1455: ! 1456: sprintf(str,"%sdosemu.conf",startup_dir); ! 1457: if (!fexist(str)) { ! 1458: ! 1459: /* If we can't find it in the door dir, look for a global one ! 1460: * in the ctrl dir. */ ! 1461: ! 1462: sprintf(str,"%sdosemu.conf",cfg.ctrl_dir); ! 1463: if (!fexist(str)) { ! 1464: ! 1465: /* If we couldn't find either, try for the system one, then ! 1466: * error out. */ ! 1467: SAFECOPY(str,"/etc/dosemu/dosemu.conf"); ! 1468: if (!fexist(str)) { ! 1469: ! 1470: SAFECOPY(str,"/etc/dosemu.conf"); ! 1471: if (!fexist(str)) { ! 1472: errormsg(WHERE,ERR_READ,str,0); ! 1473: return(-1); ! 1474: } ! 1475: else SAFECOPY(dosemuconf,str); /* using system conf */ ! 1476: } ! 1477: else SAFECOPY(dosemuconf,str); /* using system conf */ ! 1478: } ! 1479: else SAFECOPY(dosemuconf,str); /* using global conf */ ! 1480: } ! 1481: else SAFECOPY(dosemuconf,str); /* using door-specific conf */ ! 1482: ! 1483: /* same deal for emusetup.bat. */ ! 1484: ! 1485: sprintf(str,"%semusetup.bat",startup_dir); ! 1486: fprintf(stderr, str); ! 1487: if (!fexist(str)) { ! 1488: ! 1489: /* If we can't find it in the door dir, look for a global one ! 1490: * in the ctrl dir. */ ! 1491: ! 1492: sprintf(str,"%semusetup.bat",cfg.ctrl_dir); ! 1493: if (!fexist(str)) { ! 1494: ! 1495: /* If we couldn't find either, set an error condition. */ ! 1496: setup_override = -1; ! 1497: } ! 1498: else setup_override = 0; /* using global bat */ ! 1499: } ! 1500: else setup_override = 1; /* using door-specific bat */ ! 1501: ! 1502: /* Create the external bat here to be placed in the node dir. */ ! 1503: ! 1504: sprintf(str,"%sexternal.bat",cfg.node_dir); ! 1505: if(!(dosemubat=fopen(str,"w+"))) { ! 1506: errormsg(WHERE,ERR_CREATE,str,0); ! 1507: return(-1); ! 1508: } ! 1509: ! 1510: fprintf(dosemubat,"@echo off\r\n"); ! 1511: fprintf(dosemubat,"set DSZLOG=%s\\PROTOCOL.LOG\r\n",nodedrive); ! 1512: fprintf(dosemubat,"set SBBSNODE=%s\r\n",nodedrive); ! 1513: fprintf(dosemubat,"set SBBSNNUM=%d\r\n",cfg.node_num); ! 1514: fprintf(dosemubat,"set SBBSCTRL=%s\r\n",ctrldrive); ! 1515: fprintf(dosemubat,"set SBBSDATA=%s\r\n",datadrive); ! 1516: fprintf(dosemubat,"set SBBSEXEC=%s\r\n",execdrive); ! 1517: ! 1518: /* clear existing redirections on dos side */ ! 1519: fprintf(dosemubat,"lredir del %s\r\nlredir del %s\r\nlredir del %s\r\nlredir del %s\r\n",xtrndrive,ctrldrive,datadrive,execdrive); ! 1520: ! 1521: /* redirect necessary drive letters to unix paths */ ! 1522: fprintf(dosemubat,"lredir %s linux\\fs%s\r\n",xtrndrive,xtrndir_dos); ! 1523: fprintf(dosemubat,"lredir %s linux\\fs%s\r\n",ctrldrive,ctrldir_dos); ! 1524: fprintf(dosemubat,"lredir %s linux\\fs%s\r\n",datadrive,datadir_dos); ! 1525: fprintf(dosemubat,"lredir %s linux\\fs%s\r\n",execdrive,execdir_dos); ! 1526: ! 1527: /* change to the drive where the parent of the startup_dir is mounted */ ! 1528: fprintf(dosemubat,"%s\r\n",xtrndrive); ! 1529: ! 1530: if(startup_dir!=NULL && startup_dir[0]) { ! 1531: ! 1532: SAFECOPY(str,startup_dir); ! 1533: ! 1534: /* if theres a trailing slash, dump it */ ! 1535: ! 1536: p=lastchar(str); ! 1537: if (*p=='/') *p=0; ! 1538: ! 1539: if ((p=strrchr(str, '/'))!=NULL) ! 1540: SAFECOPY(str,p+1); /* str = game's starting dir */ ! 1541: ! 1542: else str[0] = '\0'; ! 1543: } ! 1544: ! 1545: else str[0] = '\0'; ! 1546: ! 1547: fprintf(dosemubat,"cd %s\r\n",str); /* startup_dir */ ! 1548: ! 1549: if (setup_override == 1) ! 1550: fprintf(dosemubat,"call %s\\%s\\emusetup.bat %s\r\n",xtrndrive,str,cmdline); ! 1551: else if (setup_override == 0) ! 1552: fprintf(dosemubat,"call %s\\emusetup.bat\r\n",ctrldrive); ! 1553: /* if (setup_override == -1) do_nothing */ ! 1554: ! 1555: /* Check if it's a bat file, to prepend "call" to the command */ ! 1556: ! 1557: SAFECOPY(tok,cmdline); ! 1558: truncstr(tok," "); ! 1559: ! 1560: p = strstr(tok, ".bat"); /* check if it's a bat file */ ! 1561: if (p) ! 1562: fprintf(dosemubat,"call "); /* if so, "call" it */ ! 1563: ! 1564: fprintf(dosemubat,"%s\r\n",cmdline); ! 1565: fprintf(dosemubat,"exitemu\r\n"); ! 1566: ! 1567: /* Check the "Stdio Interception" flag from scfg for this door. If it's ! 1568: * enabled, we enable doorway mode. Else, it's vmodem for us, unless ! 1569: * it's a timed event. ! 1570: */ ! 1571: ! 1572: if (!(mode&(EX_INR|EX_OUTR)) && online!=ON_LOCAL) ! 1573: SAFECOPY(virtualconf,"-I\"serial { virtual com 1 }\""); ! 1574: else ! 1575: virtualconf[0] = '\0'; ! 1576: ! 1577: /* Set the interception bits, since we are always going to want Synchronet ! 1578: * to intercept dos programs under Unix. ! 1579: */ ! 1580: ! 1581: mode |= (EX_INR|EX_OUTR); ! 1582: ! 1583: /* See if we have the dosemu link in the door's dir. If so, use the dosemu ! 1584: * that it points to as our command to execute. If not, use DOSemuPath. ! 1585: */ ! 1586: ! 1587: sprintf(str,"%sdosemu.bin",startup_dir); ! 1588: if (!fexist(str)) { ! 1589: SAFECOPY(dosemubinloc,(cmdstr(startup->dosemu_path,nulstr,nulstr,tok))); ! 1590: } ! 1591: else { ! 1592: SAFECOPY(dosemubinloc,str); ! 1593: } ! 1594: ! 1595: /* Attempt to keep dosemu from prompting for a disclaimer. */ ! 1596: ! 1597: sprintf(str, "%s/.dosemu", cfg.ctrl_dir); ! 1598: if (!isdir(str)) { ! 1599: mkdir(str, 0755); ! 1600: } ! 1601: ! 1602: strcat(str, "/disclaimer"); ! 1603: ftouch(str); ! 1604: ! 1605: /* Set up the command for dosemu to execute with 'unix -e'. */ ! 1606: ! 1607: sprintf(str,"%sexternal.bat",nodedrive); ! 1608: ! 1609: /* need TERM=linux for maintenance programs to work ! 1610: * (dosemu won't start with no controlling terminal) ! 1611: * Also, redirect stdout to a log if it's a timed event. ! 1612: */ ! 1613: ! 1614: if (online==ON_LOCAL) { ! 1615: SAFECOPY(dosterm,"TERM=linux"); ! 1616: sprintf(log_external,">> %sdosevent_%s.log",cfg.logs_dir,fname); ! 1617: } ! 1618: else { ! 1619: dosterm[0]='\0'; ! 1620: log_external[0] = '\0'; ! 1621: } ! 1622: ! 1623: /* Drum roll. */ ! 1624: ! 1625: sprintf(fullcmdline, ! 1626: "/usr/bin/env %s HOME=%s QUIET=1 DOSDRIVE_D=%s %s -I\"video { none }\" -I\"keystroke \\r\" %s -f%s -E%s -o%sdosemu.log 2> %sdosemu_boot.log %s", ! 1627: dosterm,cfg.ctrl_dir,cfg.node_dir,dosemubinloc,virtualconf,dosemuconf,str,cfg.node_dir,cfg.node_dir,log_external); ! 1628: ! 1629: fprintf(dosemubat,"REM For debugging: %s\r\n",fullcmdline); ! 1630: fclose(dosemubat); ! 1631: ! 1632: #else ! 1633: bprintf("\r\nExternal DOS programs are not yet supported in \r\n%s\r\n" ! 1634: ,VERSION_NOTICE); ! 1635: return(-1); ! 1636: #endif ! 1637: } ! 1638: ! 1639: if(!(mode&EX_INR) && input_thread_running) { ! 1640: lprintf(LOG_DEBUG,"Locking input thread mutex"); ! 1641: if(pthread_mutex_lock(&input_thread_mutex)!=0) ! 1642: errormsg(WHERE,ERR_LOCK,"input_thread_mutex",0); ! 1643: input_thread_mutex_locked=true; ! 1644: } ! 1645: ! 1646: #ifdef XTERN_LOG_STDERR ! 1647: if(pipe(err_pipe)!=0) { ! 1648: errormsg(WHERE,ERR_CREATE,"err_pipe",0); ! 1649: return(-1); ! 1650: } ! 1651: #endif ! 1652: ! 1653: if((mode&EX_INR) && (mode&EX_OUTR)) { ! 1654: struct winsize winsize; ! 1655: struct termios term; ! 1656: memset(&term,0,sizeof(term)); ! 1657: cfsetispeed(&term,B19200); ! 1658: cfsetospeed(&term,B19200); ! 1659: if(mode&EX_BIN) ! 1660: cfmakeraw(&term); ! 1661: else { ! 1662: term.c_iflag = TTYDEF_IFLAG; ! 1663: term.c_oflag = TTYDEF_OFLAG; ! 1664: term.c_lflag = TTYDEF_LFLAG; ! 1665: term.c_cflag = TTYDEF_CFLAG; ! 1666: memcpy(term.c_cc,ttydefchars,sizeof(term.c_cc)); ! 1667: } ! 1668: winsize.ws_row=rows; ! 1669: winsize.ws_col=cols; ! 1670: if((pid=forkpty(&in_pipe[1],NULL,&term,&winsize))==-1) { ! 1671: if(input_thread_mutex_locked && input_thread_running) { ! 1672: if(pthread_mutex_unlock(&input_thread_mutex)!=0) ! 1673: errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0); ! 1674: input_thread_mutex_locked=false; ! 1675: } ! 1676: errormsg(WHERE,ERR_EXEC,fullcmdline,0); ! 1677: return(-1); ! 1678: } ! 1679: out_pipe[0]=in_pipe[1]; ! 1680: } ! 1681: else { ! 1682: if(mode&EX_INR) ! 1683: if(pipe(in_pipe)!=0) { ! 1684: errormsg(WHERE,ERR_CREATE,"in_pipe",0); ! 1685: return(-1); ! 1686: } ! 1687: if(mode&EX_OUTR) ! 1688: if(pipe(out_pipe)!=0) { ! 1689: errormsg(WHERE,ERR_CREATE,"out_pipe",0); ! 1690: return(-1); ! 1691: } ! 1692: ! 1693: ! 1694: if((pid=FORK())==-1) { ! 1695: if(input_thread_mutex_locked && input_thread_running) { ! 1696: if(pthread_mutex_unlock(&input_thread_mutex)!=0) ! 1697: errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0); ! 1698: input_thread_mutex_locked=false; ! 1699: } ! 1700: errormsg(WHERE,ERR_EXEC,fullcmdline,0); ! 1701: return(-1); ! 1702: } ! 1703: } ! 1704: if(pid==0) { /* child process */ ! 1705: /* Give away all privs for good now */ ! 1706: if(startup->setuid!=NULL) ! 1707: startup->setuid(TRUE); ! 1708: ! 1709: sigset_t sigs; ! 1710: sigfillset(&sigs); ! 1711: sigprocmask(SIG_UNBLOCK,&sigs,NULL); ! 1712: if(!(mode&EX_BIN)) { ! 1713: static char term_env[256]; ! 1714: if(term_supports(ANSI)) ! 1715: sprintf(term_env,"TERM=%s",startup->xtrn_term_ansi); ! 1716: else ! 1717: sprintf(term_env,"TERM=%s",startup->xtrn_term_dumb); ! 1718: putenv(term_env); ! 1719: } ! 1720: #ifdef __FreeBSD__ ! 1721: if(!native) ! 1722: chdir(cfg.node_dir); ! 1723: else ! 1724: #endif ! 1725: if(startup_dir!=NULL && startup_dir[0]) ! 1726: chdir(startup_dir); ! 1727: ! 1728: if(mode&EX_SH || strcspn(fullcmdline,"<>|;\"")!=strlen(fullcmdline)) { ! 1729: argv[0]=comspec; ! 1730: argv[1]="-c"; ! 1731: argv[2]=fullcmdline; ! 1732: argv[3]=NULL; ! 1733: } else { ! 1734: argv[0]=fullcmdline; /* point to the beginning of the string */ ! 1735: argc=1; ! 1736: for(i=0;fullcmdline[i] && argc<MAX_ARGS;i++) /* Break up command line */ ! 1737: if(fullcmdline[i]==' ') { ! 1738: fullcmdline[i]=0; /* insert nulls */ ! 1739: argv[argc++]=fullcmdline+i+1; /* point to the beginning of the next arg */ ! 1740: } ! 1741: argv[argc]=NULL; ! 1742: } ! 1743: ! 1744: if(mode&EX_INR && !(mode&EX_OUTR)) { ! 1745: close(in_pipe[1]); /* close write-end of pipe */ ! 1746: dup2(in_pipe[0],0); /* redirect stdin */ ! 1747: close(in_pipe[0]); /* close excess file descriptor */ ! 1748: } ! 1749: ! 1750: if(mode&EX_OUTR && !(mode&EX_INR)) { ! 1751: close(out_pipe[0]); /* close read-end of pipe */ ! 1752: dup2(out_pipe[1],1); /* stdout */ ! 1753: #ifndef XTERN_LOG_STDERR ! 1754: dup2(out_pipe[1],2); /* stderr */ ! 1755: #endif ! 1756: close(out_pipe[1]); /* close excess file descriptor */ ! 1757: } ! 1758: ! 1759: if(mode&EX_BG) /* background execution, detach child */ ! 1760: { ! 1761: lprintf(LOG_INFO,"Detaching external process"); ! 1762: daemon(TRUE,FALSE); ! 1763: } ! 1764: ! 1765: #ifdef XTERN_LOG_STDERR ! 1766: close(err_pipe[0]); /* close read-end of pipe */ ! 1767: dup2(err_pipe[1],2); /* stderr */ ! 1768: #endif ! 1769: ! 1770: execvp(argv[0],argv); ! 1771: sprintf(str,"!ERROR %d executing %s",errno,argv[0]); ! 1772: errorlog(str); ! 1773: _exit(-1); /* should never get here */ ! 1774: } ! 1775: ! 1776: if(online!=ON_LOCAL) ! 1777: lprintf(LOG_INFO,"Node %d executing external: %s",cfg.node_num,fullcmdline); ! 1778: ! 1779: /* Disable Ctrl-C checking */ ! 1780: if(!(mode&EX_OFFLINE)) ! 1781: rio_abortable=false; ! 1782: ! 1783: #ifdef XTERN_LOG_STDERR ! 1784: close(err_pipe[1]); /* close write-end of pipe */ ! 1785: #endif ! 1786: ! 1787: if(mode&EX_OUTR) { ! 1788: if(!(mode&EX_INR)) ! 1789: close(out_pipe[1]); /* close write-end of pipe */ ! 1790: while(!terminated) { ! 1791: if(waitpid(pid, &i, WNOHANG)!=0) /* child exited */ ! 1792: break; ! 1793: ! 1794: if(mode&EX_CHKTIME) ! 1795: gettimeleft(); ! 1796: ! 1797: if(!online && !(mode&EX_OFFLINE)) { ! 1798: sprintf(str,"%s hung-up in external program",useron.alias); ! 1799: logline("X!",str); ! 1800: break; ! 1801: } ! 1802: ! 1803: /* Input */ ! 1804: if(mode&EX_INR && RingBufFull(&inbuf)) { ! 1805: if((wr=RingBufRead(&inbuf,buf,sizeof(buf)))!=0) ! 1806: write(in_pipe[1],buf,wr); ! 1807: } ! 1808: ! 1809: /* Error Output */ ! 1810: FD_ZERO(&ibits); ! 1811: #ifdef XTERN_LOG_STDERR ! 1812: FD_SET(err_pipe[0],&ibits); ! 1813: high_fd=err_pipe[0]; ! 1814: #endif ! 1815: FD_SET(out_pipe[0],&ibits); ! 1816: #ifdef XTERN_LOG_STDERR ! 1817: if(out_pipe[0]>err_pipe[0]) ! 1818: high_fd=out_pipe[0]; ! 1819: #else ! 1820: high_fd=out_pipe[0]; ! 1821: #endif ! 1822: timeout.tv_sec=0; ! 1823: timeout.tv_usec=1000; ! 1824: bp=buf; ! 1825: i=0; ! 1826: #ifndef XTERN_LOG_STDERR ! 1827: select(high_fd+1,&ibits,NULL,NULL,&timeout); ! 1828: #else ! 1829: while ((select(high_fd+1,&ibits,NULL,NULL,&timeout)>0) && FD_ISSET(err_pipe[0],&ibits) && (i<(int)sizeof(buf)-1)) { ! 1830: if((rd=read(err_pipe[0],bp,1))>0) { ! 1831: i+=rd; ! 1832: bp++; ! 1833: if(*(bp-1)=='\n') ! 1834: break; ! 1835: } ! 1836: else ! 1837: break; ! 1838: FD_ZERO(&ibits); ! 1839: FD_SET(err_pipe[0],&ibits); ! 1840: FD_SET(out_pipe[0],&ibits); ! 1841: timeout.tv_sec=0; ! 1842: timeout.tv_usec=1000; ! 1843: } ! 1844: if(i) ! 1845: lprintf(LOG_NOTICE,"%.*s",i,buf); /* lprintf mangles i? */ ! 1846: ! 1847: /* Eat stderr if mode is EX_BIN */ ! 1848: if(mode&EX_BIN) { ! 1849: bp=buf; ! 1850: i=0; ! 1851: } ! 1852: #endif ! 1853: ! 1854: data_waiting=FD_ISSET(out_pipe[0],&ibits); ! 1855: if(i==0 && data_waiting==0) ! 1856: continue; ! 1857: ! 1858: avail=(RingBufFree(&outbuf)-i)/2; // Leave room for wwiv/telnet expansion ! 1859: if(avail==0) { ! 1860: #if 0 ! 1861: lprintf("Node %d !output buffer full (%u bytes)" ! 1862: ,cfg.node_num,RingBufFull(&outbuf)); ! 1863: #endif ! 1864: YIELD(); ! 1865: continue; ! 1866: } ! 1867: ! 1868: rd=avail; ! 1869: ! 1870: if(rd>((int)sizeof(buf)-i)) ! 1871: rd=sizeof(buf)-i; ! 1872: ! 1873: if(data_waiting) { ! 1874: rd=read(out_pipe[0],bp,rd); ! 1875: if(rd<1 && i==0) ! 1876: continue; ! 1877: if(rd<0) ! 1878: rd=0; ! 1879: } ! 1880: else ! 1881: rd=0; ! 1882: ! 1883: rd += i; ! 1884: ! 1885: if(mode&EX_BIN) { ! 1886: if(telnet_mode&TELNET_MODE_OFF) { ! 1887: bp=buf; ! 1888: output_len=rd; ! 1889: } ! 1890: else ! 1891: bp=telnet_expand(buf, rd, output_buf, output_len); ! 1892: } else /* LF to CRLF expansion */ ! 1893: bp=lf_expand(buf, rd, output_buf, output_len); ! 1894: ! 1895: /* Did expansion overrun the output buffer? */ ! 1896: if(output_len>sizeof(output_buf)) { ! 1897: errorlog("OUTPUT_BUF OVERRUN"); ! 1898: output_len=sizeof(output_buf); ! 1899: } ! 1900: ! 1901: /* Does expanded size fit in the ring buffer? */ ! 1902: if(output_len>RingBufFree(&outbuf)) { ! 1903: errorlog("output buffer overflow"); ! 1904: output_len=RingBufFree(&outbuf); ! 1905: } ! 1906: ! 1907: RingBufWrite(&outbuf, bp, output_len); ! 1908: ! 1909: } ! 1910: ! 1911: if(waitpid(pid, &i, WNOHANG)==0) { // Child still running? ! 1912: kill(pid, SIGHUP); // Tell child user has hung up ! 1913: time_t start=time(NULL); // Wait up to 10 seconds ! 1914: while(time(NULL)-start<10) { // for child to terminate ! 1915: if(waitpid(pid, &i, WNOHANG)!=0) ! 1916: break; ! 1917: mswait(500); ! 1918: } ! 1919: if(waitpid(pid, &i, WNOHANG)==0) // Child still running? ! 1920: kill(pid, SIGKILL); // terminate child process ! 1921: } ! 1922: /* close unneeded descriptors */ ! 1923: if(mode&EX_INR) ! 1924: close(in_pipe[1]); ! 1925: close(out_pipe[0]); ! 1926: } ! 1927: #if 0 ! 1928: else { ! 1929: /* Enable the Nagle algorithm */ ! 1930: int nodelay=FALSE; ! 1931: setsockopt(client_socket,IPPROTO_TCP,TCP_NODELAY,(char*)&nodelay,sizeof(nodelay)); ! 1932: } ! 1933: #endif ! 1934: #ifdef XTERN_LOG_STDERR ! 1935: while(waitpid(pid, &i, WNOHANG)==0) { ! 1936: FD_ZERO(&ibits); ! 1937: FD_SET(err_pipe[0],&ibits); ! 1938: timeout.tv_sec=1; ! 1939: timeout.tv_usec=0; ! 1940: bp=buf; ! 1941: i=0; ! 1942: while ((select(err_pipe[0]+1,&ibits,NULL,NULL,&timeout)>0) && (i<XTRN_IO_BUF_LEN-1)) { ! 1943: if((rd=read(err_pipe[0],bp,1))>0) { ! 1944: i+=rd; ! 1945: if(*bp=='\n') { ! 1946: lprintf(LOG_NOTICE,"%.*s",i-1,buf); ! 1947: i=0; ! 1948: bp=buf; ! 1949: } ! 1950: else ! 1951: bp++; ! 1952: } ! 1953: else ! 1954: break; ! 1955: } ! 1956: if(i) ! 1957: lprintf(LOG_NOTICE,"%.*s",i,buf); ! 1958: } ! 1959: #else ! 1960: waitpid(pid, &i, 0)==0; ! 1961: #endif ! 1962: ! 1963: if(!(mode&EX_OFFLINE)) { /* !off-line execution */ ! 1964: ! 1965: /* Re-enable blocking (incase disabled by xtrn program) */ ! 1966: ulong l=0; ! 1967: ioctlsocket(client_socket, FIONBIO, &l); ! 1968: ! 1969: /* Re-set socket options */ ! 1970: if(set_socket_options(&cfg, client_socket, client.protocol, str, sizeof(str))) ! 1971: lprintf(LOG_ERR,"%04d !ERROR %s",client_socket, str); ! 1972: ! 1973: curatr=~0; // Can't guarantee current attributes ! 1974: attr(LIGHTGRAY); // Force to "normal" ! 1975: ! 1976: rio_abortable=rio_abortable_save; // Restore abortable state ! 1977: ! 1978: /* Got back to Text/NVT mode */ ! 1979: request_telnet_opt(TELNET_DONT,TELNET_BINARY_TX); ! 1980: } ! 1981: ! 1982: #ifdef XTERN_LOG_STDERR ! 1983: close(err_pipe[0]); ! 1984: #endif ! 1985: ! 1986: if(input_thread_mutex_locked && input_thread_running) { ! 1987: if(pthread_mutex_unlock(&input_thread_mutex)!=0) ! 1988: errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0); ! 1989: input_thread_mutex_locked=false; ! 1990: } ! 1991: ! 1992: return(errorlevel = WEXITSTATUS(i)); ! 1993: } ! 1994: ! 1995: #endif /* !WIN32 */ ! 1996: ! 1997: const char* quoted_string(const char* str, char* buf, size_t maxlen) ! 1998: { ! 1999: if(strchr(str,' ')==NULL) ! 2000: return(str); ! 2001: safe_snprintf(buf,maxlen,"\"%s\"",str); ! 2002: return(buf); ! 2003: } ! 2004: ! 2005: #define QUOTED_STRING(ch, str, buf, maxlen) \ ! 2006: ((isalpha(ch) && isupper(ch)) ? str : quoted_string(str,buf,maxlen)) ! 2007: ! 2008: /*****************************************************************************/ ! 2009: /* Returns command line generated from instr with %c replacments */ ! 2010: /*****************************************************************************/ ! 2011: char* sbbs_t::cmdstr(char *instr, char *fpath, char *fspec, char *outstr) ! 2012: { ! 2013: char str[MAX_PATH+1],*cmd; ! 2014: int i,j,len; ! 2015: ! 2016: if(outstr==NULL) ! 2017: cmd=cmdstr_output; ! 2018: else ! 2019: cmd=outstr; ! 2020: len=strlen(instr); ! 2021: for(i=j=0;i<len && j<(int)sizeof(cmdstr_output);i++) { ! 2022: if(instr[i]=='%') { ! 2023: i++; ! 2024: cmd[j]=0; ! 2025: char ch=instr[i]; ! 2026: if(isalpha(ch)) ! 2027: ch=toupper(ch); ! 2028: switch(ch) { ! 2029: case 'A': /* User alias */ ! 2030: strcat(cmd,QUOTED_STRING(instr[i],useron.alias,str,sizeof(str))); ! 2031: break; ! 2032: case 'B': /* Baud (DTE) Rate */ ! 2033: strcat(cmd,ultoa(dte_rate,str,10)); ! 2034: break; ! 2035: case 'C': /* Connect Description */ ! 2036: strcat(cmd,connection); ! 2037: break; ! 2038: case 'D': /* Connect (DCE) Rate */ ! 2039: strcat(cmd,ultoa((ulong)cur_rate,str,10)); ! 2040: break; ! 2041: case 'E': /* Estimated Rate */ ! 2042: strcat(cmd,ultoa((ulong)cur_cps*10,str,10)); ! 2043: break; ! 2044: case 'F': /* File path */ ! 2045: strcat(cmd,QUOTED_STRING(instr[i],fpath,str,sizeof(str))); ! 2046: break; ! 2047: case 'G': /* Temp directory */ ! 2048: strcat(cmd,cfg.temp_dir); ! 2049: break; ! 2050: case 'H': /* Port Handle or Hardware Flow Control */ ! 2051: strcat(cmd,ultoa(client_socket_dup,str,10)); ! 2052: break; ! 2053: case 'I': /* IP address */ ! 2054: strcat(cmd,cid); ! 2055: break; ! 2056: case 'J': ! 2057: strcat(cmd,cfg.data_dir); ! 2058: break; ! 2059: case 'K': ! 2060: strcat(cmd,cfg.ctrl_dir); ! 2061: break; ! 2062: case 'L': /* Lines per message */ ! 2063: strcat(cmd,ultoa(cfg.level_linespermsg[useron.level],str,10)); ! 2064: break; ! 2065: case 'M': /* Minutes (credits) for user */ ! 2066: strcat(cmd,ultoa(useron.min,str,10)); ! 2067: break; ! 2068: case 'N': /* Node Directory (same as SBBSNODE environment var) */ ! 2069: strcat(cmd,cfg.node_dir); ! 2070: break; ! 2071: case 'O': /* SysOp */ ! 2072: strcat(cmd,QUOTED_STRING(instr[i],cfg.sys_op,str,sizeof(str))); ! 2073: break; ! 2074: case 'P': /* Client protocol */ ! 2075: strcat(cmd,client.protocol); ! 2076: break; ! 2077: case 'Q': /* QWK ID */ ! 2078: strcat(cmd,cfg.sys_id); ! 2079: break; ! 2080: case 'R': /* Rows */ ! 2081: strcat(cmd,ultoa(rows,str,10)); ! 2082: break; ! 2083: case 'S': /* File Spec (or Baja command str) */ ! 2084: strcat(cmd,fspec); ! 2085: break; ! 2086: case 'T': /* Time left in seconds */ ! 2087: gettimeleft(); ! 2088: strcat(cmd,ultoa(timeleft,str,10)); ! 2089: break; ! 2090: case 'U': /* UART I/O Address (in hex) */ ! 2091: strcat(cmd,ultoa(cfg.com_base,str,16)); ! 2092: break; ! 2093: case 'V': /* Synchronet Version */ ! 2094: sprintf(str,"%s%c",VERSION,REVISION); ! 2095: break; ! 2096: case 'W': /* Time-slice API type (mswtype) */ ! 2097: break; ! 2098: case 'X': ! 2099: strcat(cmd,cfg.shell[useron.shell]->code); ! 2100: break; ! 2101: case '&': /* Address of msr */ ! 2102: break; ! 2103: case 'Y': ! 2104: strcat(cmd,comspec); ! 2105: break; ! 2106: case 'Z': ! 2107: strcat(cmd,cfg.text_dir); ! 2108: break; ! 2109: case '~': /* DOS-compatible (8.3) filename */ ! 2110: #ifdef _WIN32 ! 2111: char sfpath[MAX_PATH+1]; ! 2112: SAFECOPY(sfpath,fpath); ! 2113: GetShortPathName(fpath,sfpath,sizeof(sfpath)); ! 2114: strcat(cmd,sfpath); ! 2115: #else ! 2116: strcat(cmd,QUOTED_STRING(instr[i],fpath,str,sizeof(str))); ! 2117: #endif ! 2118: break; ! 2119: case '!': /* EXEC Directory */ ! 2120: strcat(cmd,cfg.exec_dir); ! 2121: break; ! 2122: case '@': /* EXEC Directory for DOS/OS2/Win32, blank for Unix */ ! 2123: #ifndef __unix__ ! 2124: strcat(cmd,cfg.exec_dir); ! 2125: #endif ! 2126: break; ! 2127: ! 2128: case '#': /* Node number (same as SBBSNNUM environment var) */ ! 2129: sprintf(str,"%d",cfg.node_num); ! 2130: strcat(cmd,str); ! 2131: break; ! 2132: case '*': ! 2133: sprintf(str,"%03d",cfg.node_num); ! 2134: strcat(cmd,str); ! 2135: break; ! 2136: case '$': /* Credits */ ! 2137: strcat(cmd,ultoa(useron.cdt+useron.freecdt,str,10)); ! 2138: break; ! 2139: case '%': /* %% for percent sign */ ! 2140: strcat(cmd,"%"); ! 2141: break; ! 2142: case '.': /* .exe for DOS/OS2/Win32, blank for Unix */ ! 2143: #ifndef __unix__ ! 2144: strcat(cmd,".exe"); ! 2145: #endif ! 2146: break; ! 2147: case '?': /* Platform */ ! 2148: #ifdef __OS2__ ! 2149: strcpy(str,"OS2"); ! 2150: #else ! 2151: strcpy(str,PLATFORM_DESC); ! 2152: #endif ! 2153: strlwr(str); ! 2154: strcat(cmd,str); ! 2155: break; ! 2156: default: /* unknown specification */ ! 2157: if(isdigit(instr[i])) { ! 2158: sprintf(str,"%0*d",instr[i]&0xf,useron.number); ! 2159: strcat(cmd,str); } ! 2160: break; } ! 2161: j=strlen(cmd); } ! 2162: else ! 2163: cmd[j++]=instr[i]; } ! 2164: cmd[j]=0; ! 2165: ! 2166: return(cmd); ! 2167: } ! 2168: ! 2169: /****************************************************************************/ ! 2170: /* Returns command line generated from instr with %c replacments */ ! 2171: /* This is the C-exported version */ ! 2172: /****************************************************************************/ ! 2173: extern "C" ! 2174: char* DLLCALL cmdstr(scfg_t* cfg, user_t* user, const char* instr, const char* fpath ! 2175: ,const char* fspec, char* cmd) ! 2176: { ! 2177: char str[MAX_PATH+1]; ! 2178: int i,j,len; ! 2179: ! 2180: len=strlen(instr); ! 2181: for(i=j=0;i<len && j<MAX_PATH;i++) { ! 2182: if(instr[i]=='%') { ! 2183: i++; ! 2184: cmd[j]=0; ! 2185: char ch=instr[i]; ! 2186: if(isalpha(ch)) ! 2187: ch=toupper(ch); ! 2188: switch(ch) { ! 2189: case 'A': /* User alias */ ! 2190: if(user!=NULL) ! 2191: strcat(cmd,QUOTED_STRING(instr[i],user->alias,str,sizeof(str))); ! 2192: break; ! 2193: case 'B': /* Baud (DTE) Rate */ ! 2194: break; ! 2195: case 'C': /* Connect Description */ ! 2196: break; ! 2197: case 'D': /* Connect (DCE) Rate */ ! 2198: break; ! 2199: case 'E': /* Estimated Rate */ ! 2200: break; ! 2201: case 'F': /* File path */ ! 2202: strcat(cmd,QUOTED_STRING(instr[i],fpath,str,sizeof(str))); ! 2203: break; ! 2204: case 'G': /* Temp directory */ ! 2205: strcat(cmd,cfg->temp_dir); ! 2206: break; ! 2207: case 'H': /* Port Handle or Hardware Flow Control */ ! 2208: break; ! 2209: case 'I': /* IP address */ ! 2210: break; ! 2211: case 'J': ! 2212: strcat(cmd,cfg->data_dir); ! 2213: break; ! 2214: case 'K': ! 2215: strcat(cmd,cfg->ctrl_dir); ! 2216: break; ! 2217: case 'L': /* Lines per message */ ! 2218: if(user!=NULL) ! 2219: strcat(cmd,ultoa(cfg->level_linespermsg[user->level],str,10)); ! 2220: break; ! 2221: case 'M': /* Minutes (credits) for user */ ! 2222: if(user!=NULL) ! 2223: strcat(cmd,ultoa(user->min,str,10)); ! 2224: break; ! 2225: case 'N': /* Node Directory (same as SBBSNODE environment var) */ ! 2226: strcat(cmd,cfg->node_dir); ! 2227: break; ! 2228: case 'O': /* SysOp */ ! 2229: strcat(cmd,QUOTED_STRING(instr[i],cfg->sys_op,str,sizeof(str))); ! 2230: break; ! 2231: case 'P': /* Client protocol */ ! 2232: break; ! 2233: case 'Q': /* QWK ID */ ! 2234: strcat(cmd,cfg->sys_id); ! 2235: break; ! 2236: case 'R': /* Rows */ ! 2237: if(user!=NULL) ! 2238: strcat(cmd,ultoa(user->rows,str,10)); ! 2239: break; ! 2240: case 'S': /* File Spec */ ! 2241: strcat(cmd,fspec); ! 2242: break; ! 2243: case 'T': /* Time left in seconds */ ! 2244: break; ! 2245: case 'U': /* UART I/O Address (in hex) */ ! 2246: strcat(cmd,ultoa(cfg->com_base,str,16)); ! 2247: break; ! 2248: case 'V': /* Synchronet Version */ ! 2249: sprintf(str,"%s%c",VERSION,REVISION); ! 2250: break; ! 2251: case 'W': /* Time-slice API type (mswtype) */ ! 2252: break; ! 2253: case 'X': ! 2254: if(user!=NULL) ! 2255: strcat(cmd,cfg->shell[user->shell]->code); ! 2256: break; ! 2257: case '&': /* Address of msr */ ! 2258: break; ! 2259: case 'Y': ! 2260: break; ! 2261: case 'Z': ! 2262: strcat(cmd,cfg->text_dir); ! 2263: break; ! 2264: case '~': /* DOS-compatible (8.3) filename */ ! 2265: #ifdef _WIN32 ! 2266: char sfpath[MAX_PATH+1]; ! 2267: SAFECOPY(sfpath,fpath); ! 2268: GetShortPathName(fpath,sfpath,sizeof(sfpath)); ! 2269: strcat(cmd,sfpath); ! 2270: #else ! 2271: strcat(cmd,QUOTED_STRING(instr[i],fpath,str,sizeof(str))); ! 2272: #endif ! 2273: break; ! 2274: case '!': /* EXEC Directory */ ! 2275: strcat(cmd,cfg->exec_dir); ! 2276: break; ! 2277: case '@': /* EXEC Directory for DOS/OS2/Win32, blank for Unix */ ! 2278: #ifndef __unix__ ! 2279: strcat(cmd,cfg->exec_dir); ! 2280: #endif ! 2281: break; ! 2282: ! 2283: case '#': /* Node number (same as SBBSNNUM environment var) */ ! 2284: sprintf(str,"%d",cfg->node_num); ! 2285: strcat(cmd,str); ! 2286: break; ! 2287: case '*': ! 2288: sprintf(str,"%03d",cfg->node_num); ! 2289: strcat(cmd,str); ! 2290: break; ! 2291: case '$': /* Credits */ ! 2292: if(user!=NULL) ! 2293: strcat(cmd,ultoa(user->cdt+user->freecdt,str,10)); ! 2294: break; ! 2295: case '%': /* %% for percent sign */ ! 2296: strcat(cmd,"%"); ! 2297: break; ! 2298: case '.': /* .exe for DOS/OS2/Win32, blank for Unix */ ! 2299: #ifndef __unix__ ! 2300: strcat(cmd,".exe"); ! 2301: #endif ! 2302: break; ! 2303: case '?': /* Platform */ ! 2304: #ifdef __OS2__ ! 2305: strcpy(str,"OS2"); ! 2306: #else ! 2307: strcpy(str,PLATFORM_DESC); ! 2308: #endif ! 2309: strlwr(str); ! 2310: strcat(cmd,str); ! 2311: break; ! 2312: default: /* unknown specification */ ! 2313: if(isdigit(instr[i]) && user!=NULL) { ! 2314: sprintf(str,"%0*d",instr[i]&0xf,user->number); ! 2315: strcat(cmd,str); ! 2316: } ! 2317: break; ! 2318: } ! 2319: j=strlen(cmd); ! 2320: } ! 2321: else ! 2322: cmd[j++]=instr[i]; ! 2323: } ! 2324: cmd[j]=0; ! 2325: ! 2326: return(cmd); ! 2327: } ! 2328:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.