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[MAX_STRING_LEN * 2];
50 for (p = str; *p; p++) {
51 if (i >= MAX_STRING_LEN)
59 if (c == '%') switch(*p) {
63 case 'f': /* Framed IP address */
65 if ((tmp = pairfind(reply,
66 PW_FRAMED_IP_ADDRESS)) != NULL) {
72 case 'n': /* NAS IP address */
74 if ((tmp = pairfind(request,
75 PW_NAS_IP_ADDRESS)) != NULL) {
83 if ((tmp = pairfind(reply,
84 PW_FRAMED_MTU)) != NULL) {
87 sprintf(buf + i, "%d", n);
90 case 'p': /* Port number */
92 if ((tmp = pairfind(request,
93 PW_NAS_PORT_ID)) != NULL) {
96 sprintf(buf + i, "%d", n);
99 case 'u': /* User name */
100 if ((tmp = pairfind(request,
101 PW_USER_NAME)) != NULL)
102 strcpy(buf + i, tmp->strvalue);
104 strcpy(buf + i, "unknown");
105 i += strlen(buf + i);
107 case 'i': /* Calling station ID */
108 if ((tmp = pairfind(request,
109 PW_CALLING_STATION_ID)) != NULL)
110 strcpy(buf + i, tmp->strvalue);
112 strcpy(buf + i, "unknown");
113 i += strlen(buf + i);
115 case 'c': /* Callback-Number */
116 if ((tmp = pairfind(reply,
117 PW_CALLBACK_NUMBER)) != NULL)
118 strcpy(buf + i, tmp->strvalue);
120 strcpy(buf + i, "unknown");
121 i += strlen(buf + i);
123 case 'a': /* Protocol: SLIP/PPP */
124 if ((tmp = pairfind(reply,
125 PW_FRAMED_PROTOCOL)) != NULL)
126 strcpy(buf + i, tmp->lvalue == PW_PPP ? "PPP" : "SLIP");
128 strcpy(buf + i, "unknown");
129 i += strlen(buf + i);
131 case 's': /* Speed */
132 if ((tmp = pairfind(request,
133 PW_CONNECT_INFO)) != NULL)
134 strcpy(buf + i, tmp->strvalue);
136 strcpy(buf + i, "unknown");
137 i += strlen(buf + i);
145 if (i >= MAX_STRING_LEN)
146 i = MAX_STRING_LEN - 1;
153 * Execute a program on successful authentication.
154 * Return 0 if exec_wait == 0.
155 * Return the exit code of the called program if exec_wait != 0.
158 int radius_exec_program(char *cmd, VALUE_PAIR *request, VALUE_PAIR **reply,
159 int exec_wait, char **user_msg)
162 static char message[256];
172 void (*oldsig)(int) = NULL;
176 * (hs) - Open a pipe for child/parent communication.
177 * - Reset the signal handler for SIGCHLD, so
178 * we have a chance to notice the dead child here and
179 * not in some signal handler.
180 * This has to be done for the exec_wait case only, since
181 * if we don't wait we aren't interested in any
186 log(L_ERR|L_CONS, "Couldn't open pipe: %m");
189 if ((oldsig = signal(SIGCHLD, SIG_DFL)) == SIG_ERR) {
190 log(L_ERR|L_CONS, "Can't reset SIGCHLD: %m");
195 if ((pid = fork()) == 0) {
199 buf = radius_xlate(cmd, request, *reply);
202 * XXX FIXME: This is debugging info.
204 log(L_INFO, "Exec-Program: %s", buf);
207 * Build vector list and execute.
209 p = strtok(buf, " \t");
212 p = strtok(NULL, " \t");
216 log(L_ERR, "Exec-Program: empty command line.");
221 if (close(pd[0]) != 0)
222 log(L_ERR|L_CONS, "Can't close pipe: %m");
223 if (dup2(pd[1], 1) != 1)
224 log(L_ERR|L_CONS, "Can't dup stdout: %m");
227 for(n = 32; n >= 3; n--)
230 execvp(argv[0], argv);
232 log(L_ERR, "Exec-Program: %s: %m", argv[0]);
240 log(L_ERR|L_CONS, "Couldn't fork: %m");
247 * (hs) Do we have a pipe?
248 * --> Close the write side of the pipe
252 if (pd[0] || pd[1]) {
253 if (close(pd[1]) != 0)
254 log(L_ERR|L_CONS, "Can't close pipe: %m");
257 * (hs) Read until we doesn't get any more
258 * or until the message is full.
261 left = sizeof(answer) - 1;
262 while ((n = read(pd[0], answer + done, left)) > 0) {
265 if (left <= 0) break;
270 * (hs) Make sure that the writer can't block
271 * while writing in a pipe that isn't read anymore.
277 * Parse the output, if any.
281 * For backwards compatibility, first check
282 * for plain text (user_msg).
285 n = userparse(answer, &vp);
286 if (vp) pairfree(vp);
290 log(L_DBG, "Exec-Program-Wait: plaintext: %s", answer);
292 strncpy(message, answer, sizeof(message));
293 message[sizeof(message) - 1] = 0;
298 * HACK: Replace '\n' with ',' so that
299 * userparse() can parse the buffer in
300 * one go (the proper way would be to
301 * fix userparse(), but oh well).
303 for (p = answer; *p; p++) {
305 *p = comma ? ' ' : ',';
308 if (*p == ',') comma++;
311 log(L_DBG,"Exec-Program-Wait: value-pairs: %s", answer);
312 if (userparse(answer, &vp) != 0)
314 "Exec-Program-Wait: %s: unparsable reply", cmd);
316 pairmove(reply, &vp);
322 while(waitpid(pid, &status, 0) != pid)
326 * (hs) Now we let our cleanup_sig handler take care for
327 * all signals that will arise.
329 if (oldsig && (signal(SIGCHLD, oldsig) == SIG_ERR))
331 "Can't set SIGCHLD to the cleanup handler: %m");
332 sig_cleanup(SIGCHLD);
334 if (WIFEXITED(status)) {
335 status = WEXITSTATUS(status);
336 log(L_INFO, "Exec-Program: returned: %d", status);
339 log(L_ERR|L_CONS, "Exec-Program: Abnormal child exit (killed or coredump)");