2 * exec.c Execute external programs.
7 static const char rcsid[] = "$Id$";
10 #include "libradius.h"
21 # include <sys/wait.h>
24 # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
27 # define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
33 * Execute a program on successful authentication.
34 * Return 0 if exec_wait == 0.
35 * Return the exit code of the called program if exec_wait != 0.
38 int radius_exec_program(const char *cmd, REQUEST *request,
39 int exec_wait, const char **user_msg)
42 static char message[256];
52 void (*oldsig)(int) = NULL;
55 * (hs) - Open a pipe for child/parent communication.
56 * - Reset the signal handler for SIGCHLD, so
57 * we have a chance to notice the dead child here and
58 * not in some signal handler.
59 * This has to be done for the exec_wait case only, since
60 * if we don't wait we aren't interested in any
65 radlog(L_ERR|L_CONS, "Couldn't open pipe: %m");
68 if ((oldsig = signal(SIGCHLD, SIG_DFL)) == SIG_ERR) {
69 radlog(L_ERR|L_CONS, "Can't reset SIGCHLD: %m");
74 if ((pid = fork()) == 0) {
83 buf = radius_xlat2(answer, sizeof(answer), cmd, request);
86 * XXX FIXME: This is debugging info.
88 radlog(L_INFO, "Exec-Program: %s", buf);
91 * Build vector list and execute.
93 p = strtok(buf, " \t");
96 p = strtok(NULL, " \t");
100 radlog(L_ERR, "Exec-Program: empty command line.");
105 if (close(pd[0]) != 0)
106 radlog(L_ERR|L_CONS, "Can't close pipe: %m");
107 if (dup2(pd[1], 1) != 1)
108 radlog(L_ERR|L_CONS, "Can't dup stdout: %m");
112 * Set up the environment variables.
113 * We're in the child, and it will exit in 4 lines
114 * anyhow, so memory allocation isn't an issue.
118 for (vp = request->packet->vps; vp->next; vp = vp->next) {
120 * Hmm... maybe we shouldn't pass the
121 * user's password in an environment
124 snprintf(buffer, sizeof(buffer), "%s=", vp->name);
125 for (p = buffer; *p != '='; p++) {
128 } else if (isalpha(*p)) {
134 vp_prints_value(buffer+n, sizeof(buffer) - n, vp, 1);
136 envp[envlen++] = strdup(buffer);
144 for(n = 32; n >= 3; n--)
147 execve(argv[0], argv, envp);
149 radlog(L_ERR, "Exec-Program: %s: %m", argv[0]);
157 radlog(L_ERR|L_CONS, "Couldn't fork: %m");
164 * (hs) Do we have a pipe?
165 * --> Close the write side of the pipe
169 if (pd[0] || pd[1]) {
170 if (close(pd[1]) != 0)
171 radlog(L_ERR|L_CONS, "Can't close pipe: %m");
174 * (hs) Read until we doesn't get any more
175 * or until the message is full.
178 left = sizeof(answer) - 1;
179 while ((n = read(pd[0], answer + done, left)) > 0) {
182 if (left <= 0) break;
187 * (hs) Make sure that the writer can't block
188 * while writing in a pipe that isn't read anymore.
194 * Parse the output, if any.
198 * For backwards compatibility, first check
199 * for plain text (user_msg).
202 n = userparse(answer, &vp);
203 if (vp) pairfree(vp);
207 radlog(L_DBG, "Exec-Program-Wait: plaintext: %s", answer);
209 strncpy(message, answer, sizeof(message));
210 message[sizeof(message) - 1] = 0;
215 * HACK: Replace '\n' with ',' so that
216 * userparse() can parse the buffer in
217 * one go (the proper way would be to
218 * fix userparse(), but oh well).
220 for (p = answer; *p; p++) {
222 *p = comma ? ' ' : ',';
226 if (*p == ',') comma++;
230 * Replace any trailing comma by a NUL.
232 if (answer[strlen(answer) - 1] == ',')
233 answer[strlen(answer) - 1] = '\0';
235 radlog(L_DBG,"Exec-Program-Wait: value-pairs: %s", answer);
236 if (userparse(answer, &vp) != 0)
238 "Exec-Program-Wait: %s: unparsable reply", cmd);
240 pairmove(&request->reply->vps, &vp);
246 while(waitpid(pid, &status, 0) != pid)
250 * (hs) Now we let our cleanup_sig handler take care for
251 * all signals that will arise.
253 if (oldsig && (signal(SIGCHLD, oldsig) == SIG_ERR))
255 "Can't set SIGCHLD to the cleanup handler: %m");
256 sig_cleanup(SIGCHLD);
258 if (WIFEXITED(status)) {
259 status = WEXITSTATUS(status);
260 radlog(L_INFO, "Exec-Program: returned: %d", status);
263 radlog(L_ERR|L_CONS, "Exec-Program: Abnormal child exit (killed or coredump)");