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