2 * exec.c Execute external programs.
4 * Version: @(#)exec.c 1.83 07-Aug-1999 miquels@cistron.nl
8 "@(#)exec.c 1.83 Copyright 1999 Cistron Internet Services B.V.";
12 #include <sys/types.h>
30 * Replace %<whatever> in a string.
34 * %f Framed IP address
38 * %a Protocol (SLIP/PPP)
39 * %s Speed (PW_CONNECT_INFO)
40 * %i Calling Station ID
43 char *radius_xlate(char *str, VALUE_PAIR *request, VALUE_PAIR *reply)
45 static char buf[2048];
50 for (p = str; *p; p++) {
57 if (c == '%') switch(*p) {
61 case 'f': /* Framed IP address */
63 if ((tmp = pairfind(reply,
64 PW_FRAMED_IP_ADDRESS)) != NULL) {
70 case 'n': /* NAS IP address */
72 if ((tmp = pairfind(request,
73 PW_NAS_IP_ADDRESS)) != NULL) {
81 if ((tmp = pairfind(reply,
82 PW_FRAMED_MTU)) != NULL) {
85 sprintf(buf + i, "%d", n);
88 case 'p': /* Port number */
90 if ((tmp = pairfind(request,
91 PW_NAS_PORT_ID)) != NULL) {
94 sprintf(buf + i, "%d", n);
97 case 'u': /* User name */
98 if ((tmp = pairfind(request,
99 PW_USER_NAME)) != NULL)
100 strcpy(buf + i, tmp->strvalue);
102 strcpy(buf + i, "unknown");
103 i += strlen(buf + i);
105 case 'i': /* Calling station ID */
106 if ((tmp = pairfind(request,
107 PW_CALLING_STATION_ID)) != NULL)
108 strcpy(buf + i, tmp->strvalue);
110 strcpy(buf + i, "unknown");
111 i += strlen(buf + i);
113 case 'c': /* Callback-Number */
114 if ((tmp = pairfind(reply,
115 PW_CALLBACK_NUMBER)) != NULL)
116 strcpy(buf + i, tmp->strvalue);
118 strcpy(buf + i, "unknown");
119 i += strlen(buf + i);
121 case 'a': /* Protocol: SLIP/PPP */
122 if ((tmp = pairfind(reply,
123 PW_FRAMED_PROTOCOL)) != NULL)
124 strcpy(buf + i, tmp->lvalue == PW_PPP ? "PPP" : "SLIP");
126 strcpy(buf + i, "unknown");
127 i += strlen(buf + i);
129 case 's': /* Speed */
130 if ((tmp = pairfind(request,
131 PW_CONNECT_INFO)) != NULL)
132 strcpy(buf + i, tmp->strvalue);
134 strcpy(buf + i, "unknown");
135 i += strlen(buf + i);
143 if (i >= MAX_STRING_LEN)
144 i = MAX_STRING_LEN - 1;
151 * Execute a program on successful authentication.
152 * Return 0 if exec_wait == 0.
153 * Return the exit code of the called program if exec_wait != 0.
156 int radius_exec_program(char *cmd, VALUE_PAIR *request, VALUE_PAIR **reply,
157 int exec_wait, char **user_msg)
160 static char message[256];
170 void (*oldsig)(int) = NULL;
174 * (hs) - Open a pipe for child/parent communication.
175 * - Reset the signal handler for SIGCHLD, so
176 * we have a chance to notice the dead child here and
177 * not in some signal handler.
178 * This has to be done for the exec_wait case only, since
179 * if we don't wait we aren't interested in any
184 log(L_ERR|L_CONS, "Couldn't open pipe: %m");
187 if ((oldsig = signal(SIGCHLD, SIG_DFL)) == SIG_ERR) {
188 log(L_ERR|L_CONS, "Can't reset SIGCHLD: %m");
193 if ((pid = fork()) == 0) {
197 buf = radius_xlate(cmd, request, *reply);
200 * XXX FIXME: This is debugging info.
202 log(L_INFO, "Exec-Program: %s", buf);
205 * Build vector list and execute.
207 p = strtok(buf, " \t");
210 p = strtok(NULL, " \t");
214 log(L_ERR, "Exec-Program: empty command line.");
219 if (close(pd[0]) != 0)
220 log(L_ERR|L_CONS, "Can't close pipe: %m");
221 if (dup2(pd[1], 1) != 1)
222 log(L_ERR|L_CONS, "Can't dup stdout: %m");
225 for(n = 32; n >= 3; n--)
228 execvp(argv[0], argv);
230 log(L_ERR, "Exec-Program: %s: %m", argv[0]);
238 log(L_ERR|L_CONS, "Couldn't fork: %m");
245 * (hs) Do we have a pipe?
246 * --> Close the write side of the pipe
250 if (pd[0] || pd[1]) {
251 if (close(pd[1]) != 0)
252 log(L_ERR|L_CONS, "Can't close pipe: %m");
255 * (hs) Read until we doesn't get any more
256 * or until the message is full.
259 left = sizeof(answer) - 1;
260 while ((n = read(pd[0], answer + done, left)) > 0) {
263 if (left <= 0) break;
268 * (hs) Make sure that the writer can't block
269 * while writing in a pipe that isn't read anymore.
275 * Parse the output, if any.
279 * For backwards compatibility, first check
280 * for plain text (user_msg).
283 n = userparse(answer, &vp);
284 if (vp) pairfree(vp);
288 log(L_DBG, "Exec-Program-Wait: plaintext: %s", answer);
290 strncpy(message, answer, sizeof(message));
291 message[sizeof(message) - 1] = 0;
296 * HACK: Replace '\n' with ',' so that
297 * userparse() can parse the buffer in
298 * one go (the proper way would be to
299 * fix userparse(), but oh well).
301 for (p = answer; *p; p++) {
303 *p = comma ? ' ' : ',';
306 if (*p == ',') comma++;
309 log(L_DBG,"Exec-Program-Wait: value-pairs: %s", answer);
310 if (userparse(answer, &vp) != 0)
312 "Exec-Program-Wait: %s: unparsable reply", cmd);
314 pairmove(reply, &vp);
320 while(waitpid(pid, &status, 0) != pid)
324 * (hs) Now we let our cleanup_sig handler take care for
325 * all signals that will arise.
327 if (oldsig && (signal(SIGCHLD, oldsig) == SIG_ERR))
329 "Can't set SIGCHLD to the cleanup handler: %m");
330 sig_cleanup(SIGCHLD);
332 if (WIFEXITED(status)) {
333 status = WEXITSTATUS(status);
334 log(L_INFO, "Exec-Program: returned: %d", status);
337 log(L_ERR|L_CONS, "Exec-Program: Abnormal child exit (killed or coredump)");