Annotation of sbbs/src/sbbs3/xtrn.cpp, revision 1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.