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