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

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

unix.superglobalmegacorp.com

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