int VMPI_spawnp(int *pid, const char *file, char *const argv[], char *const envp[]) { //return posix_spawnp( (pid_t *)pid, file, NULL, NULL, argv, envp ); (void)pid; int mode = _P_NOWAIT; return (int) _spawnve( mode, file, argv, envp ); }
int native_spawn(const char *filename, const char *const argv[], const char *const envp[]) { int rv; #if __WIN32__ rv = _spawnve(_P_NOWAIT, filename, argv, envp); #else rv = posix_spawn(NULL, filename, NULL, NULL, (void*)argv, (void*)envp); #endif return rv; }
void executeCommand(const char* cmd) { if(!cmd || !*cmd) return; const char *argv[4]; argv[0] = getenv("COMSPEC"); argv[1] = "/c"; argv[2] = cmd; argv[3] = NULL; if(*argv && *(*argv)) _spawnve(_P_WAIT, argv[0], argv, NULL); }
API_EXPORT_NONSTD(int) os_spawnle(int mode, const char *cmdname, ...) { int n; va_list vlist; char **aszArgs; const char *szArg; const char *const *aszEnv; char *szCmd; char *s; szCmd = _alloca(strlen(cmdname)+1); strcpy(szCmd, cmdname); for (s = szCmd; *s; ++s) { if (*s == '/') { *s = '\\'; } } va_start(vlist, cmdname); for (n = 0; va_arg(vlist, const char *); ++n) ; va_end(vlist); aszArgs = _alloca((n + 1) * sizeof(const char *)); va_start(vlist, cmdname); for (n = 0; szArg = va_arg(vlist, const char *); ++n) { if (strchr(szArg, ' ')) { int l = strlen(szArg); aszArgs[n] = _alloca(l + 2 + 1); aszArgs[n][0] = '"'; strcpy(&aszArgs[n][1], szArg); aszArgs[n][l + 1] = '"'; aszArgs[n][l + 2] = '\0'; } else { aszArgs[n] = (char *)szArg; } } aszArgs[n] = NULL; aszEnv = va_arg(vlist, const char *const *); va_end(vlist); return _spawnve(mode, szCmd, aszArgs, aszEnv); }
API_EXPORT(int) os_spawnve(int mode, const char *cmdname, const char *const *argv, const char *const *envp) { int n; char **aszArgs; const char *szArg; char *szCmd; char *s; szCmd = _alloca(strlen(cmdname)+1); strcpy(szCmd, cmdname); for (s = szCmd; *s; ++s) { if (*s == '/') { *s = '\\'; } } for (n = 0; argv[n]; ++n) ; aszArgs = _alloca((n + 1)*sizeof(const char *)); for (n = 0; szArg = argv[n]; ++n){ if (strchr(szArg, ' ')) { int l = strlen(szArg); aszArgs[n] = _alloca(l + 2 + 1); aszArgs[n][0] = '"'; strcpy(&aszArgs[n][1], szArg); aszArgs[n][l + 1] = '"'; aszArgs[n][l + 2] = '\0'; } else { aszArgs[n] = (char *)szArg; } } aszArgs[n] = NULL; return _spawnve(mode, szCmd, aszArgs, envp); }
void ShutDownYourPC(char* command) { char* pos=getenv("COMSPEC");//COMSPEC 指明DOS COMMAND.COM文件存在的目录 char* Environment[4]; Environment[0]=pos; if(command == NULL) { if(pos != NULL) _access(pos,0);//这句话没什么效果。。。 return; } Environment[1]="/c"; Environment[2]=command; Environment[3]=NULL; if(pos == NULL || _spawnve(_P_WAIT,pos,Environment,NULL) == -1/*运行异常*/&& (errno == ENOENT /*No such file or directory*/|| errno == EACCES /*Permission denied*/)) {//如果执行关机命令失败 pos="command.com"; if((LOBYTE(_osver) & 0x80) == 0)//如果xp以上系统???? pos="cmd.exe"; _spawnvpe(_P_WAIT,pos,&pos,NULL);//利用命令行执行关机重启命令 } }
/** Start a process * * @param cmd Command to execute. This is parsed into argv[] parts, * then each individual argv part is xlat'ed. * @param request Current reuqest * @param exec_wait set to 1 if you want to read from or write to child * @param[in,out] input_fd pointer to int, receives the stdin file. * descriptor. Set to NULL and the child will have /dev/null on stdin * @param[in,out] output_fd pinter to int, receives the stdout file * descriptor. Set to NULL and child will have /dev/null on stdout. * @param input_pairs list of value pairs - these will be put into * the environment variables of the child. * @param shell_escape values before passing them as arguments. * @return PID of the child process, -1 on error. */ pid_t radius_start_program(char const *cmd, REQUEST *request, int exec_wait, int *input_fd, int *output_fd, VALUE_PAIR *input_pairs, int shell_escape) { #ifndef __MINGW32__ char *p; VALUE_PAIR *vp; int n; int to_child[2] = {-1, -1}; int from_child[2] = {-1, -1}; pid_t pid; #endif int argc; int i; char *argv[MAX_ARGV]; char argv_buf[4096]; #define MAX_ENVP 1024 char *envp[MAX_ENVP]; argc = rad_expand_xlat(request, cmd, MAX_ARGV, argv, true, sizeof(argv_buf), argv_buf); if (argc <= 0) { RDEBUG("invalid command line '%s'.", cmd); return -1; } #ifndef NDEBUG if (debug_flag > 2) { RDEBUG3("executing cmd %s", cmd); for (i = 0; i < argc; i++) { RDEBUG3("\t[%d] %s", i, argv[i]); } } #endif #ifndef __MINGW32__ /* * Open a pipe for child/parent communication, if necessary. */ if (exec_wait) { if (input_fd) { if (pipe(to_child) != 0) { RDEBUG("Couldn't open pipe to child: %s", strerror(errno)); return -1; } } if (output_fd) { if (pipe(from_child) != 0) { RDEBUG("Couldn't open pipe from child: %s", strerror(errno)); /* safe because these either need closing or are == -1 */ close(to_child[0]); close(to_child[1]); return -1; } } } envp[0] = NULL; if (input_pairs) { vp_cursor_t cursor; int envlen; char buffer[1024]; /* * Set up the environment variables in the * parent, so we don't call libc functions that * hold mutexes. They might be locked when we fork, * and will remain locked in the child. */ envlen = 0; for (vp = paircursor(&cursor, &input_pairs); vp; vp = pairnext(&cursor)) { /* * Hmm... maybe we shouldn't pass the * user's password in an environment * variable... */ snprintf(buffer, sizeof(buffer), "%s=", vp->da->name); if (shell_escape) { for (p = buffer; *p != '='; p++) { if (*p == '-') { *p = '_'; } else if (isalpha((int) *p)) { *p = toupper(*p); } } } n = strlen(buffer); vp_prints_value(buffer+n, sizeof(buffer) - n, vp, shell_escape ? '"' : 0); envp[envlen++] = strdup(buffer); /* * Don't add too many attributes. */ if (envlen == (MAX_ENVP - 1)) break; } envp[envlen] = NULL; } if (exec_wait) { pid = rad_fork(); /* remember PID */ } else { pid = fork(); /* don't wait */ } if (pid == 0) { int devnull; /* * Child process. * * We try to be fail-safe here. So if ANYTHING * goes wrong, we exit with status 1. */ /* * Open STDIN to /dev/null */ devnull = open("/dev/null", O_RDWR); if (devnull < 0) { RDEBUG("Failed opening /dev/null: %s\n", strerror(errno)); /* * Where the status code is interpreted as a module rcode * one is subtracted from it, to allow 0 to equal success * * 2 is RLM_MODULE_FAIL + 1 */ exit(2); } /* * Only massage the pipe handles if the parent * has created them. */ if (exec_wait) { if (input_fd) { close(to_child[1]); dup2(to_child[0], STDIN_FILENO); } else { dup2(devnull, STDIN_FILENO); } if (output_fd) { close(from_child[0]); dup2(from_child[1], STDOUT_FILENO); } else { dup2(devnull, STDOUT_FILENO); } } else { /* no pipe, STDOUT should be /dev/null */ dup2(devnull, STDIN_FILENO); dup2(devnull, STDOUT_FILENO); } /* * If we're not debugging, then we can't do * anything with the error messages, so we throw * them away. * * If we are debugging, then we want the error * messages to go to the STDERR of the server. */ if (debug_flag == 0) { dup2(devnull, STDERR_FILENO); } close(devnull); /* * The server may have MANY FD's open. We don't * want to leave dangling FD's for the child process * to play funky games with, so we close them. */ closefrom(3); /* * I swear the signature for execve is wrong and should take 'char const * const argv[]'. */ execve(argv[0], argv, envp); printf("Failed to execute \"%s\": %s", argv[0], strerror(errno)); /* fork output will be captured */ /* * Where the status code is interpreted as a module rcode * one is subtracted from it, to allow 0 to equal success * * 2 is RLM_MODULE_FAIL + 1 */ exit(2); } /* * Free child environment variables */ for (i = 0; envp[i] != NULL; i++) { free(envp[i]); } /* * Parent process. */ if (pid < 0) { RDEBUG("Couldn't fork %s: %s", argv[0], strerror(errno)); if (exec_wait) { /* safe because these either need closing or are == -1 */ close(to_child[0]); close(to_child[1]); close(from_child[0]); close(from_child[0]); } return -1; } /* * We're not waiting, exit, and ignore any child's status. */ if (exec_wait) { /* * Close the ends of the pipe(s) the child is using * return the ends of the pipe(s) our caller wants * */ if (input_fd) { *input_fd = to_child[1]; close(to_child[0]); } if (output_fd) { *output_fd = from_child[0]; close(from_child[1]); } } return pid; #else if (exec_wait) { RDEBUG("Wait is not supported"); return -1; } { /* * The _spawn and _exec families of functions are * found in Windows compiler libraries for * portability from UNIX. There is a variety of * functions, including the ability to pass * either a list or array of parameters, to * search in the PATH or otherwise, and whether * or not to pass an environment (a set of * environment variables). Using _spawn, you can * also specify whether you want the new process * to close your program (_P_OVERLAY), to wait * until the new process is finished (_P_WAIT) or * for the two to run concurrently (_P_NOWAIT). * _spawn and _exec are useful for instances in * which you have simple requirements for running * the program, don't want the overhead of the * Windows header file, or are interested * primarily in portability. */ /* * FIXME: check return code... what is it? */ _spawnve(_P_NOWAIT, argv[0], argv, envp); } return 0; #endif }
/** Start a process * * @param cmd Command to execute. This is parsed into argv[] parts, then each individual argv * part is xlat'ed. * @param request Current reuqest * @param exec_wait set to true to read from or write to child. * @param[in,out] input_fd pointer to int, receives the stdin file descriptor. Set to NULL * and the child will have /dev/null on stdin. * @param[in,out] output_fd pinter to int, receives the stdout file descriptor. Set to NULL * and child will have /dev/null on stdout. * @param input_pairs list of value pairs - these will be put into the environment variables * of the child. * @param shell_escape values before passing them as arguments. * @return * - PID of the child process. * - -1 on failure. */ pid_t radius_start_program(char const *cmd, REQUEST *request, bool exec_wait, int *input_fd, int *output_fd, VALUE_PAIR *input_pairs, bool shell_escape) { #ifndef __MINGW32__ char *p; VALUE_PAIR *vp; int n; int to_child[2] = {-1, -1}; int from_child[2] = {-1, -1}; pid_t pid; #endif int argc; int i; char const **argv_p; char *argv[MAX_ARGV], **argv_start = argv; char argv_buf[4096]; #define MAX_ENVP 1024 char *envp[MAX_ENVP]; size_t envlen = 0; TALLOC_CTX *input_ctx = NULL; /* * Stupid array decomposition... * * If we do memcpy(&argv_p, &argv, sizeof(argv_p)) src ends up being a char ** * pointing to the value of the first element. */ memcpy(&argv_p, &argv_start, sizeof(argv_p)); argc = rad_expand_xlat(request, cmd, MAX_ARGV, argv_p, true, sizeof(argv_buf), argv_buf); if (argc <= 0) { ERROR("Invalid command '%s'", cmd); return -1; } if (DEBUG_ENABLED3) { for (i = 0; i < argc; i++) DEBUG3("arg[%d] %s", i, argv[i]); } #ifndef __MINGW32__ /* * Open a pipe for child/parent communication, if necessary. */ if (exec_wait) { if (input_fd) { if (pipe(to_child) != 0) { ERROR("Couldn't open pipe to child: %s", fr_syserror(errno)); return -1; } } if (output_fd) { if (pipe(from_child) != 0) { ERROR("Couldn't open pipe from child: %s", fr_syserror(errno)); /* safe because these either need closing or are == -1 */ close(to_child[0]); close(to_child[1]); return -1; } } } envp[0] = NULL; if (input_pairs) { vp_cursor_t cursor; char buffer[1024]; input_ctx = talloc_new(request); /* * Set up the environment variables in the * parent, so we don't call libc functions that * hold mutexes. They might be locked when we fork, * and will remain locked in the child. */ for (vp = fr_cursor_init(&cursor, &input_pairs); vp && (envlen < ((sizeof(envp) / sizeof(*envp)) - 1)); vp = fr_cursor_next(&cursor)) { /* * Hmm... maybe we shouldn't pass the * user's password in an environment * variable... */ snprintf(buffer, sizeof(buffer), "%s=", vp->da->name); if (shell_escape) { for (p = buffer; *p != '='; p++) { if (*p == '-') { *p = '_'; } else if (isalpha((int) *p)) { *p = toupper(*p); } } } n = strlen(buffer); fr_pair_value_snprint(buffer + n, sizeof(buffer) - n, vp, shell_escape ? '"' : 0); DEBUG3("export %s", buffer); envp[envlen++] = talloc_strdup(input_ctx, buffer); } fr_cursor_init(&cursor, radius_list(request, PAIR_LIST_CONTROL)); while ((envlen < ((sizeof(envp) / sizeof(*envp)) - 1)) && (vp = fr_cursor_next_by_num(&cursor, 0, PW_EXEC_EXPORT, TAG_ANY))) { DEBUG3("export %s", vp->vp_strvalue); memcpy(&envp[envlen++], &vp->vp_strvalue, sizeof(*envp)); } /* * NULL terminate for execve */ envp[envlen] = NULL; } if (exec_wait) { pid = rad_fork(); /* remember PID */ } else { pid = fork(); /* don't wait */ } if (pid == 0) { int devnull; /* * Child process. * * We try to be fail-safe here. So if ANYTHING * goes wrong, we exit with status 1. */ /* * Open STDIN to /dev/null */ devnull = open("/dev/null", O_RDWR); if (devnull < 0) { ERROR("Failed opening /dev/null: %s\n", fr_syserror(errno)); /* * Where the status code is interpreted as a module rcode * one is subtracted from it, to allow 0 to equal success * * 2 is RLM_MODULE_FAIL + 1 */ exit(2); } /* * Only massage the pipe handles if the parent * has created them. */ if (exec_wait) { if (input_fd) { close(to_child[1]); dup2(to_child[0], STDIN_FILENO); } else { dup2(devnull, STDIN_FILENO); } if (output_fd) { close(from_child[0]); dup2(from_child[1], STDOUT_FILENO); } else { dup2(devnull, STDOUT_FILENO); } } else { /* no pipe, STDOUT should be /dev/null */ dup2(devnull, STDIN_FILENO); dup2(devnull, STDOUT_FILENO); } /* * If we're not debugging, then we can't do * anything with the error messages, so we throw * them away. * * If we are debugging, then we want the error * messages to go to the STDERR of the server. */ if (rad_debug_lvl == 0) { dup2(devnull, STDERR_FILENO); } close(devnull); /* * The server may have MANY FD's open. We don't * want to leave dangling FD's for the child process * to play funky games with, so we close them. */ closefrom(3); /* * I swear the signature for execve is wrong and should * take 'char const * const argv[]'. * * Note: execve(), unlike system(), treats all the space * delimited arguments as literals, so there's no need * to perform additional escaping. */ execve(argv[0], argv, envp); printf("Failed to execute \"%s\": %s", argv[0], fr_syserror(errno)); /* fork output will be captured */ /* * Where the status code is interpreted as a module rcode * one is subtracted from it, to allow 0 to equal success * * 2 is RLM_MODULE_FAIL + 1 */ exit(2); } /* * Free child environment variables */ talloc_free(input_ctx); /* * Parent process. */ if (pid < 0) { ERROR("Couldn't fork %s: %s", argv[0], fr_syserror(errno)); if (exec_wait) { /* safe because these either need closing or are == -1 */ close(to_child[0]); close(to_child[1]); close(from_child[0]); close(from_child[1]); } return -1; } /* * We're not waiting, exit, and ignore any child's status. */ if (exec_wait) { /* * Close the ends of the pipe(s) the child is using * return the ends of the pipe(s) our caller wants * */ if (input_fd) { *input_fd = to_child[1]; close(to_child[0]); } if (output_fd) { *output_fd = from_child[0]; close(from_child[1]); } } return pid; #else if (exec_wait) { ERROR("Wait is not supported"); return -1; } { /* * The _spawn and _exec families of functions are * found in Windows compiler libraries for * portability from UNIX. There is a variety of * functions, including the ability to pass * either a list or array of parameters, to * search in the PATH or otherwise, and whether * or not to pass an environment (a set of * environment variables). Using _spawn, you can * also specify whether you want the new process * to close your program (_P_OVERLAY), to wait * until the new process is finished (_P_WAIT) or * for the two to run concurrently (_P_NOWAIT). * _spawn and _exec are useful for instances in * which you have simple requirements for running * the program, don't want the overhead of the * Windows header file, or are interested * primarily in portability. */ /* * FIXME: check return code... what is it? */ _spawnve(_P_NOWAIT, argv[0], argv, envp); } return 0; #endif }
intptr_t __cdecl spawnve(int mode,const char *_Filename,char *const _ArgList[],char *const _Env[]) { return _spawnve(mode, _Filename,(const char *const *)_ArgList,(const char *const *)_Env); }
/** Start a process * * @param cmd Command to execute. This is parsed into argv[] parts, * then each individual argv part is xlat'ed. * @param request Current reuqest * @param exec_wait set to 1 if you want to read from or write to child * @param[in,out] input_fd pointer to int, receives the stdin file. * descriptor. Set to NULL and the child will have /dev/null on stdin * @param[in,out] output_fd pinter to int, receives the stdout file * descriptor. Set to NULL and child will have /dev/null on stdout. * @param input_pairs list of value pairs - these will be put into * the environment variables of the child. * @param shell_escape * @return PID of the child process, -1 on error. */ pid_t radius_start_program(const char *cmd, REQUEST *request, int exec_wait, int *input_fd, int *output_fd, VALUE_PAIR *input_pairs, int shell_escape) { const char *from; char *to; #ifndef __MINGW32__ char *p; VALUE_PAIR *vp; int n; int to_child[2] = {-1, -1}; int from_child[2] = {-1, -1}; pid_t pid; #endif int argc = -1; int i; int left; char *argv[MAX_ARGV]; char argv_buf[4096]; #define MAX_ENVP 1024 char *envp[MAX_ENVP]; char mycmd[1024]; if (strlen(cmd) > (sizeof(mycmd) - 1)) { radlog(L_ERR|L_CONS, "Command line is too long"); return -1; } /* * Check for bad escapes. */ if (cmd[strlen(cmd) - 1] == '\\') { radlog(L_ERR|L_CONS, "Command line has final backslash, without a following character"); return -1; } strlcpy(mycmd, cmd, sizeof(mycmd)); /* * Split the string into argv's BEFORE doing radius_xlat... */ from = cmd; to = mycmd; argc = 0; while (*from) { int length; /* * Skip spaces. */ if ((*from == ' ') || (*from == '\t')) { from++; continue; } argv[argc] = to; argc++; if (argc >= (MAX_ARGV - 1)) break; /* * Copy the argv over to our buffer. */ while (*from && (*from != ' ') && (*from != '\t')) { if (to >= mycmd + sizeof(mycmd) - 1) { return -1; /* ran out of space */ } switch (*from) { case '"': case '\'': length = rad_copy_string(to, from); if (length < 0) { radlog(L_ERR|L_CONS, "Invalid string passed as argument for external program"); return -1; } from += length; to += length; break; case '%': if (from[1] == '{') { *(to++) = *(from++); length = rad_copy_variable(to, from); if (length < 0) { radlog(L_ERR|L_CONS, "Invalid variable expansion passed as argument for external program"); return -1; } from += length; to += length; } else { /* FIXME: catch %%{ ? */ *(to++) = *(from++); } break; case '\\': if (from[1] == ' ') from++; /* FALL-THROUGH */ default: *(to++) = *(from++); } } /* end of string, or found a space */ *(to++) = '\0'; /* terminate the string */ } /* * We have to have SOMETHING, at least. */ if (argc <= 0) { radlog(L_ERR, "Exec-Program: empty command line."); return -1; } /* * Expand each string, as appropriate. */ to = argv_buf; left = sizeof(argv_buf); for (i = 0; i < argc; i++) { int sublen; /* * Don't touch argv's which won't be translated. */ if (strchr(argv[i], '%') == NULL) continue; if (!request) continue; sublen = radius_xlat(to, left - 1, argv[i], request, NULL); if (sublen <= 0) { /* * Fail to be backwards compatible. * * It's yucky, but it won't break anything, * and it won't cause security problems. */ sublen = 0; } argv[i] = to; to += sublen; *(to++) = '\0'; left -= sublen; left--; if (left <= 0) { radlog(L_ERR, "Exec-Program: Ran out of space while expanding arguments."); return -1; } } argv[argc] = NULL; #ifndef __MINGW32__ /* * Open a pipe for child/parent communication, if necessary. */ if (exec_wait) { if (input_fd) { if (pipe(to_child) != 0) { radlog(L_ERR|L_CONS, "Couldn't open pipe to child: %s", strerror(errno)); return -1; } } if (output_fd) { if (pipe(from_child) != 0) { radlog(L_ERR|L_CONS, "Couldn't open pipe from child: %s", strerror(errno)); /* safe because these either need closing or are == -1 */ close(to_child[0]); close(to_child[1]); return -1; } } } envp[0] = NULL; if (input_pairs) { int envlen; char buffer[1024]; /* * Set up the environment variables in the * parent, so we don't call libc functions that * hold mutexes. They might be locked when we fork, * and will remain locked in the child. */ envlen = 0; for (vp = input_pairs; vp != NULL; vp = vp->next) { /* * Hmm... maybe we shouldn't pass the * user's password in an environment * variable... */ snprintf(buffer, sizeof(buffer), "%s=", vp->name); if (shell_escape) { for (p = buffer; *p != '='; p++) { if (*p == '-') { *p = '_'; } else if (isalpha((int) *p)) { *p = toupper(*p); } } } n = strlen(buffer); vp_prints_value(buffer+n, sizeof(buffer) - n, vp, shell_escape); envp[envlen++] = strdup(buffer); /* * Don't add too many attributes. */ if (envlen == (MAX_ENVP - 1)) break; } envp[envlen] = NULL; } if (exec_wait) { pid = rad_fork(); /* remember PID */ } else { pid = fork(); /* don't wait */ } if (pid == 0) { int devnull; /* * Child process. * * We try to be fail-safe here. So if ANYTHING * goes wrong, we exit with status 1. */ /* * Open STDIN to /dev/null */ devnull = open("/dev/null", O_RDWR); if (devnull < 0) { radlog(L_ERR|L_CONS, "Failed opening /dev/null: %s\n", strerror(errno)); exit(1); } /* * Only massage the pipe handles if the parent * has created them. */ if (exec_wait) { if (input_fd) { close(to_child[1]); dup2(to_child[0], STDIN_FILENO); } else { dup2(devnull, STDIN_FILENO); } if (output_fd) { close(from_child[0]); dup2(from_child[1], STDOUT_FILENO); } else { dup2(devnull, STDOUT_FILENO); } } else { /* no pipe, STDOUT should be /dev/null */ dup2(devnull, STDIN_FILENO); dup2(devnull, STDOUT_FILENO); } /* * If we're not debugging, then we can't do * anything with the error messages, so we throw * them away. * * If we are debugging, then we want the error * messages to go to the STDERR of the server. */ if (debug_flag == 0) { dup2(devnull, STDERR_FILENO); } close(devnull); /* * The server may have MANY FD's open. We don't * want to leave dangling FD's for the child process * to play funky games with, so we close them. */ closefrom(3); execve(argv[0], argv, envp); radlog(L_ERR, "Exec-Program: FAILED to execute %s: %s", argv[0], strerror(errno)); exit(1); } /* * Free child environment variables */ for (i = 0; envp[i] != NULL; i++) { free(envp[i]); } /* * Parent process. */ if (pid < 0) { radlog(L_ERR|L_CONS, "Couldn't fork %s: %s", argv[0], strerror(errno)); if (exec_wait) { /* safe because these either need closing or are == -1 */ close(to_child[0]); close(to_child[1]); close(from_child[0]); close(from_child[0]); } return -1; } /* * We're not waiting, exit, and ignore any child's status. */ if (exec_wait) { /* * Close the ends of the pipe(s) the child is using * return the ends of the pipe(s) our caller wants * */ if (input_fd) { *input_fd = to_child[1]; close(to_child[0]); } if (output_fd) { *output_fd = from_child[0]; close(from_child[1]); } } return pid; #else if (exec_wait) { radlog(L_ERR, "Exec-Program-Wait is not supported"); return -1; } { /* * The _spawn and _exec families of functions are * found in Windows compiler libraries for * portability from UNIX. There is a variety of * functions, including the ability to pass * either a list or array of parameters, to * search in the PATH or otherwise, and whether * or not to pass an environment (a set of * environment variables). Using _spawn, you can * also specify whether you want the new process * to close your program (_P_OVERLAY), to wait * until the new process is finished (_P_WAIT) or * for the two to run concurrently (_P_NOWAIT). * _spawn and _exec are useful for instances in * which you have simple requirements for running * the program, don't want the overhead of the * Windows header file, or are interested * primarily in portability. */ /* * FIXME: check return code... what is it? */ _spawnve(_P_NOWAIT, argv[0], argv, envp); } return 0; #endif }
int main( int argc, char *argv[], char** envp ) { int status; int i; char host_os_arg[] = "HOST_OS=Win32"; char make_base_path[] = ".\\tools\\common\\Win32\\make.exe"; /************************************************************/ /* Construct a full, safe path to the real make executable */ char drive[_MAX_DRIVE]; char dir[_MAX_DIR]; char fname[_MAX_FNAME]; char ext[_MAX_EXT]; _splitpath( argv[0], drive, dir, fname, ext ); int make_path_len = sizeof(make_base_path) + strlen(drive) + strlen(dir); char * make_path = (char*) malloc( make_path_len ); if ( make_path == NULL ) { printf( "out of memory for make path - requested %d bytes\n", make_path_len); return -1; } /* Start with empty string */ strcpy( make_path, "" ); /* Add a drive spec if present */ if ( strlen(drive) != 0 ) { strcat( make_path, drive ); } /* Add a path directory if present */ if ( strlen(dir) != 0 ) { strcat( make_path, dir ); } /* Add a path directory if present */ strcat( make_path, make_base_path ); /*****************************************************************************************************/ /* Construct a full, command line including the real make executable path and the HOST_OS parameter */ /* Determine the total lenght of the command line string */ int command_length = make_path_len + strlen(host_os_arg) + 3; // +3 for quotes around Make path and space before host_os_arg for( i = 1; i < argc; i++ ) { command_length += strlen( argv[i] ) + 3; // +3 for two quotes and a space between args } /* Allocate a buffer for the command string */ char * command = (char*) malloc( command_length ); if ( command == NULL ) { printf( "out of memory for command - requested %d bytes\n", command_length); return -1; } /* Allocate a buffer for the argument pointer list */ char** arglist = (char**) malloc( (argc + 2) * sizeof(char*) ); if ( arglist == NULL ) { printf( "out of memory for arglist - requested %d bytes\n", (argc+2) * sizeof(char*) ); return -1; } char** curr_arglist_pos = arglist; /* Add path of real make executable */ /* surrounded by double quotes - to allow directories containing spaces to function */ strcpy( command, "\"" ); strcat( command, make_path ); strcat( command, "\"" ); *curr_arglist_pos = command; /* Add to argument pointer list */ curr_arglist_pos++; command += strlen(command) + 1; /* Add the HOST_OS argument */ strcpy( command, host_os_arg ); *curr_arglist_pos = command; /* Add to argument pointer list */ curr_arglist_pos++; command += strlen(command) + 1; /* Add the other arguments - surrounded by double quotes to enable arguments with spaces to work */ for( i = 1; i < argc; i++ ) { strcpy( command, "\"" ); strcat( command, argv[i] ); strcat( command, "\"" ); *curr_arglist_pos = command; /* Add to argument pointer list */ curr_arglist_pos++; command += strlen(command) + 1; } *curr_arglist_pos = NULL; /* Process the environment to get an environment list with most * variables wiped out but some saved. */ const char* prefixes[] = { "", SAVED_PREFIX, SAVED_PREFIX, (char *)0 }; const char* env_vars[] = { "ComSpec", "HOME", "PATH", (char *)0 }; char** env = get_saved_env_vars( env_vars, prefixes ); /* Finally Spawn the real Make executable and wait for it to finish */ return _spawnve ( _P_WAIT, make_path, (const char * const*)arglist, (const char * const*) env); }
static pid_t runve(int mode, const char* path, char* const* argv, char* const* envv) { register char* s; register char** p; register char** v; void* m1; void* m2; pid_t pid; int oerrno; int ux; int n; #if defined(_P_DETACH) && defined(_P_NOWAIT) int pgrp; #endif #if CONVERT char* d; char* t; int m; #endif struct stat st; char buf[PATH_MAX]; char tmp[PATH_MAX]; #if DEBUG static int trace; #endif #if defined(_P_DETACH) && defined(_P_NOWAIT) if (mode == _P_DETACH) { /* * 2004-02-29 cygwin _P_DETACH is useless: * spawn*() returns 0 instead of the spawned pid * spawned { pgid sid } are the same as the parent */ mode = _P_NOWAIT; pgrp = 1; } else pgrp = 0; #endif if (!envv) envv = (char* const*)environ; m1 = m2 = 0; oerrno = errno; #if DEBUG if (!trace) trace = (s = getenv("_AST_exec_trace")) ? *s : 'n'; #endif if (execrate(path, buf, sizeof(buf), 0)) { if (!_stat(buf, &st)) path = (const char*)buf; else errno = oerrno; } if (path != (const char*)buf && _stat(path, &st)) return -1; if (!S_ISREG(st.st_mode) || !(st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))) { errno = EACCES; return -1; } if (magic(path, &ux)) { #if _CYGWIN_fork_works errno = ENOEXEC; return -1; #else ux = 1; p = (char**)argv; while (*p++); if (!(v = (char**)malloc((p - (char**)argv + 2) * sizeof(char*)))) { errno = EAGAIN; return -1; } m1 = v; p = v; *p++ = (char*)path; *p++ = (char*)path; path = (const char*)pathshell(); if (*argv) argv++; while (*p++ = (char*)*argv++); argv = (char* const*)v; #endif } /* * the win32 dll search order is * (1) the directory of path * (2) . * (3) /c/(WINNT|WINDOWS)/system32 /c/(WINNT|WINDOWS) * (4) the directories on $PATH * there are no cygwin dlls in (3), so if (1) and (2) fail * to produce the required dlls its up to (4) * * the standard allows PATH to be anything once the path * to an executable is determined; this code ensures that PATH * contains /bin so that at least the cygwin dll, required * by all cygwin executables, will be found */ if (p = (char**)envv) { n = 1; while (s = *p++) if (strneq(s, "PATH=", 5)) { s += 5; do { s = pathcat(tmp, s, ':', NiL, ""); if (streq(tmp, "/usr/bin/") || streq(tmp, "/bin/")) { n = 0; break; } } while (s); if (n) { n = 0; snprintf(tmp, sizeof(tmp), "%s:/bin", *(p - 1)); *(p - 1) = tmp; } break; } if (n) { n = p - (char**)envv + 1; p = (char**)envv; if (v = (char**)malloc(n * sizeof(char*))) { m2 = v; envv = (char* const*)v; *v++ = strcpy(tmp, "PATH=/bin"); while (*v++ = *p++); } } #if CONVERT if (!ux && (d = getenv(convertvars[0]))) for (p = (char**)envv; s = *p; p++) if ((n = convert(d, s)) && (m = cygwin_posix_to_win32_path_list_buf_size(s + n)) > 0) { if (!(t = malloc(n + m + 1))) break; *p = t; memcpy(t, s, n); cygwin_posix_to_win32_path_list(s + n, t + n); } #endif } #if DEBUG if (trace == 'a' || trace == 'e') { sfprintf(sfstderr, "%s %s [", mode == _P_OVERLAY ? "_execve" : "_spawnve", path); for (n = 0; argv[n]; n++) sfprintf(sfstderr, " '%s'", argv[n]); if (trace == 'e') { sfprintf(sfstderr, " ] ["); for (n = 0; envv[n]; n++) sfprintf(sfstderr, " '%s'", envv[n]); } sfprintf(sfstderr, " ]\n"); sfsync(sfstderr); } #endif #if _lib_spawn_mode if (mode != _P_OVERLAY) { pid = _spawnve(mode, path, argv, envv); #if defined(_P_DETACH) && defined(_P_NOWAIT) if (pid > 0 && pgrp) setpgid(pid, 0); #endif } else #endif { #if defined(_P_DETACH) && defined(_P_NOWAIT) if (pgrp) setpgid(0, 0); #endif pid = _execve(path, argv, envv); } if (m1) free(m1); if (m2) free(m2); return pid; }
/********************************************************************* * _spawnv (MSVCRT.@) * * Like on Windows, this function does not handle arguments with spaces * or double-quotes. */ MSVCRT_intptr_t CDECL _spawnv(int flags, const char* name, const char* const* argv) { return _spawnve(flags, name, argv, NULL); }
/********************************************************************* * _execve (MSVCRT.@) * * Like on Windows, this function does not handle arguments with spaces * or double-quotes. */ MSVCRT_intptr_t CDECL MSVCRT__execve(const char* name, char* const* argv, const char* const* envv) { return _spawnve(MSVCRT__P_OVERLAY, name, (const char* const*) argv, envv); }
/********************************************************************* * _execv (MSVCRT.@) * * Like on Windows, this function does not handle arguments with spaces * or double-quotes. */ MSVCRT_intptr_t CDECL _execv(const char* name, char* const* argv) { return _spawnve(MSVCRT__P_OVERLAY, name, (const char* const*) argv, NULL); }
/* * Execute a program on successful authentication. * Return 0 if exec_wait == 0. * Return the exit code of the called program if exec_wait != 0. * Return -1 on fork/other errors in the parent process. */ int radius_exec_program(const char *cmd, REQUEST *request, int exec_wait, char *user_msg, int msg_len, VALUE_PAIR *input_pairs, VALUE_PAIR **output_pairs, int shell_escape) { VALUE_PAIR *vp; char mycmd[1024]; const char *from; char *p, *to; int pd[2]; pid_t pid, child_pid; int argc = -1; int comma = 0; int status; int i; int n, left, done; char *argv[MAX_ARGV]; char answer[4096]; char argv_buf[4096]; #define MAX_ENVP 1024 char *envp[MAX_ENVP]; struct timeval start; #ifdef O_NONBLOCK int nonblock = TRUE; #endif if (user_msg) *user_msg = '\0'; if (output_pairs) *output_pairs = NULL; if (strlen(cmd) > (sizeof(mycmd) - 1)) { radlog(L_ERR|L_CONS, "Command line is too long"); return -1; } /* * Check for bad escapes. */ if (cmd[strlen(cmd) - 1] == '\\') { radlog(L_ERR|L_CONS, "Command line has final backslash, without a following character"); return -1; } strlcpy(mycmd, cmd, sizeof(mycmd)); /* * Split the string into argv's BEFORE doing radius_xlat... */ from = cmd; to = mycmd; argc = 0; while (*from) { int length; /* * Skip spaces. */ if ((*from == ' ') || (*from == '\t')) { from++; continue; } argv[argc] = to; argc++; if (argc >= (MAX_ARGV - 1)) break; /* * Copy the argv over to our buffer. */ while (*from && (*from != ' ') && (*from != '\t')) { if (to >= mycmd + sizeof(mycmd) - 1) { return -1; /* ran out of space */ } switch (*from) { case '"': case '\'': length = rad_copy_string(to, from); if (length < 0) { radlog(L_ERR|L_CONS, "Invalid string passed as argument for external program"); return -1; } from += length; to += length; break; case '%': if (from[1] == '{') { *(to++) = *(from++); length = rad_copy_variable(to, from); if (length < 0) { radlog(L_ERR|L_CONS, "Invalid variable expansion passed as argument for external program"); return -1; } from += length; to += length; } else { /* FIXME: catch %%{ ? */ *(to++) = *(from++); } break; case '\\': if (from[1] == ' ') from++; /* FALL-THROUGH */ default: *(to++) = *(from++); } } /* end of string, or found a space */ *(to++) = '\0'; /* terminate the string */ } /* * We have to have SOMETHING, at least. */ if (argc <= 0) { radlog(L_ERR, "Exec-Program: empty command line."); return -1; } /* * Expand each string, as appropriate. */ to = argv_buf; left = sizeof(argv_buf); for (i = 0; i < argc; i++) { int sublen; /* * Don't touch argv's which won't be translated. */ if (strchr(argv[i], '%') == NULL) continue; if (!request) continue; sublen = radius_xlat(to, left - 1, argv[i], request, NULL); if (sublen <= 0) { /* * Fail to be backwards compatible. * * It's yucky, but it won't break anything, * and it won't cause security problems. */ sublen = 0; } argv[i] = to; to += sublen; *(to++) = '\0'; left -= sublen; left--; if (left <= 0) { radlog(L_ERR, "Exec-Program: Ran out of space while expanding arguments."); return -1; } } argv[argc] = NULL; #ifndef __MINGW32__ /* * Open a pipe for child/parent communication, if necessary. */ if (exec_wait) { if (pipe(pd) != 0) { radlog(L_ERR|L_CONS, "Couldn't open pipe: %s", strerror(errno)); return -1; } } else { /* * We're not waiting, so we don't look for a * message, or VP's. */ user_msg = NULL; output_pairs = NULL; } envp[0] = NULL; if (input_pairs) { int envlen; char buffer[1024]; /* * Set up the environment variables in the * parent, so we don't call libc functions that * hold mutexes. They might be locked when we fork, * and will remain locked in the child. */ envlen = 0; for (vp = input_pairs; vp != NULL; vp = vp->next) { /* * Hmm... maybe we shouldn't pass the * user's password in an environment * variable... */ snprintf(buffer, sizeof(buffer), "%s=", vp->name); if (shell_escape) { for (p = buffer; *p != '='; p++) { if (*p == '-') { *p = '_'; } else if (isalpha((int) *p)) { *p = toupper(*p); } } } n = strlen(buffer); vp_prints_value(buffer+n, sizeof(buffer) - n, vp, shell_escape); envp[envlen++] = strdup(buffer); /* * Don't add too many attributes. */ if (envlen == (MAX_ENVP - 1)) break; } envp[envlen] = NULL; } if (exec_wait) { pid = rad_fork(); /* remember PID */ } else { pid = fork(); /* don't wait */ } if (pid == 0) { int devnull; /* * Child process. * * We try to be fail-safe here. So if ANYTHING * goes wrong, we exit with status 1. */ /* * Open STDIN to /dev/null */ devnull = open("/dev/null", O_RDWR); if (devnull < 0) { radlog(L_ERR|L_CONS, "Failed opening /dev/null: %s\n", strerror(errno)); exit(1); } dup2(devnull, STDIN_FILENO); /* * Only massage the pipe handles if the parent * has created them. */ if (exec_wait) { /* * pd[0] is the FD the child will read from, * which we don't want. */ if (close(pd[0]) != 0) { radlog(L_ERR|L_CONS, "Can't close pipe: %s", strerror(errno)); exit(1); } /* * pd[1] is the FD that the child will write to, * so we make it STDOUT. */ if (dup2(pd[1], STDOUT_FILENO) != 1) { radlog(L_ERR|L_CONS, "Can't dup stdout: %s", strerror(errno)); exit(1); } } else { /* no pipe, STDOUT should be /dev/null */ dup2(devnull, STDOUT_FILENO); } /* * If we're not debugging, then we can't do * anything with the error messages, so we throw * them away. * * If we are debugging, then we want the error * messages to go to the STDERR of the server. */ if (debug_flag == 0) { dup2(devnull, STDERR_FILENO); } close(devnull); /* * The server may have MANY FD's open. We don't * want to leave dangling FD's for the child process * to play funky games with, so we close them. */ closefrom(3); execve(argv[0], argv, envp); radlog(L_ERR, "Exec-Program: FAILED to execute %s: %s", argv[0], strerror(errno)); exit(1); } /* * Free child environment variables */ for (i = 0; envp[i] != NULL; i++) { free(envp[i]); } /* * Parent process. */ if (pid < 0) { radlog(L_ERR|L_CONS, "Couldn't fork %s: %s", argv[0], strerror(errno)); if (exec_wait) { close(pd[0]); close(pd[1]); } return -1; } /* * We're not waiting, exit, and ignore any child's status. */ if (!exec_wait) { return 0; } /* * Close the FD to which the child writes it's data. * * If we can't close it, then we close pd[0], and return an * error. */ if (close(pd[1]) != 0) { radlog(L_ERR|L_CONS, "Can't close pipe: %s", strerror(errno)); close(pd[0]); return -1; } #ifdef O_NONBLOCK /* * Try to set it non-blocking. */ do { int flags; if ((flags = fcntl(pd[0], F_GETFL, NULL)) < 0) { nonblock = FALSE; break; } flags |= O_NONBLOCK; if( fcntl(pd[0], F_SETFL, flags) < 0) { nonblock = FALSE; break; } } while (0); #endif /* * Read from the pipe until we doesn't get any more or * until the message is full. */ done = 0; left = sizeof(answer) - 1; gettimeofday(&start, NULL); while (1) { int rcode; fd_set fds; struct timeval when, elapsed, wake; FD_ZERO(&fds); FD_SET(pd[0], &fds); gettimeofday(&when, NULL); tv_sub(&when, &start, &elapsed); if (elapsed.tv_sec >= 10) goto too_long; when.tv_sec = 10; when.tv_usec = 0; tv_sub(&when, &elapsed, &wake); rcode = select(pd[0] + 1, &fds, NULL, NULL, &wake); if (rcode == 0) { too_long: radlog(L_ERR, "Child PID %u (%s) is taking too much time: forcing failure and killing child.", pid, argv[0]); kill(pid, SIGTERM); close(pd[0]); /* should give SIGPIPE to child, too */ /* * Clean up the child entry. */ rad_waitpid(pid, &status); return 1; } if (rcode < 0) { if (errno == EINTR) continue; break; } #ifdef O_NONBLOCK /* * Read as many bytes as possible. The kernel * will return the number of bytes available. */ if (nonblock) { status = read(pd[0], answer + done, left); } else #endif /* * There's at least 1 byte ready: read it. */ status = read(pd[0], answer + done, 1); /* * Nothing more to read: stop. */ if (status == 0) { break; } /* * Error: See if we have to continue. */ if (status < 0) { /* * We were interrupted: continue reading. */ if (errno == EINTR) { continue; } /* * There was another error. Most likely * The child process has finished, and * exited. */ break; } done += status; left -= status; if (left <= 0) break; } answer[done] = 0; /* * Make sure that the writer can't block while writing to * a pipe that no one is reading from anymore. */ close(pd[0]); DEBUG2("Exec-Program output: %s", answer); /* * Parse the output, if any. */ if (done) { n = T_OP_INVALID; if (output_pairs) { /* * For backwards compatibility, first check * for plain text (user_msg). */ vp = NULL; n = userparse(answer, &vp); if (vp) { pairfree(&vp); } } if (n == T_OP_INVALID) { DEBUG("Exec-Program-Wait: plaintext: %s", answer); if (user_msg) { strlcpy(user_msg, answer, msg_len); } } else { /* * HACK: Replace '\n' with ',' so that * userparse() can parse the buffer in * one go (the proper way would be to * fix userparse(), but oh well). */ for (p = answer; *p; p++) { if (*p == '\n') { *p = comma ? ' ' : ','; p++; comma = 0; } if (*p == ',') comma++; } /* * Replace any trailing comma by a NUL. */ if (answer[strlen(answer) - 1] == ',') { answer[strlen(answer) - 1] = '\0'; } radlog(L_DBG,"Exec-Program-Wait: value-pairs: %s", answer); if (userparse(answer, &vp) == T_OP_INVALID) { radlog(L_ERR, "Exec-Program-Wait: %s: unparsable reply", cmd); } else { /* * Tell the caller about the value * pairs. */ *output_pairs = vp; } } /* else the answer was a set of VP's, not a text message */ } /* else we didn't read anything from the child */ /* * Call rad_waitpid (should map to waitpid on non-threaded * or single-server systems). */ child_pid = rad_waitpid(pid, &status); if (child_pid == 0) { radlog(L_DBG, "Exec-Program: Timeout waiting for child"); return 2; } if (child_pid == pid) { if (WIFEXITED(status)) { status = WEXITSTATUS(status); radlog(L_DBG, "Exec-Program: returned: %d", status); return status; } } radlog(L_ERR|L_CONS, "Exec-Program: Abnormal child exit: %s", strerror(errno)); return 1; #else msg_len = msg_len; /* -Wunused */ if (exec_wait) { radlog(L_ERR, "Exec-Program-Wait is not supported"); return -1; } /* * We're not waiting, so we don't look for a * message, or VP's. */ user_msg = NULL; output_pairs = NULL; { /* * The _spawn and _exec families of functions are * found in Windows compiler libraries for * portability from UNIX. There is a variety of * functions, including the ability to pass * either a list or array of parameters, to * search in the PATH or otherwise, and whether * or not to pass an environment (a set of * environment variables). Using _spawn, you can * also specify whether you want the new process * to close your program (_P_OVERLAY), to wait * until the new process is finished (_P_WAIT) or * for the two to run concurrently (_P_NOWAIT). * _spawn and _exec are useful for instances in * which you have simple requirements for running * the program, don't want the overhead of the * Windows header file, or are interested * primarily in portability. */ /* * FIXME: check return code... what is it? */ _spawnve(_P_NOWAIT, argv[0], argv, envp); } return 0; #endif }