Пример #1
0
/**
 * Try to handle special commands like "cd" or "export" internally.  This will
 * work if we have a simple commend with no redirection, piping, or such.
 *
 * @param context    The Exit oontext.
 * @param cmd        The command string.
 * @param rc         The operating system return code.
 *
 * @return           True if command was handled, false otherwise.
 */
logical_t handleCommandInternally(RexxExitContext *context, char *cmd, RexxObjectPtr rc)
{
    /* check for redirection symbols, ignore them when enclosed in double quotes.
       escaped quotes are ignored. */
    bool noDirectInvoc = false;
    bool inQuotes = false;
    bool escape = false;
    size_t i;
    for (i = 0; i<strlen(cmd); i++)
    {
        if (escape)
        {
            escape = false;
        }
        else if (cmd[i] == '\\')
        {
            escape = true;
        }
        else if (cmd[i] == '"')
        {
            inQuotes = !inQuotes;
        }
        else
        {
            /* if we're in the unquoted part and the current character is one of */
            /* the redirection characters or the & for multiple commands then we */
            /* will no longer try to invoke the command directly                 */
            if (!inQuotes && (strchr("<>|&;", cmd[i]) != NULL))
            {
                noDirectInvoc = true;
                break;
            }
        }
    }

    if (!noDirectInvoc)
    {
        // execute special commands in the same process
        // we currently don't handle formats like " cd" or "'cd'" internally
        if (strcmp("cd", cmd) == 0 || strncmp("cd ", cmd, 3) == 0)
        {
            return sys_process_cd(context, cmd, rc);
        }
        if (strncmp("set ", cmd, 4) == 0)
        {
            return sys_process_export(context, cmd, rc, SET_FLAG);
        }
        if (strncmp("unset ", cmd, 6) == 0)
        {
            return sys_process_export(context, cmd, rc, UNSET_FLAG);
        }
        if (strncmp("export ", cmd, 7) == 0)
        {
            return sys_process_export(context, cmd, rc, EXPORT_FLAG);
        }
    }
    return false;
}
RexxObjectPtr RexxEntry systemCommandHandler(RexxExitContext *context, RexxStringObject address, RexxStringObject command)
{
    const char *cmd = context->StringData(command);
    const char *envName = context->StringData(address);

    RexxObjectPtr rc = NULLOBJECT;

    /* check for redirection symbols, ignore them when enclosed in double quotes.
       escaped quotes are ignored. */
    bool noDirectInvoc = false;
    bool inQuotes = false;
    bool escape = false;
    size_t i;
    for (i = 0; i<strlen(cmd); i++)
    {
        if (escape)
        {
            escape = false;
        }
        else if (cmd[i] == '\\')
        {
            escape = true;
        }
        else if (cmd[i] == '"')
        {
            inQuotes = !inQuotes;
        }
        else
        {
            /* if we're in the unquoted part and the current character is one of */
            /* the redirection characters or the & for multiple commands then we */
            /* will no longer try to invoke the command directly                 */
            if (!inQuotes && (strchr("<>|&", cmd[i]) != NULL))
            {
                noDirectInvoc = true;
                break;
            }
        }
    }

    if (!noDirectInvoc)
    {
        /* execute 'cd' in the same process */
        size_t commandLen = strlen(cmd);

        if (strcmp(cmd, "cd") == 0)
        {
            if (sys_process_cd(context, cmd, rc))
            {
                return rc;
            }
        }
        else if (commandLen >= 3)
        {
            char tmp[16];
            strncpy(tmp, cmd, 3);
            tmp[3] = '\0';
            if (strcmp("cd ",tmp) == 0)
            {
                if (sys_process_cd(context, cmd, rc))
                {
                    return rc;
                }
            }
            strncpy(tmp, cmd, 4);
            tmp[4] = '\0';
            if (strcmp("set ",tmp) == 0)
            {
                if (sys_process_export(context, cmd, rc, SET_FLAG)) /*unset works fine for me*/
                {
                    return rc;
                }
            }
            strncpy(tmp, cmd, 6);
            tmp[6] = '\0';
            if (Utilities::strCaselessCompare("unset ", tmp) == 0)
            {
                if (sys_process_export(context, cmd, rc, UNSET_FLAG))
                {
                    return rc;
                }
            }
            strncpy(tmp, cmd, 7);
            tmp[7] = '\0';
            if (Utilities::strCaselessCompare("export ", tmp) == 0)
            {
                if (sys_process_export(context, cmd, rc, EXPORT_FLAG))
                {
                    return rc;
                }
            }
        }
    }


    /****************************************************************************/
    /* Invoke the system command handler to execute the command                 */
    /****************************************************************************/
    // if this is the null string, then use the default address environment
    // for the platform
    if (strlen(envName) == 0)
    {
        envName = SYSINITIALADDRESS;
    }

    int errCode = 0;

#ifdef LINUX

    if (Utilities::strCaselessCompare("bash", envName) == 0)
    {
        errCode = system( cmd );
        if ( errCode >= 256 )
        {
            errCode = errCode / 256;
        }
    }
    else
#endif
    {
        int pid = fork();
        int status;

        if (pid != 0)                         /* spawn a child process to run the  */
        {
            waitpid ( pid, &status, 0);          /* command and wait for it to finish */
            if (WIFEXITED(status))               /* If cmd process ended normal       */
            {
                                                 /* Give 'em the exit code            */
                errCode = WEXITSTATUS(status);
            }
            else                              /* Else process died ugly, so        */
            {
                errCode = -(WTERMSIG(status));
                if (errCode == 1)                    /* If process was stopped            */
                {
                    errCode = -1;                   /* Give 'em a -1.                    */
                }
            }
        }
        else
        {
        	/* run the command in the child      */
            if (Utilities::strCaselessCompare("sh", envName) == 0)
            {
				#ifdef ANDROID
            		execl("/system/bin/sh", "sh", "-c", cmd, NULL);
				#else
                    execl("/bin/sh", "sh", "-c", cmd, NULL);
				#endif
            }
            else if (Utilities::strCaselessCompare("ksh", envName) == 0)
            {
                execl("/bin/ksh", "ksh", "-c", cmd, NULL);
            }
            else if (Utilities::strCaselessCompare("bsh", envName) == 0)
            {
                execl("/bin/bsh", "bsh", "-c", cmd, NULL);
            }
            else if (Utilities::strCaselessCompare("csh", envName) == 0)
            {
                execl("/bin/csh", "csh", "-c", cmd, NULL);
            }
            else if (Utilities::strCaselessCompare("bash", envName) == 0)
            {
                execl("/bin/bash", "bash", "-c", cmd, NULL);
            }
            else if (Utilities::strCaselessCompare("cmd", envName) == 0)
            {
                char * args[MAX_COMMAND_ARGS+1];      /* Array for argument parsing */
                if (!scan_cmd(cmd, args))             /* Parse cmd into arguments  */
                {
                    exit(1);
                }
                execvp(args[0], args);           /* Invoke command directly   */
                exit(1);                         /* we couldn't run the       */
            }
            else
            {
                execl("/bin/sh", "sh", "-c", cmd, NULL);
            }
        }
    }
    // unknown command code?
    if (errCode == UNKNOWN_COMMAND)
    {
        // failure condition
        context->RaiseCondition("FAILURE", context->String(cmd), NULL, context->WholeNumberToObject(errCode));
    }
    else if (errCode != 0)
    {
        // non-zero is an error condition
        context->RaiseCondition("ERROR", context->String(cmd), NULL, context->WholeNumberToObject(errCode));
    }

    return context->False();      // zero return code
}
RexxObjectPtr RexxEntry systemCommandHandler(RexxExitContext *context, RexxStringObject address, RexxStringObject command)
{
    // address the command information
    const char *cmd = context->StringData(command);
    const char *cl_opt = " /c "; // The "/c" opt for system commandd handler
    const char *interncmd;

    // Remove the "quiet sign" if present
    if (cmd[0] == '@')
    {
        interncmd = cmd + 1;
    }
    else
    {
        interncmd = cmd;
    }

    /* Check for redirection symbols, ignore them when enclosed in double
     * quotes.  If there are more than 2 double qoutes, cmd.exe strips off the
     * first and last.  To preserve what the user sent to us, we count the
     * double quotes, and, if more than two, add a double quote to the front and
     * end of the string.
     */
    size_t quoteCount    = 0;
    bool   noDirectInvoc = false;
    bool   inQuotes      = false;
    size_t i;

    for (i = 0; i < strlen(interncmd); i++)
    {
        if (interncmd[i] == '"')
        {
            inQuotes = !inQuotes;
            quoteCount++;
        }
        else
        {
            /* if we're in the unquoted part and the current character is one of */
            /* the redirection characters or the & for multiple commands then we */
            /* will no longer try to invoke the command directly                 */
            if (!inQuotes && (strchr("<>|&", interncmd[i]) != NULL))
            {
                noDirectInvoc = true;
                if ( quoteCount > 2 )
                {
                    break;
                }
            }
        }
    }

    i = 0; // reset to zero for next usage

    // scan for the first non-whitespace character
    size_t j = 0;
    while (interncmd[j] == ' ')
    {
        j++;
    }

    RexxObjectPtr result = NULLOBJECT;

    if (!noDirectInvoc)
    {
        char tmp[8];
        strncpy(tmp, &interncmd[j], 4);
        tmp[4] = '\0';

        if (!stricmp("set ",tmp))
        {
            if (sys_process_set(context, cmd, &interncmd[j], result))
            {
                return result;
            }
        }
        else
        {
            strncpy(tmp, &interncmd[j], 3);
            tmp[3] = '\0';
            if (!stricmp("cd ",tmp))
            {
                if (sys_process_cd(context, cmd, &interncmd[j], result))
                {
                    return result;
                }
            }
            else
            {   // Check if the command is to change drive
                if ((tmp[1] == ':') && ((tmp[2] == ' ') || (!tmp[2])))
                {
                    int code = _chdrive(toupper( tmp[0] ) - 'A' + 1);
                    if (code != 0)
                    {
                        context->RaiseCondition("ERROR", command, NULLOBJECT, context->WholeNumberToObject(code));
                        return NULLOBJECT;
                    }
                    else
                    {
                        // 0 result.
                        return context->False();
                    }
                }
                else
                {
                    // Check if a START command is specified, if so do not
                    // invoke the command directly.
                    strncpy(tmp, &interncmd[j], 6);
                    tmp[6] = '\0';
                    noDirectInvoc = stricmp("start ",tmp) == 0;
                }
            }
        }
    }

    const char *sys_cmd_handler; // Pointer to system cmd handler

    // Determine the system command interpreter.  This could be the full path
    // name if COMSPEC is set, or a default name if it isn't.  We no longer
    // suport Windows 95, so the default will be cmd.exe. But, it is still
    // possible for people to set COMSPEC to command.com, so we could end up
    // with command.com
    if ( (sys_cmd_handler = getenv(COMSPEC)) == NULL )
    {
        sys_cmd_handler = CMDDEFNAMENT;
    }

    // Determine the maximum possible buffer size needed to pass the final
    // command to sysCommandNT().
    size_t maxBufferSize = strlen(sys_cmd_handler) + 1
                           + strlen(cl_opt)
                           + strlen(&interncmd[j])
                           + 2   // Two possible extra quotes
                           + 1;  // Terminating null

    char  cmdstring[CMDBUFSIZENT];    // Default static buffer.
    char *cmdstring_ptr = cmdstring;  // Will point to static buffer.

    if ( maxBufferSize > CMDBUFSIZENT )
    {
        // Allocate dynamic memory and set cmdstring_ptr to point to it.
        cmdstring_ptr = (char *)LocalAlloc(LPTR, maxBufferSize);
        if ( cmdstring_ptr == NULL )
        {
            context->RaiseException1(Rexx_Error_System_resources_user_defined,
                                     context->String("Failed to allocate memory"));
            return NULLOBJECT;
        }
    }
    else
    {
        // We want maxBufferSize to relect the actual size of the buffer we are
        // using so that we can test the return from SearchPath()
        maxBufferSize = CMDBUFSIZENT;
    }

    SystemInterpreter::exceptionConsole = false;
    SystemInterpreter::explicitConsole = false;

    // Check whether or not the command to invoke is cmd.exe or command.com
    _strupr(strcpy(cmdstring_ptr, &interncmd[j]));
    bool searchFile = strstr(cmdstring_ptr, "CMD") != NULL;

    if (searchFile)
    {
        if (cmdstring_ptr[0] == '\"')
        {
            cmdstring_ptr++;
            while (cmdstring_ptr[i] && (cmdstring_ptr[i] != '\"'))
            {
                i++;
            }
            cmdstring_ptr[i]='\0';
        }
        else if (cmdstring_ptr[0] == '\'')
        {
            cmdstring_ptr++;
            while (cmdstring_ptr[i] && (cmdstring_ptr[i] != '\''))
            {
                i++;
            }
            cmdstring_ptr[i]='\0';
        }
        else
        {
            while (cmdstring_ptr[i] && (cmdstring_ptr[i] != ' '))
            {
                i++;
            }
            cmdstring_ptr[i]='\0';
        }

        LPSTR filepart;
        uint32_t count = SearchPath(NULL, cmdstring_ptr, ".EXE", (uint32_t)(maxBufferSize - 1), cmdstring, &filepart);
        bool fileFound = count != 0 && count <= maxBufferSize;

        // Set pointer back again to cmd buffer (might have been increased)
        cmdstring_ptr = cmdstring;

        if (fileFound && !stricmp(sys_cmd_handler, cmdstring_ptr))
        {
            SystemInterpreter::exceptionConsole = true;
            SystemInterpreter::explicitConsole = true;
        }
    }

    // First check whether we can run the command directly as a program. (There
    // can be no file redirection when not using cmd.exe or command.com)
    if (SystemInterpreter::explicitConsole || !noDirectInvoc)
    {
        // Invoke this directly.  If we fail, we fall through and try again.
        if (sysCommandNT(context, cmd, &interncmd[j], true, result))
        {
            if ( cmdstring_ptr != cmdstring )
            {
                // Not pointing to static buffer so we need to free it.
                LocalFree(cmdstring_ptr);
            }
            return result;
        }
    }

    // We couldn't invoke the command directly, or we tried and failed.  So,
    // pass the command to cmd.exe or command.com.


    // Start the command buffer with the system cmd handler
    strcpy(cmdstring_ptr,sys_cmd_handler);

    // Check whether or not the user specified the /k option.  If so do not use
    // the /c option.  The /k option can only be specified as the first
    // argument, and if used, keeps the command handler process open after the
    // command has finished.  Normally the /c option would be usee to close the
    // command handler process when the command is finished..
    if (!( (strlen(interncmd) > j+1) && (interncmd[j] == '/')
           && ((interncmd[j+1] == 'k') || (interncmd[j+1] == 'K'))
           && ((interncmd[j+2] == ' ') || (interncmd[j+2] == '\0')) ))
    {
        strcat(cmdstring_ptr,cl_opt);
    }
    else
    {
        SystemInterpreter::exceptionConsole = true;
        strcat(cmdstring_ptr," ");
    }

    // Add cmd to be executed, possibly quoting it to preserve embedded quotes.
    if ( quoteCount> 2 )
    {
        strcat(cmdstring_ptr,"\"");
        strcat(cmdstring_ptr,interncmd);
        strcat(cmdstring_ptr,"\"");
    }
    else
    {
        strcat(cmdstring_ptr,interncmd);
    }

    // Invoke the command
    if (!sysCommandNT(context, cmd, cmdstring_ptr, false, result))
    {
        // Failed, get error code and return
        context->RaiseCondition("FAILURE", context->String(cmd), NULLOBJECT, context->WholeNumberToObject(GetLastError()));

        if ( cmdstring_ptr != cmdstring )
        {
            // Not pointing to static buffer so we need to free it.
            LocalFree(cmdstring_ptr);
        }
        return NULLOBJECT;
    }

    if ( cmdstring_ptr != cmdstring )
    {
        // Not pointing to static buffer so we need to free it.
        LocalFree(cmdstring_ptr);
    }
    SystemInterpreter::exceptionConsole = false;
    return result;
}