Newer
Older
/* xtrn.cpp */
/* Synchronet external program support routines */
/* $Id$ */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright 2010 Rob Swindell - http://www.synchro.net/copyright.html *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* See the GNU General Public License for more details: gpl.txt or *
* http://www.fsf.org/copyleft/gpl.html *
* *
* Anonymous FTP access to the most recent released source is available at *
* ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
* *
* Anonymous CVS access to the development source and modification history *
* is available at cvs.synchro.net:/cvsroot/sbbs, example: *
* cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login *
* (just hit return, no password is necessary) *
* cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* You are encouraged to submit any modifications (preferably in Unix diff *
* format) via e-mail to mods@synchro.net *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
#include "sbbs.h"
#include "cmdshell.h"
#include "telnet.h"
#include <signal.h> // kill()
#ifdef __unix__
#include <sys/wait.h> // WEXITSTATUS
#define TTYDEFCHARS // needed for ttydefchars definition
#include <libutil.h> // forkpty()
#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DARWIN__)
#include <pty.h>
#elif defined(__QNX__)
#include <unix.h>
#else
#define NEEDS_FORKPTY
#endif
#endif
#ifdef NEEDS_FORKPTY
#include <grp.h>
#endif
#include <termios.h>
/*
* Control Character Defaults
*/
#ifndef CTRL
#define CTRL(x) (x&037)
#define CEOF CTRL('d')
#define CEOL 0xff /* XXX avoid _POSIX_VDISABLE */
#define CERASE 0177
#define CERASE2 CTRL('h')
#define CINTR CTRL('c')
#define CSTATUS CTRL('t')
#define CKILL CTRL('u')
#define CMIN 1
#define CQUIT 034 /* FS, ^\ */
#define CSUSP CTRL('z')
#define CTIME 0
#define CDSUSP CTRL('y')
#define CSTART CTRL('q')
#define CSTOP CTRL('s')
#define CLNEXT CTRL('v')
#define CDISCARD CTRL('o')
#define CWERASE CTRL('w')
#define CREPRINT CTRL('r')
#define CEOT CEOF
#define CBRK CEOL
#define CRPRNT CREPRINT
#define CFLUSH CDISCARD
#endif
#define TTYDEF_IFLAG (BRKINT | ICRNL | IMAXBEL | IXON | IXANY)
#define TTYDEF_OFLAG (OPOST | ONLCR)
#define TTYDEF_LFLAG (ECHO | ICANON | ISIG | IEXTEN | ECHOE|ECHOKE|ECHOCTL)
#define TTYDEF_CFLAG (CREAD | CS8 | HUPCL)
#if defined(__QNX__) || defined(__solaris__) || defined(__NetBSD__)
static cc_t ttydefchars[NCCS] = {
CEOF, CEOL, CEOL, CERASE, CWERASE, CKILL, CREPRINT,
CERASE2, CINTR, CQUIT, CSUSP, CDSUSP, CSTART, CSTOP, CLNEXT,
CDISCARD, CMIN, CTIME, CSTATUS
#ifndef __solaris__
, _POSIX_VDISABLE
#endif
#endif
#endif /* __unix__ */
#define XTRN_IO_BUF_LEN 10000 /* 50% of IO_THREAD_BUF_SIZE */
/*****************************************************************************/
/* Interrupt routine to expand WWIV Ctrl-C# codes into ANSI escape sequences */
/*****************************************************************************/
BYTE* wwiv_expand(BYTE* buf, ulong buflen, BYTE* outbuf, ulong& newlen
,ulong user_misc, bool& ctrl_c)
{
char ansi_seq[32];
ulong i,j,k;
for(i=j=0;i<buflen;i++) {
if(buf[i]==CTRL_C) { /* WWIV color escape char */
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
ctrl_c=true;
continue;
}
if(!ctrl_c) {
outbuf[j++]=buf[i];
continue;
}
ctrl_c=false;
if(user_misc&ANSI) {
switch(buf[i]) {
default:
strcpy(ansi_seq,"\x1b[0m"); /* low grey */
break;
case '1':
strcpy(ansi_seq,"\x1b[0;1;36m"); /* high cyan */
break;
case '2':
strcpy(ansi_seq,"\x1b[0;1;33m"); /* high yellow */
break;
case '3':
strcpy(ansi_seq,"\x1b[0;35m"); /* low magenta */
break;
case '4':
strcpy(ansi_seq,"\x1b[0;1;44m"); /* white on blue */
break;
case '5':
strcpy(ansi_seq,"\x1b[0;32m"); /* low green */
break;
case '6':
strcpy(ansi_seq,"\x1b[0;1;5;31m"); /* high blinking red */
break;
case '7':
strcpy(ansi_seq,"\x1b[0;1;34m"); /* high blue */
break;
case '8':
strcpy(ansi_seq,"\x1b[0;34m"); /* low blue */
break;
case '9':
strcpy(ansi_seq,"\x1b[0;36m"); /* low cyan */
break;
}
for(k=0;ansi_seq[k];k++)
outbuf[j++]=ansi_seq[k];
}
}
newlen=j;
return(outbuf);
}
/*****************************************************************************/
// Escapes Telnet IAC (255) by doubling the IAC char
/*****************************************************************************/
BYTE* telnet_expand(BYTE* inbuf, ulong inlen, BYTE* outbuf, ulong& newlen)
{
BYTE* first_iac;
ulong i,outlen;
first_iac=(BYTE*)memchr(inbuf, TELNET_IAC, inlen);
if(first_iac==NULL) { /* Nothing to expand */
newlen=inlen;
return(inbuf);
}
outlen=first_iac-inbuf;
memcpy(outbuf, inbuf, outlen);
for(i=outlen;i<inlen;i++) {
if(inbuf[i]==TELNET_IAC)
outbuf[outlen++]=inbuf[i];
}
newlen=outlen;
return(outbuf);
}
static bool native_executable(scfg_t* cfg, const char* cmdline, long mode)
{
char* p;
char str[MAX_PATH+1];
char name[64];
char base[64];
unsigned i;
if(mode&EX_NATIVE)
return(TRUE);
SAFECOPY(str,cmdline); /* Set str to program name only */
truncstr(str," ");
SAFECOPY(name,getfname(str));
SAFECOPY(base,name);
if((p=getfext(base))!=NULL)
*p=0;
for(i=0;i<cfg->total_natvpgms;i++)
if(stricmp(name,cfg->natvpgm[i]->name)==0
|| stricmp(base,cfg->natvpgm[i]->name)==0)
break;
return(i<cfg->total_natvpgms);
}
#define XTRN_LOADABLE_MODULE \
if(cmdline[0]=='*') /* Baja module or JavaScript */ \
return(exec_bin(cmdline+1,&main_csi))
#ifdef JAVASCRIPT
#define XTRN_LOADABLE_JS_MODULE \
if(cmdline[0]=='?') /* JavaScript */ \
return(js_execfile(cmdline+1))
#else
#define XTRN_LOADABLE_JS_MODULE
#endif
#ifdef _WIN32
#include "execvxd.h" /* Win9X FOSSIL VxD API */
extern SOCKET node_socket[];
// -------------------------------------------------------------------------
// GetAddressOfOpenVxDHandle
//
// This function returns the address of OpenVxDHandle. OpenVxDHandle is a
// KERNEL32 function that returns a ring 0 event handle that corresponds to a
// given ring 3 event handle. The ring 0 handle can be used by VxDs to
// synchronize with the Win32 app.
//
typedef HANDLE (WINAPI *OPENVXDHANDLE)(HANDLE);
OPENVXDHANDLE GetAddressOfOpenVxDHandle(void)
return((OPENVXDHANDLE)GetProcAddress(hK32, "OpenVxDHandle"));
/*****************************************************************************/
// Expands Single CR to CRLF
/*****************************************************************************/
BYTE* cr_expand(BYTE* inbuf, ulong inlen, BYTE* outbuf, ulong& newlen)
{
ulong i,j;
for(i=j=0;i<inlen;i++) {
outbuf[j++]=inbuf[i];
if(inbuf[i]=='\r')
outbuf[j++]='\n';
}
newlen=j;
return(outbuf);
}
static void add_env_var(str_list_t* list, const char* var, const char* val)
{
char str[MAX_PATH*2];
SetEnvironmentVariable(var,NULL); /* Delete in current process env */
SAFEPRINTF2(str,"%s=%s",var,val);
strListPush(list,str);
}
/* Clean-up resources while preserving current LastError value */
#define XTRN_CLEANUP \
last_error=GetLastError(); \
if(vxd!=INVALID_HANDLE_VALUE) CloseHandle(vxd); \
if(rdslot!=INVALID_HANDLE_VALUE) CloseHandle(rdslot); \
if(wrslot!=INVALID_HANDLE_VALUE) CloseHandle(wrslot); \
if(start_event!=NULL) CloseHandle(start_event); \
if(hungup_event!=NULL) CloseHandle(hungup_event); \
if(hangup_event!=NULL) CloseHandle(hangup_event); \
ReleaseMutex(exec_mutex); \
SetLastError(last_error)
/****************************************************************************/
/* Runs an external program */
/****************************************************************************/
int sbbs_t::external(const char* cmdline, long mode, const char* startup_dir)
char str[MAX_PATH+1];
char* env_block=NULL;
char* env_strings;
const char* p_startup_dir;
char path[MAX_PATH+1];
char fullcmdline[MAX_PATH+1];
char realcmdline[MAX_PATH+1];
char comspec_str[MAX_PATH+1];
char title[MAX_PATH+1];
BYTE buf[XTRN_IO_BUF_LEN],*bp;
BYTE telnet_buf[XTRN_IO_BUF_LEN*2];
BYTE output_buf[XTRN_IO_BUF_LEN*2];
BYTE wwiv_buf[XTRN_IO_BUF_LEN*2];
bool wwiv_flag=false;

rswindell
committed
bool native=false; // DOS program by default
bool nt=false; // WinNT/2K?
bool rio_abortable_save=rio_abortable;
bool use_pipes=false; // NT-compatible console redirection
BOOL success;
BOOL processTerminated=false;

rswindell
committed
uint i;
time_t hungup=0;
HANDLE vxd=INVALID_HANDLE_VALUE;
HANDLE rdslot=INVALID_HANDLE_VALUE;
HANDLE wrslot=INVALID_HANDLE_VALUE;
HANDLE start_event=NULL;
HANDLE hangup_event=NULL;
HANDLE rdoutpipe;
HANDLE wrinpipe;
PROCESS_INFORMATION process_info;
DWORD hVM;
unsigned long dummy;
unsigned long msglen;
unsigned long retval;
DWORD last_error;
DWORD loop_since_io=0;
struct tm tm;
str_list_t env_list;
OPENVXDHANDLE OpenVxDHandle;
eprintf(LOG_DEBUG,"Executing external: %s",cmdline);
else
lprintf(LOG_DEBUG,"Node %d Executing external: %s",cfg.node_num,cmdline);
if(startup_dir!=NULL && startup_dir[0] && !isdir(startup_dir)) {
errormsg(WHERE, ERR_CHK, startup_dir, 0);
return -1;
}
XTRN_LOADABLE_MODULE;
XTRN_LOADABLE_JS_MODULE;
attr(cfg.color[clr_external]); /* setup default attributes */
native = native_executable(&cfg, cmdline, mode);
if(mode&EX_SH || strcspn(cmdline,"<>|")!=strlen(cmdline))
sprintf(comspec_str,"%s /C ", comspec);
else
comspec_str[0]=0;
if(startup_dir && cmdline[1]!=':' && cmdline[0]!='/'
&& cmdline[0]!='\\' && cmdline[0]!='.')
sprintf(fullcmdline, "%s%s%s", comspec_str, startup_dir, cmdline);
else
sprintf(fullcmdline, "%s%s", comspec_str, cmdline);
SAFECOPY(realcmdline, fullcmdline); // for errormsg if failed to execute
if(localtime_r(&now,&tm)==NULL)
memset(&tm,0,sizeof(tm));
OpenVxDHandle=GetAddressOfOpenVxDHandle();
if(OpenVxDHandle==NULL)
nt=true; // Windows NT/2000
if(!nt && !native && !(cfg.xtrn_misc&XTRN_NO_MUTEX)
&& (retval=WaitForSingleObject(exec_mutex,5000))!=WAIT_OBJECT_0) {
errormsg(WHERE, ERR_TIMEOUT, "exec_mutex", retval);
return(GetLastError());
}
if(native && mode&EX_OUTR && !(mode&EX_OFFLINE))
use_pipes=true;
if(native) { // Native (32-bit) external
if((env_list=strListInit())==NULL) {
XTRN_CLEANUP;
errormsg(WHERE, ERR_CREATE, "env_list", 0);
return(errno);
}
// Current environment passed to child process
sprintf(str,"%sprotocol.log",cfg.node_dir);
add_env_var(&env_list,"DSZLOG",str);
add_env_var(&env_list,"SBBSNODE",cfg.node_dir);
add_env_var(&env_list,"SBBSCTRL",cfg.ctrl_dir);
add_env_var(&env_list,"SBBSDATA",cfg.data_dir);
add_env_var(&env_list,"SBBSEXEC",cfg.exec_dir);
sprintf(str,"%d",cfg.node_num);
add_env_var(&env_list,"SBBSNNUM",str);
/* date/time env vars */
sprintf(str,"%02u",tm.tm_mday);
add_env_var(&env_list,"DAY",str);
add_env_var(&env_list,"WEEKDAY",wday[tm.tm_wday]);
add_env_var(&env_list,"MONTHNAME",mon[tm.tm_mon]);
sprintf(str,"%02u",tm.tm_mon+1);
add_env_var(&env_list,"MONTH",str);
sprintf(str,"%u",1900+tm.tm_year);
add_env_var(&env_list,"YEAR",str);
env_strings=GetEnvironmentStrings();
env_block=strListCopyBlock(env_strings);
if(env_strings!=NULL)
FreeEnvironmentStrings(env_strings);
env_block=strListAppendBlock(env_block,env_list);
strListFree(&env_list);
if(env_block==NULL) {
XTRN_CLEANUP;
errormsg(WHERE, ERR_CREATE, "env_block", 0);
return(errno);
}
} else { // DOS external
// DOS-compatible (short) paths
char node_dir[MAX_PATH+1];
char ctrl_dir[MAX_PATH+1];
char data_dir[MAX_PATH+1];
char exec_dir[MAX_PATH+1];
// in case GetShortPathName fails
SAFECOPY(node_dir,cfg.node_dir);
SAFECOPY(ctrl_dir,cfg.ctrl_dir);
SAFECOPY(data_dir,cfg.data_dir);
SAFECOPY(exec_dir,cfg.exec_dir);
GetShortPathName(cfg.node_dir,node_dir,sizeof(node_dir));
GetShortPathName(cfg.ctrl_dir,ctrl_dir,sizeof(node_dir));
GetShortPathName(cfg.data_dir,data_dir,sizeof(data_dir));
GetShortPathName(cfg.exec_dir,exec_dir,sizeof(exec_dir));
sprintf(path,"%sDOSXTRN.RET", cfg.node_dir);
remove(path);
// Create temporary environment file
sprintf(path,"%sDOSXTRN.ENV", node_dir);
FILE* fp=fopen(path,"w");

rswindell
committed
XTRN_CLEANUP;
errormsg(WHERE, ERR_CREATE, path, 0);
}
fprintf(fp, "%s\n", fullcmdline);
fprintf(fp, "DSZLOG=%sPROTOCOL.LOG\n", node_dir);
fprintf(fp, "SBBSNODE=%s\n", node_dir);
fprintf(fp, "SBBSCTRL=%s\n", ctrl_dir);
fprintf(fp, "SBBSDATA=%s\n", data_dir);
fprintf(fp, "SBBSEXEC=%s\n", exec_dir);
fprintf(fp, "SBBSNNUM=%d\n", cfg.node_num);
/* date/time env vars */
fprintf(fp, "DAY=%02u\n", tm.tm_mday);
fprintf(fp, "WEEKDAY=%s\n",wday[tm.tm_wday]);
fprintf(fp, "MONTHNAME=%s\n",mon[tm.tm_mon]);
fprintf(fp, "MONTH=%02u\n",tm.tm_mon+1);
fprintf(fp, "YEAR=%u\n",1900+tm.tm_year);
sprintf(fullcmdline, "%sDOSXTRN.EXE %s", cfg.exec_dir, path);
if(!(mode&EX_OFFLINE) && nt) { // Windows NT/2000
i=SBBSEXEC_MODE_FOSSIL;
if(mode&EX_INR)
i|=SBBSEXEC_MODE_DOS_IN;
if(mode&EX_OUTR)
i|=SBBSEXEC_MODE_DOS_OUT;
sprintf(str," NT %u %u"
,cfg.node_num,i);
strcat(fullcmdline,str);
sprintf(str,"sbbsexec_hungup%d",cfg.node_num);
if((hungup_event=CreateEvent(
NULL // pointer to security attributes
,TRUE // flag for manual-reset event
,FALSE // flag for initial state
,str // pointer to event-object name
))==NULL) {
errormsg(WHERE, ERR_CREATE, str, 0);
return(GetLastError());
}
sprintf(str,"sbbsexec_hangup%d",cfg.node_num);
if((hangup_event=CreateEvent(
NULL // pointer to security attributes
,TRUE // flag for manual-reset event
,FALSE // flag for initial state
,str // pointer to event-object name
))==NULL) {
XTRN_CLEANUP;
errormsg(WHERE, ERR_CREATE, str, 0);
return(GetLastError());
}
sprintf(str,"\\\\.\\mailslot\\sbbsexec\\rd%d"
,cfg.node_num);
rdslot=CreateMailslot(str
,sizeof(buf)/2 // Maximum message size (0=unlimited)
,0 // Read time-out
,NULL); // Security
if(rdslot==INVALID_HANDLE_VALUE) {
errormsg(WHERE, ERR_CREATE, str, 0);
return(GetLastError());
}
}
else if(!(mode&EX_OFFLINE)) {
// Load vxd to intercept interrupts
sprintf(str,"\\\\.\\%s%s",cfg.exec_dir, SBBSEXEC_VXD);
if((vxd=CreateFile(str,0,0,0
,CREATE_NEW, FILE_FLAG_DELETE_ON_CLOSE,0))
==INVALID_HANDLE_VALUE) {
errormsg(WHERE, ERR_OPEN, str, 0);
return(GetLastError());
}
if((start_event=CreateEvent(
NULL // pointer to security attributes
,TRUE // flag for manual-reset event
,FALSE // flag for initial state
,NULL // pointer to event-object name
))==NULL) {
errormsg(WHERE, ERR_CREATE, "exec start event", 0);
return(GetLastError());
}
if(OpenVxDHandle!=NULL)
start.event=OpenVxDHandle(start_event);
else
start.event=start_event;
start.mode=SBBSEXEC_MODE_FOSSIL;
if(mode&EX_INR)
start.mode|=SBBSEXEC_MODE_DOS_IN;
if(mode&EX_OUTR)
start.mode|=SBBSEXEC_MODE_DOS_OUT;
sprintf(str," 95 %u %u"
,cfg.node_num,start.mode);

rswindell
committed
strcat(fullcmdline,str);
if(!DeviceIoControl(
vxd, // handle to device of interest
SBBSEXEC_IOCTL_START, // control code of operation to perform
&start, // pointer to buffer to supply input data
sizeof(start), // size of input buffer
NULL, // pointer to buffer to receive output data
0, // size of output buffer
&rd, // pointer to variable to receive output byte count
NULL // Overlapped I/O
)) {
errormsg(WHERE, ERR_IOCTL, SBBSEXEC_VXD, SBBSEXEC_IOCTL_START);
return(GetLastError());
}
}
}
if(startup_dir!=NULL && startup_dir[0])
p_startup_dir=startup_dir;
else
p_startup_dir=NULL;
STARTUPINFO startup_info={0};
startup_info.cb=sizeof(startup_info);
if(mode&EX_OFFLINE)
startup_info.lpTitle=NULL;
else {
sprintf(title,"%s running %s on node %d"
,useron.number ? useron.alias : "Event"
,realcmdline
,cfg.node_num);
startup_info.lpTitle=title;
}
if(startup->options&BBS_OPT_XTRN_MINIMIZED) {
startup_info.wShowWindow=SW_SHOWMINNOACTIVE;
startup_info.dwFlags|=STARTF_USESHOWWINDOW;
}
if(use_pipes) {
// Set up the security attributes struct.
SECURITY_ATTRIBUTES sa;
memset(&sa,0,sizeof(sa));
sa.nLength= sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
// Create the child output pipe (override default 4K buffer size)
if(!CreatePipe(&rdoutpipe,&startup_info.hStdOutput,&sa,sizeof(buf))) {
errormsg(WHERE,ERR_CREATE,"stdout pipe",0);
return(GetLastError());
}
startup_info.hStdError=startup_info.hStdOutput;
// Create the child input pipe.
if(!CreatePipe(&startup_info.hStdInput,&wrinpipe,&sa,sizeof(buf))) {
errormsg(WHERE,ERR_CREATE,"stdin pipe",0);
return(GetLastError());
}
DuplicateHandle(
GetCurrentProcess(), rdoutpipe,
GetCurrentProcess(), &rdslot, 0, FALSE, DUPLICATE_SAME_ACCESS);
DuplicateHandle(
GetCurrentProcess(), wrinpipe,
GetCurrentProcess(), &wrslot, 0, FALSE, DUPLICATE_SAME_ACCESS);
CloseHandle(rdoutpipe);
CloseHandle(wrinpipe);
startup_info.dwFlags|=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
startup_info.wShowWindow=SW_HIDE;
}
if(native && !(mode&EX_OFFLINE)) {
if(!(mode&EX_INR) && input_thread_running) {
pthread_mutex_lock(&input_thread_mutex);
input_thread_mutex_locked=true;
}
if(!(mode&EX_OUTR)) { /* Native Socket I/O program */
/* Enable the Nagle algorithm */
BOOL nodelay=FALSE;
setsockopt(client_socket,IPPROTO_TCP,TCP_NODELAY,(char*)&nodelay,sizeof(nodelay));
}
success=CreateProcess(
NULL, // pointer to name of executable module
fullcmdline, // pointer to command line string
NULL, // process security attributes
NULL, // thread security attributes
native && !(mode&EX_OFFLINE), // handle inheritance flag
CREATE_NEW_CONSOLE/*|CREATE_SEPARATE_WOW_VDM*/, // creation flags
env_block, // pointer to new environment block
p_startup_dir, // pointer to current directory name
&startup_info, // pointer to STARTUPINFO
&process_info // pointer to PROCESS_INFORMATION
);
strListFreeBlock(env_block);
if(!success) {
if(input_thread_mutex_locked && input_thread_running) {
pthread_mutex_unlock(&input_thread_mutex);
input_thread_mutex_locked=false;
}
SetLastError(last_error); /* Restore LastError */
errormsg(WHERE, ERR_EXEC, realcmdline, mode);
return(GetLastError());
}

rswindell
committed
#if 0
char dbgstr[256];
sprintf(dbgstr,"Node %d created: hProcess %X hThread %X processID %X threadID %X\n"
,cfg.node_num
,process_info.hProcess
,process_info.hThread
,process_info.dwProcessId
,process_info.dwThreadId);
OutputDebugString(dbgstr);
#endif
CloseHandle(process_info.hThread);
if(!native) {
if(!(mode&EX_OFFLINE) && !nt) {
// Wait for notification from VXD that new VM has started
if((retval=WaitForSingleObject(start_event, 5000))!=WAIT_OBJECT_0) {
TerminateProcess(process_info.hProcess, __LINE__);
CloseHandle(process_info.hProcess);
errormsg(WHERE, ERR_TIMEOUT, "start_event", retval);
return(GetLastError());
}
CloseHandle(start_event);
start_event=NULL; /* Mark as closed */
if(!DeviceIoControl(
vxd, // handle to device of interest
SBBSEXEC_IOCTL_COMPLETE, // control code of operation to perform
NULL, // pointer to buffer to supply input data
0, // size of input buffer
&hVM, // pointer to buffer to receive output data
sizeof(hVM), // size of output buffer
&rd, // pointer to variable to receive output byte count
NULL // Overlapped I/O
)) {
TerminateProcess(process_info.hProcess, __LINE__);
CloseHandle(process_info.hProcess);
errormsg(WHERE, ERR_IOCTL, SBBSEXEC_VXD, SBBSEXEC_IOCTL_COMPLETE);
return(GetLastError());
}
}
}

rswindell
committed
ReleaseMutex(exec_mutex);
/* Disable Ctrl-C checking */

rswindell
committed
rio_abortable=false;

rswindell
committed
// Executing app in foreground?, monitor

rswindell
committed
while(!(mode&EX_BG)) {
if(mode&EX_CHKTIME)
gettimeleft();
if(!online && !(mode&EX_OFFLINE)) { // Tell VXD/VDD and external that user hung-up
if(was_online) {
sprintf(str,"%s hung-up in external program",useron.alias);
logline(LOG_NOTICE,"X!",str);
hungup=time(NULL);
if(!native) {
if(nt)
SetEvent(hungup_event);
else if(!DeviceIoControl(
vxd, // handle to device of interest
SBBSEXEC_IOCTL_DISCONNECT, // operation to perform
&hVM, // pointer to buffer to supply input data
sizeof(hVM),// size of input buffer
NULL, // pointer to buffer to receive output data
0, // size of output buffer
&rd, // pointer to variable to receive output byte count
NULL // Overlapped I/O
)) {
errormsg(WHERE, ERR_IOCTL, SBBSEXEC_VXD, SBBSEXEC_IOCTL_DISCONNECT);
break;
}
}
was_online=false;
}
if(hungup && time(NULL)-hungup>5 && !processTerminated) {
lprintf(LOG_INFO,"Node %d Terminating process from line %d",cfg.node_num,__LINE__);
processTerminated=TerminateProcess(process_info.hProcess, 2112);
}
if((native && !use_pipes) || mode&EX_OFFLINE) {
/* Monitor for process termination only */
if(WaitForSingleObject(process_info.hProcess,1000)==WAIT_OBJECT_0)
break;
if(nt || use_pipes) { // Windows NT/2000
/* Write to VDD */
wr=RingBufPeek(&inbuf,buf,sizeof(buf));
if(wr) {
if(!use_pipes && wrslot==INVALID_HANDLE_VALUE) {
sprintf(str,"\\\\.\\mailslot\\sbbsexec\\wr%d"
,cfg.node_num);
wrslot=CreateFile(str
,GENERIC_WRITE
,FILE_SHARE_READ
,NULL
,OPEN_EXISTING
,FILE_ATTRIBUTE_NORMAL
,(HANDLE) NULL);
#if 0
if(wrslot==INVALID_HANDLE_VALUE) {
errormsg(WHERE,ERR_OPEN,str,0);
break;
}
#endif
}
/* CR expansion */
if(use_pipes)
bp=cr_expand(buf,wr,output_buf,wr);
else
bp=buf;
if(wrslot!=INVALID_HANDLE_VALUE
&& WriteFile(wrslot,bp,wr,&len,NULL)==TRUE) {
RingBufRead(&inbuf, NULL, len);
wr=len;
if(use_pipes && !(mode&EX_NOECHO)) {
/* echo */
RingBufWrite(&outbuf, bp, wr);
} else // VDD not loaded yet
wr=0;
}
/* Read from VDD */
rd=0;
avail=RingBufFree(&outbuf)/2; // leave room for wwiv/telnet expansion
if(avail==0)
lprintf("Node %d !output buffer full (%u bytes)"
,cfg.node_num,RingBufFull(&outbuf));
while(rd<len) {
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
if(use_pipes)
PeekNamedPipe(
rdslot, // handle to pipe to copy from
NULL, // pointer to data buffer
0, // size, in bytes, of data buffer
NULL, // pointer to number of bytes read
&waiting, // pointer to total number of bytes available
NULL // pointer to unread bytes in this message
);
else
GetMailslotInfo(
rdslot, // mailslot handle
NULL, // address of maximum message size
NULL, // address of size of next message
&waiting, // address of number of messages
NULL // address of read time-out
);
if(!waiting)
break;
if(ReadFile(rdslot,buf+rd,len-rd,&msglen,NULL)==FALSE || msglen<1)
break;
rd+=msglen;
}
if(rd) {
if(mode&EX_WWIV) {
bp=wwiv_expand(buf, rd, wwiv_buf, rd, useron.misc, wwiv_flag);
if(rd>sizeof(wwiv_buf))
errorlog("WWIV_BUF OVERRUN");
} else if(telnet_mode&TELNET_MODE_OFF) {
bp=buf;
bp=telnet_expand(buf, rd, telnet_buf, rd);
if(rd>sizeof(telnet_buf))
errorlog("TELNET_BUF OVERRUN");
}
if(rd>RingBufFree(&outbuf)) {
errorlog("output buffer overflow");
rd=RingBufFree(&outbuf);
}
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
RingBufWrite(&outbuf, bp, rd);
}
} else { // Windows 9x
/* Write to VXD */
wr=RingBufPeek(&inbuf, buf+sizeof(hVM),sizeof(buf)-sizeof(hVM));
if(wr) {
*(DWORD*)buf=hVM;
wr+=sizeof(hVM);
if(!DeviceIoControl(
vxd, // handle to device of interest
SBBSEXEC_IOCTL_WRITE, // control code of operation to perform
buf, // pointer to buffer to supply input data
wr, // size of input buffer
&rd, // pointer to buffer to receive output data
sizeof(rd), // size of output buffer
&dummy, // pointer to variable to receive output byte count
NULL // Overlapped I/O
)) {
errormsg(WHERE, ERR_IOCTL, SBBSEXEC_VXD, SBBSEXEC_IOCTL_READ);
break;
}
RingBufRead(&inbuf, NULL, rd);
wr=rd;
}
/* Read from VXD */
rd=0;
len=sizeof(buf);
avail=RingBufFree(&outbuf)/2; // leave room for wwiv/telnet expansion
#if 0
if(avail==0)
lprintf("Node %d !output buffer full (%u bytes)"
,cfg.node_num,RingBufFull(&outbuf));
if(!DeviceIoControl(
vxd, // handle to device of interest
SBBSEXEC_IOCTL_READ, // control code of operation to perform
&hVM, // pointer to buffer to supply input data
sizeof(hVM), // size of input buffer
buf, // pointer to buffer to receive output data
len, // size of output buffer
&rd, // pointer to variable to receive output byte count
NULL // Overlapped I/O
)) {
errormsg(WHERE, ERR_IOCTL, SBBSEXEC_VXD, SBBSEXEC_IOCTL_READ);
break;
}
if(mode&EX_WWIV) {
bp=wwiv_expand(buf, rd, wwiv_buf, rd, useron.misc, wwiv_flag);
if(rd>sizeof(wwiv_buf))
errorlog("WWIV_BUF OVERRUN");
} else if(telnet_mode&TELNET_MODE_OFF) {
bp=buf;
} else {
bp=telnet_expand(buf, rd, telnet_buf, rd);
if(rd>sizeof(telnet_buf))
errorlog("TELNET_BUF OVERRUN");
}
if(rd>RingBufFree(&outbuf)) {
errorlog("output buffer overflow");
rd=RingBufFree(&outbuf);
}
RingBufWrite(&outbuf, bp, rd);
}
}
#if defined(_DEBUG) && 0
if(rd>1) {
sprintf(str,"Node %d read %5d bytes from xtrn", cfg.node_num, rd);
OutputDebugString(str);
}
#endif
if((!rd && !wr) || hungup) {
loop_since_io++; /* number of loop iterations with no I/O */