/*** *spawnve.c - Low level routine eventually called by all _spawnXX routines * also contains all code for _execve, called by _execXX routines * * Copyright (c) Microsoft Corporation. All rights reserved. * *Purpose: * * This is the low level routine which is eventually invoked by all the * _spawnXX routines. * * This is also the low-level routine which is eventually invoked by * all of the _execXX routines. * *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #define SLASHCHAR _T('\\') #define XSLASHCHAR _T('/') #ifndef EXECVE #ifdef WPRFLAG static intptr_t __cdecl wcomexecmd(int mode, const wchar_t * name, const wchar_t * const * argv, const wchar_t * const * envp); #else /* WPRFLAG */ static intptr_t __cdecl comexecmd(int mode, const char * name, const char * const * argv, const char * const * envp); #endif /* WPRFLAG */ #else /* EXECVE */ #ifdef WPRFLAG static intptr_t __cdecl wcomexecmd(const wchar_t * name, const wchar_t * const * argv, const wchar_t * const * envp); #else /* WPRFLAG */ static intptr_t __cdecl comexecmd(const char * name, const char * const * argv, const char * const * envp); #endif /* WPRFLAG */ #endif /* EXECVE */ /*** *static int comexecmd(mode, name, argv, envp) - do the exec * or spawn after name fixup * *Purpose: * Spawns a child process with given parameters and environment. Either * overlays current process or loads in free memory while parent process * waits. If the named file is a .cmd file, modifies the calling sequence * and prepends the /c and filename arguments into the command string * * Exec doesn't take a mode; instead, the parent process goes away as * the child process is brought in. * *Entry: * int mode - mode to spawn (WAIT, NOWAIT, or OVERLAY) * only WAIT and OVERLAY currently supported * * **** mode is only used in the spawnve() version **** * * _TSCHAR *name - pathname of file to spawn. Includes the extension * _TSCHAR **argv - vector of parameter strings * _TSCHAR **envp - vector of environment variables * *Exit: * returns exit code of child process * if fails, returns -1 * *Exceptions: * Returns a value of (-1) to indicate an error in exec'ing the child * process. errno may be set to: * * E2BIG = failed in argument/environment processing (_cenvarg) * argument list or environment too big; * EACCESS = locking or sharing violation on file; * EMFILE = too many files open; * ENOENT = failed to find program name - no such file or directory; * ENOEXEC = failed in exec - bad executable format; * ENOMEM = failed in memory allocation (during malloc, or in * setting up memory for executing child process). * *******************************************************************************/ #ifdef WPRFLAG static intptr_t __cdecl wcomexecmd ( #else /* WPRFLAG */ static intptr_t __cdecl comexecmd ( #endif /* WPRFLAG */ #ifndef EXECVE REG3 int mode, #endif /* EXECVE */ REG2 const _TSCHAR *name, const _TSCHAR * const *argv, const _TSCHAR * const *envp ) { _TSCHAR *argblk; _TSCHAR *envblk; REG4 intptr_t rc; /* validation section */ _VALIDATE_RETURN(name != NULL, EINVAL, -1); _VALIDATE_RETURN(argv != NULL, EINVAL, -1); #ifdef WPRFLAG if (_wcenvarg(argv, envp, &argblk, &envblk, name) == -1) #else /* WPRFLAG */ if (_cenvarg(argv, envp, &argblk, &envblk, name) == -1) #endif /* WPRFLAG */ return -1; #ifndef EXECVE #ifdef WPRFLAG rc = _wdospawn(mode, name, argblk, envblk); #else /* WPRFLAG */ rc = _dospawn(mode, name, argblk, envblk); #endif /* WPRFLAG */ #else /* EXECVE */ #ifdef WPRFLAG rc = _wdospawn(_P_OVERLAY, name, argblk, envblk); #else /* WPRFLAG */ rc = _dospawn(_P_OVERLAY, name, argblk, envblk); #endif /* WPRFLAG */ #endif /* EXECVE */ /* free memory */ _free_crt(argblk); _free_crt(envblk); return rc; } /*** *int _spawnve(mode, name, argv, envp) - low level _spawnXX library function *int _execve(name, argv, envp) - low level _execXX library function * *Purpose: * spawns or execs a child process; takes a single pointer to an argument * list as well as a pointer to the environment; unlike _spawnvpe, * _spawnve does not search the PATH= list in processing the name * parameter; mode specifies the parent's execution mode. * *Entry: * int mode - parent process's execution mode: * must be one of _P_OVERLAY, _P_WAIT, _P_NOWAIT; * not used for _execve * _TSCHAR *name - path name of program to spawn; * _TSCHAR **argv - pointer to array of pointers to child's arguments; * _TSCHAR **envp - pointer to array of pointers to child's environment * settings. * *Exit: * Returns : (int) a status value whose meaning is as follows: * 0 = normal termination of child process; * positive = exit code of child upon error termination * (abort or exit(nonzero)); * -1 = child process was not spawned; * errno indicates what kind of error: * (E2BIG, EINVAL, ENOENT, ENOEXEC, ENOMEM). * *Exceptions: * Returns a value of (-1) to indicate an error in spawning the child * process. errno may be set to: * * E2BIG = failed in argument/environment processing (_cenvarg) - * argument list or environment too big; * EINVAL = invalid mode argument; * ENOENT = failed to find program name - no such file or directory; * ENOEXEC = failed in spawn - bad executable format; * ENOMEM = failed in memory allocation (during malloc, or in * setting up memory for spawning child process). * *******************************************************************************/ /* Extension array - ordered in search order from right to left. ext_strings = array of extensions */ static _TSCHAR *ext_strings[] = { _T(".cmd"), _T(".bat"), _T(".exe"), _T(".com") }; enum {CMD, BAT, EXE, COM, EXTFIRST=CMD, EXTLAST=COM}; intptr_t __cdecl #ifndef EXECVE _tspawnve ( REG3 int mode, #else /* EXECVE */ _texecve ( #endif /* EXECVE */ const _TSCHAR *name, const _TSCHAR * const *argv, const _TSCHAR * const *envp ) { _TSCHAR *ext; /* where the extension goes if we have to add one */ REG1 _TSCHAR *p; _TSCHAR *q; REG2 _TSCHAR *pathname = (_TSCHAR *)name; REG4 intptr_t rc; REG5 int i; errno_t save_errno = 0; /* validation section */ _VALIDATE_RETURN(name != NULL, EINVAL, -1); _VALIDATE_RETURN(*name != _T('\0'), EINVAL, -1); _VALIDATE_RETURN(argv != NULL, EINVAL, -1); _VALIDATE_RETURN(*argv != NULL, EINVAL, -1); _VALIDATE_RETURN(**argv != _T('\0'), EINVAL, -1); p = _tcsrchr(pathname, SLASHCHAR); q = _tcsrchr(pathname, XSLASHCHAR); /* ensure that pathname is an absolute or relative pathname. also, * position p to point at the filename portion of pathname (i.e., just * after the last occurence of a colon, slash or backslash character */ if (!q) { if (!p) if (!(p = _tcschr(pathname, _T(':')))) { /* pathname is a filename only, force it to be * a relative pathname. note that an extra byte * is malloc-ed just in case pathname is NULL, * to keep the heap from being trashed by * strcpy */ size_t pathname_size = _tcslen(pathname) + 3; if (!(pathname = _calloc_crt(pathname_size, sizeof(_TSCHAR)))) return(-1); _ERRCHECK(_tcscpy_s(pathname, pathname_size, _T(".\\"))); _ERRCHECK(_tcscat_s(pathname, pathname_size, name)); /* set p to point to the start of the filename * (i.e., past the ".\\" prefix) */ p = pathname + 2; } /* else pathname has drive specifier prefix and p is * is pointing to the ':' */ } else if (!p || q > p) /* p == NULL or q > p */ p = q; rc = -1; /* init to error value */ if (ext = _tcsrchr(p, _T('.'))) { /* extension given; only do filename */ if (_taccess_s(pathname, 0) == 0) { #ifndef EXECVE #ifdef WPRFLAG rc = wcomexecmd(mode, pathname, argv, envp); #else /* WPRFLAG */ rc = comexecmd(mode, pathname, argv, envp); #endif /* WPRFLAG */ #else /* EXECVE */ #ifdef WPRFLAG rc = wcomexecmd(pathname, argv, envp); #else /* WPRFLAG */ rc = comexecmd(pathname, argv, envp); #endif /* WPRFLAG */ #endif /* EXECVE */ } } else { /* no extension; try .cmd/.bat, then .com and .exe */ size_t size = _tcslen(pathname) + 5; if (!(p = _calloc_crt(size, sizeof(_TSCHAR)))) return(-1); _ERRCHECK(_tcscpy_s(p, size, pathname)); ext = p + _tcslen(pathname); save_errno = errno; for (i = EXTLAST; i >= EXTFIRST; --i) { _ERRCHECK(_tcscpy_s(ext, size - (ext - p), ext_strings[i])); if (_taccess_s(p, 0) == 0) { errno = save_errno; #ifndef EXECVE #ifdef WPRFLAG rc = wcomexecmd(mode, p, argv, envp); #else /* WPRFLAG */ rc = comexecmd(mode, p, argv, envp); #endif /* WPRFLAG */ #else /* EXECVE */ #ifdef WPRFLAG rc = wcomexecmd(p, argv, envp); #else /* WPRFLAG */ rc = comexecmd(p, argv, envp); #endif /* WPRFLAG */ #endif /* EXECVE */ break; } } _free_crt(p); } if (pathname != name) _free_crt(pathname); return rc; }