/** Execute a program. * * @param[in] request Current request. * @param[in] cmd Command to execute. This is parsed into argv[] parts, then each individual argv part * is xlat'ed. * @param[in] exec_wait set to 1 if you want to read from or write to child. * @param[in] shell_escape values before passing them as arguments. * @param[in] user_msg buffer to append plaintext (non valuepair) output. * @param[in] msg_len length of user_msg buffer. * @param[in] input_pairs list of value pairs - these will be available in the environment of the child. * @param[out] output_pairs list of value pairs - child stdout will be parsed and added into this list * of value pairs. * @return 0 if exec_wait==0, exit code if exec_wait!=0, -1 on error. */ int radius_exec_program(REQUEST *request, char const *cmd, bool exec_wait, bool shell_escape, char *user_msg, size_t msg_len, VALUE_PAIR *input_pairs, VALUE_PAIR **output_pairs) { pid_t pid; int from_child; #ifndef __MINGW32__ VALUE_PAIR *vp; char *p; pid_t child_pid; int comma = 0; int status; int n, done; char answer[4096]; #endif RDEBUG2("Executing: \"%s\"", cmd); if (user_msg) *user_msg = '\0'; pid = radius_start_program(cmd, request, exec_wait, NULL, &from_child, input_pairs, shell_escape); if (pid < 0) { return -1; } if (!exec_wait) { return 0; } #ifndef __MINGW32__ done = radius_readfrom_program(request, from_child, pid, 10, answer, sizeof(answer)); if (done < 0) { /* * failure - radius_readfrom_program will * have called close(from_child) for us */ DEBUG("Failed to read from child output"); return -1; } answer[done] = '\0'; /* * Make sure that the writer can't block while writing to * a pipe that no one is reading from anymore. */ close(from_child); /* * 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(request, answer, &vp); if (vp) { pairfree(&vp); } } if (n == T_OP_INVALID) { 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'; } if (userparse(request, answer, &vp) == T_OP_INVALID) { REDEBUG("Unparsable reply from '%s'", cmd); return -1; } 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) { REDEBUG("Timeout waiting for child"); return -2; } if (child_pid == pid) { if (WIFEXITED(status)) { status = WEXITSTATUS(status); RDEBUG("Program returned code (%d): %s", status, answer); return status; } } REDEBUG("Abnormal child exit: %s", strerror(errno)); #endif /* __MINGW32__ */ return -1; }
/** Execute a program. * * @param[in,out] ctx to allocate new VALUE_PAIR (s) in. * @param[out] out buffer to append plaintext (non valuepair) output. * @param[in] outlen length of out buffer. * @param[out] output_pairs list of value pairs - Data on child's stdout will be parsed and * added into this list of value pairs. * @param[in] request Current request (may be NULL). * @param[in] cmd Command to execute. This is parsed into argv[] parts, then each individual argv * part is xlat'ed. * @param[in] input_pairs list of value pairs - these will be available in the environment of the * child. * @param[in] exec_wait set to 1 if you want to read from or write to child. * @param[in] shell_escape values before passing them as arguments. * @param[in] timeout amount of time to wait, in seconds. * @return * - 0 if exec_wait==0. * - exit code if exec_wait!=0. * - -1 on failure. */ int radius_exec_program(TALLOC_CTX *ctx, char *out, size_t outlen, VALUE_PAIR **output_pairs, REQUEST *request, char const *cmd, VALUE_PAIR *input_pairs, bool exec_wait, bool shell_escape, int timeout) { pid_t pid; int from_child; #ifndef __MINGW32__ char *p; pid_t child_pid; int comma = 0; int status, ret = 0; ssize_t len; char answer[4096]; #endif RDEBUG2("Executing: %s:", cmd); if (out) *out = '\0'; pid = radius_start_program(cmd, request, exec_wait, NULL, &from_child, input_pairs, shell_escape); if (pid < 0) { return -1; } if (!exec_wait) { return 0; } #ifndef __MINGW32__ len = radius_readfrom_program(from_child, pid, timeout, answer, sizeof(answer)); if (len < 0) { /* * Failure - radius_readfrom_program will * have called close(from_child) for us */ RERROR("Failed to read from child output"); return -1; } answer[len] = '\0'; /* * Make sure that the writer can't block while writing to * a pipe that no one is reading from anymore. */ close(from_child); if (len == 0) { goto wait; } /* * Parse the output, if any. */ if (output_pairs) { /* * HACK: Replace '\n' with ',' so that * fr_pair_list_afrom_str() can parse the buffer in * one go (the proper way would be to * fix fr_pair_list_afrom_str(), 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[len - 1] == ',') { answer[--len] = '\0'; } if (fr_pair_list_afrom_str(ctx, answer, output_pairs) == T_INVALID) { RERROR("Failed parsing output from: %s: %s", cmd, fr_strerror()); strlcpy(out, answer, len); ret = -1; } /* * We've not been told to extract output pairs, * just copy the programs output to the out * buffer. */ } else if (out) { strlcpy(out, answer, outlen); } /* * Call rad_waitpid (should map to waitpid on non-threaded * or single-server systems). */ wait: child_pid = rad_waitpid(pid, &status); if (child_pid == 0) { RERROR("Timeout waiting for child"); return -2; } if (child_pid == pid) { if (WIFEXITED(status)) { status = WEXITSTATUS(status); if ((status != 0) || (ret < 0)) { RERROR("Program returned code (%d) and output '%s'", status, answer); } else { RDEBUG2("Program returned code (%d) and output '%s'", status, answer); } return ret < 0 ? ret : status; } } RERROR("Abnormal child exit: %s", fr_syserror(errno)); #endif /* __MINGW32__ */ return -1; }