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