9b1279bce5ff57b596d1d40bfd6c612622a154df
[freeradius.git] / src / main / exec.c
1 /*
2  *   This program is free software; you can redistribute it and/or modify
3  *   it under the terms of the GNU General Public License as published by
4  *   the Free Software Foundation; either version 2 of the License, or
5  *   (at your option) any later version.
6  *
7  *   This program is distributed in the hope that it will be useful,
8  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *   GNU General Public License for more details.
11  *
12  *   You should have received a copy of the GNU General Public License
13  *   along with this program; if not, write to the Free Software
14  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16  
17 /*
18  * $Id$
19  *
20  * @file exec.c
21  * @brief Execute external programs.
22  *
23  * @copyright 2000-2004,2006  The FreeRADIUS server project
24  */
25
26 #include <freeradius-devel/ident.h>
27 RCSID("$Id$")
28
29 #include <freeradius-devel/radiusd.h>
30 #include <freeradius-devel/rad_assert.h>
31
32 #include <sys/file.h>
33
34 #include <fcntl.h>
35 #include <ctype.h>
36 #include <signal.h>
37
38 #ifdef HAVE_SYS_WAIT_H
39 #       include <sys/wait.h>
40 #endif
41 #ifndef WEXITSTATUS
42 #       define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
43 #endif
44 #ifndef WIFEXITED
45 #       define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
46 #endif
47
48 #define MAX_ARGV (256)
49
50 #define USEC 1000000
51 static void tv_sub(struct timeval *end, struct timeval *start,
52                    struct timeval *elapsed)
53 {
54         elapsed->tv_sec = end->tv_sec - start->tv_sec;
55         if (elapsed->tv_sec > 0) {
56                 elapsed->tv_sec--;
57                 elapsed->tv_usec = USEC;
58         } else {
59                 elapsed->tv_usec = 0;
60         }
61         elapsed->tv_usec += end->tv_usec;
62         elapsed->tv_usec -= start->tv_usec;
63         
64         if (elapsed->tv_usec >= USEC) {
65                 elapsed->tv_usec -= USEC;
66                 elapsed->tv_sec++;
67         }
68 }
69
70
71 /** Start a process
72  *
73  * @param cmd Command to execute. This is parsed into argv[] parts,
74  *      then each individual argv part is xlat'ed.
75  * @param request Current reuqest
76  * @param exec_wait set to 1 if you want to read from or write to child
77  * @param[in,out] input_fd pointer to int, receives the stdin file.
78  *      descriptor. Set to NULL and the child will have /dev/null on stdin
79  * @param[in,out] output_fd pinter to int, receives the stdout file
80  *      descriptor. Set to NULL and child will have /dev/null on stdout.
81  * @param input_pairs list of value pairs - these will be put into
82  *      the environment variables of the child.
83  * @param shell_escape
84  * @return PID of the child process, -1 on error.
85  */
86 pid_t radius_start_program(const char *cmd, REQUEST *request,
87                         int exec_wait,
88                         int *input_fd,
89                         int *output_fd,
90                         VALUE_PAIR *input_pairs,
91                         int shell_escape)
92 {
93         const char *from;
94         char *to;
95 #ifndef __MINGW32__
96         char *p;
97         VALUE_PAIR *vp;
98         int n;
99         int to_child[2] = {-1, -1};
100         int from_child[2] = {-1, -1};
101         pid_t pid;
102 #endif
103         int argc = -1;
104         int i;
105         int left;
106         char *argv[MAX_ARGV];
107         char argv_buf[4096];
108 #define MAX_ENVP 1024
109         char *envp[MAX_ENVP];
110         char mycmd[1024];
111
112         if (strlen(cmd) > (sizeof(mycmd) - 1)) {
113                 radlog(L_ERR, "Command line is too long");
114                 return -1;
115         }
116
117         /*
118          *      Check for bad escapes.
119          */
120         if (cmd[strlen(cmd) - 1] == '\\') {
121                 radlog(L_ERR, "Command line has final backslash, without a following character");
122                 return -1;
123         }
124
125         strlcpy(mycmd, cmd, sizeof(mycmd));
126
127         /*
128          *      Split the string into argv's BEFORE doing radius_xlat...
129          */
130         from = cmd;
131         to = mycmd;
132         argc = 0;
133         while (*from) {
134                 int length;
135
136                 /*
137                  *      Skip spaces.
138                  */
139                 if ((*from == ' ') || (*from == '\t')) {
140                         from++;
141                         continue;
142                 }
143
144                 argv[argc] = to;
145                 argc++;
146
147                 if (argc >= (MAX_ARGV - 1)) break;
148
149                 /*
150                  *      Copy the argv over to our buffer.
151                  */
152                 while (*from && (*from != ' ') && (*from != '\t')) {
153                         if (to >= mycmd + sizeof(mycmd) - 1) {
154                                 return -1; /* ran out of space */
155                         }
156
157                         switch (*from) {
158                         case '"':
159                         case '\'':
160                                 length = rad_copy_string(to, from);
161                                 if (length < 0) {
162                                         radlog(L_ERR, "Invalid string passed as argument for external program");
163                                         return -1;
164                                 }
165                                 from += length;
166                                 to += length;
167                                 break;
168
169                         case '%':
170                                 if (from[1] == '{') {
171                                         *(to++) = *(from++);
172
173                                         length = rad_copy_variable(to, from);
174                                         if (length < 0) {
175                                                 radlog(L_ERR, "Invalid variable expansion passed as argument for external program");
176                                                 return -1;
177                                         }
178                                         from += length;
179                                         to += length;
180                                 } else { /* FIXME: catch %%{ ? */
181                                         *(to++) = *(from++);
182                                 }
183                                 break;
184
185                         case '\\':
186                                 if (from[1] == ' ') from++;
187                                 /* FALL-THROUGH */
188
189                         default:
190                                 *(to++) = *(from++);
191                         }
192                 } /* end of string, or found a space */
193
194                 *(to++) = '\0'; /* terminate the string */
195         }
196
197         /*
198          *      We have to have SOMETHING, at least.
199          */
200         if (argc <= 0) {
201                 radlog(L_ERR, "Exec-Program: empty command line.");
202                 return -1;
203         }
204
205         /*
206          *      Expand each string, as appropriate.
207          */
208         to = argv_buf;
209         left = sizeof(argv_buf);
210         for (i = 0; i < argc; i++) {
211                 int sublen;
212
213                 /*
214                  *      Don't touch argv's which won't be translated.
215                  */
216                 if (strchr(argv[i], '%') == NULL) continue;
217
218                 if (!request) continue;
219
220                 sublen = radius_xlat(to, left - 1, argv[i], request, NULL, NULL);
221                 if (sublen <= 0) {
222                         /*
223                          *      Fail to be backwards compatible.
224                          *
225                          *      It's yucky, but it won't break anything,
226                          *      and it won't cause security problems.
227                          */
228                         sublen = 0;
229                 }
230
231                 argv[i] = to;
232                 to += sublen;
233                 *(to++) = '\0';
234                 left -= sublen;
235                 left--;
236
237                 if (left <= 0) {
238                         radlog(L_ERR, "Exec-Program: Ran out of space while expanding arguments.");
239                         return -1;
240                 }
241         }
242         argv[argc] = NULL;
243
244 #ifndef __MINGW32__
245         /*
246          *      Open a pipe for child/parent communication, if necessary.
247          */
248         if (exec_wait) {
249                 if (input_fd) {
250                         if (pipe(to_child) != 0) {
251                                 radlog(L_ERR, "Couldn't open pipe to child: %s",
252                                        strerror(errno));
253                                 return -1;
254                         }
255                 }
256                 if (output_fd) {
257                         if (pipe(from_child) != 0) {
258                                 radlog(L_ERR, "Couldn't open pipe from child: %s",
259                                        strerror(errno));
260                                 /* safe because these either need closing or are == -1 */
261                                 close(to_child[0]);
262                                 close(to_child[1]);
263                                 return -1;
264                         }
265                 }
266         }
267
268         envp[0] = NULL;
269
270         if (input_pairs) {
271                 int envlen;
272                 char buffer[1024];
273
274                 /*
275                  *      Set up the environment variables in the
276                  *      parent, so we don't call libc functions that
277                  *      hold mutexes.  They might be locked when we fork,
278                  *      and will remain locked in the child.
279                  */
280                 envlen = 0;
281
282                 for (vp = input_pairs; vp != NULL; vp = vp->next) {
283                         /*
284                          *      Hmm... maybe we shouldn't pass the
285                          *      user's password in an environment
286                          *      variable...
287                          */
288                         snprintf(buffer, sizeof(buffer), "%s=", vp->da->name);
289                         if (shell_escape) {
290                                 for (p = buffer; *p != '='; p++) {
291                                         if (*p == '-') {
292                                                 *p = '_';
293                                         } else if (isalpha((int) *p)) {
294                                                 *p = toupper(*p);
295                                         }
296                                 }
297                         }
298
299                         n = strlen(buffer);
300                         vp_prints_value(buffer+n, sizeof(buffer) - n, vp, shell_escape);
301
302                         envp[envlen++] = strdup(buffer);
303
304                         /*
305                          *      Don't add too many attributes.
306                          */
307                         if (envlen == (MAX_ENVP - 1)) break;
308                 }
309                 envp[envlen] = NULL;
310         }
311
312         if (exec_wait) {
313                 pid = rad_fork();       /* remember PID */
314         } else {
315                 pid = fork();           /* don't wait */
316         }
317
318         if (pid == 0) {
319                 int devnull;
320
321                 /*
322                  *      Child process.
323                  *
324                  *      We try to be fail-safe here. So if ANYTHING
325                  *      goes wrong, we exit with status 1.
326                  */
327
328                 /*
329                  *      Open STDIN to /dev/null
330                  */
331                 devnull = open("/dev/null", O_RDWR);
332                 if (devnull < 0) {
333                         radlog(L_ERR, "Failed opening /dev/null: %s\n",
334                                strerror(errno));
335                         exit(1);
336                 }
337
338                 /*
339                  *      Only massage the pipe handles if the parent
340                  *      has created them.
341                  */
342                 if (exec_wait) {
343
344                         if (input_fd) {
345                                 close(to_child[1]);
346                                 dup2(to_child[0], STDIN_FILENO);
347                         } else {
348                                 dup2(devnull, STDIN_FILENO);
349                         }
350
351                         if (output_fd) {
352                                 close(from_child[0]);
353                                 dup2(from_child[1], STDOUT_FILENO);
354                         } else {
355                                 dup2(devnull, STDOUT_FILENO);
356                         }
357
358                 } else {        /* no pipe, STDOUT should be /dev/null */
359                         dup2(devnull, STDIN_FILENO);
360                         dup2(devnull, STDOUT_FILENO);
361                 }
362
363                 /*
364                  *      If we're not debugging, then we can't do
365                  *      anything with the error messages, so we throw
366                  *      them away.
367                  *
368                  *      If we are debugging, then we want the error
369                  *      messages to go to the STDERR of the server.
370                  */
371                 if (debug_flag == 0) {
372                         dup2(devnull, STDERR_FILENO);
373                 }
374                 close(devnull);
375
376                 /*
377                  *      The server may have MANY FD's open.  We don't
378                  *      want to leave dangling FD's for the child process
379                  *      to play funky games with, so we close them.
380                  */
381                 closefrom(3);
382
383                 execve(argv[0], argv, envp);
384                 radlog(L_ERR, "Exec-Program: FAILED to execute %s: %s",
385                        argv[0], strerror(errno));
386                 exit(1);
387         }
388
389         /*
390          *      Free child environment variables
391          */
392         for (i = 0; envp[i] != NULL; i++) {
393                 free(envp[i]);
394         }
395
396         /*
397          *      Parent process.
398          */
399         if (pid < 0) {
400                 radlog(L_ERR, "Couldn't fork %s: %s",
401                        argv[0], strerror(errno));
402                 if (exec_wait) {
403                         /* safe because these either need closing or are == -1 */
404                         close(to_child[0]);
405                         close(to_child[1]);
406                         close(from_child[0]);
407                         close(from_child[0]);
408                 }
409                 return -1;
410         }
411
412         /*
413          *      We're not waiting, exit, and ignore any child's status.
414          */
415         if (exec_wait) {
416                 /*
417                  *      Close the ends of the pipe(s) the child is using
418                  *      return the ends of the pipe(s) our caller wants
419                  *
420                  */
421                 if (input_fd) {
422                         *input_fd = to_child[1];
423                         close(to_child[0]);
424                 }
425                 if (output_fd) {
426                         *output_fd = from_child[0];
427                         close(from_child[1]);
428                 }
429         }
430
431         return pid;
432 #else
433         if (exec_wait) {
434                 radlog(L_ERR, "Exec-Program-Wait is not supported");
435                 return -1;
436         }
437         
438         {
439                 /*
440                  *      The _spawn and _exec families of functions are
441                  *      found in Windows compiler libraries for
442                  *      portability from UNIX. There is a variety of
443                  *      functions, including the ability to pass
444                  *      either a list or array of parameters, to
445                  *      search in the PATH or otherwise, and whether
446                  *      or not to pass an environment (a set of
447                  *      environment variables). Using _spawn, you can
448                  *      also specify whether you want the new process
449                  *      to close your program (_P_OVERLAY), to wait
450                  *      until the new process is finished (_P_WAIT) or
451                  *      for the two to run concurrently (_P_NOWAIT).
452                  
453                  *      _spawn and _exec are useful for instances in
454                  *      which you have simple requirements for running
455                  *      the program, don't want the overhead of the
456                  *      Windows header file, or are interested
457                  *      primarily in portability.
458                  */
459
460                 /*
461                  *      FIXME: check return code... what is it?
462                  */
463                 _spawnve(_P_NOWAIT, argv[0], argv, envp);
464         }
465
466         return 0;
467 #endif
468 }
469
470 /** Read from the child process.
471  *
472  * @param fd file descriptor to read from.
473  * @param pid pid of child, will be reaped if it dies.
474  * @param timeout amount of time to wait, in seconds.
475  * @param answer buffer to write into.
476  * @param left length of buffer.
477  * @return -1 on error, or length of output.
478  */
479 int radius_readfrom_program(int fd, pid_t pid, int timeout, char *answer,
480                             int left)
481 {
482         int done = 0;
483 #ifndef __MINGW32__
484         int status;
485         struct timeval start;
486 #ifdef O_NONBLOCK
487         int nonblock = TRUE;
488 #endif
489
490 #ifdef O_NONBLOCK
491         /*
492          *      Try to set it non-blocking.
493          */
494         do {
495                 int flags;
496                 
497                 if ((flags = fcntl(fd, F_GETFL, NULL)) < 0)  {
498                         nonblock = FALSE;
499                         break;
500                 }
501                 
502                 flags |= O_NONBLOCK;
503                 if( fcntl(fd, F_SETFL, flags) < 0) {
504                         nonblock = FALSE;
505                         break;
506                 }
507         } while (0);
508 #endif
509
510
511         /*
512          *      Read from the pipe until we doesn't get any more or
513          *      until the message is full.
514          */
515         gettimeofday(&start, NULL);
516         while (1) {
517                 int rcode;
518                 fd_set fds;
519                 struct timeval when, elapsed, wake;
520
521                 FD_ZERO(&fds);
522                 FD_SET(fd, &fds);
523
524                 gettimeofday(&when, NULL);
525                 tv_sub(&when, &start, &elapsed);
526                 if (elapsed.tv_sec >= timeout) goto too_long;
527                 
528                 when.tv_sec = timeout;
529                 when.tv_usec = 0;
530                 tv_sub(&when, &elapsed, &wake);
531
532                 rcode = select(fd + 1, &fds, NULL, NULL, &wake);
533                 if (rcode == 0) {
534                 too_long:
535                         radlog(L_ERR, "Child PID %u is taking too much time: forcing failure and killing child.", pid);
536                         kill(pid, SIGTERM);
537                         close(fd); /* should give SIGPIPE to child, too */
538
539                         /*
540                          *      Clean up the child entry.
541                          */
542                         rad_waitpid(pid, &status);
543                         return -1;
544                 }
545                 if (rcode < 0) {
546                         if (errno == EINTR) continue;
547                         break;
548                 }
549
550 #ifdef O_NONBLOCK
551                 /*
552                  *      Read as many bytes as possible.  The kernel
553                  *      will return the number of bytes available.
554                  */
555                 if (nonblock) {
556                         status = read(fd, answer + done, left);
557                 } else 
558 #endif
559                         /*
560                          *      There's at least 1 byte ready: read it.
561                          */
562                         status = read(fd, answer + done, 1);
563
564                 /*
565                  *      Nothing more to read: stop.
566                  */
567                 if (status == 0) {
568                         break;
569                 }
570
571                 /*
572                  *      Error: See if we have to continue.
573                  */
574                 if (status < 0) {
575                         /*
576                          *      We were interrupted: continue reading.
577                          */
578                         if (errno == EINTR) {
579                                 continue;
580                         }
581
582                         /*
583                          *      There was another error.  Most likely
584                          *      The child process has finished, and
585                          *      exited.
586                          */
587                         break;
588                 }
589
590                 done += status;
591                 left -= status;
592                 if (left <= 0) break;
593         }
594 #endif  /* __MINGW32__ */
595         return done;
596 }
597
598 /** Execute a program.
599  *
600  * @param cmd Command to execute. This is parsed into argv[] parts,
601  *      then each individual argv part is xlat'ed.
602  * @param request current request.
603  * @param exec_wait set to 1 if you want to read from or write to child
604  * @param user_msg buffer to append plaintext (non valuepair) output.
605  * @param msg_len length of user_msg buffer.
606  * @param input_pairs list of value pairs - these will be put into
607  *      the environment variables of the child.
608  * @param[out] output_pairs list of value pairs - child stdout will be
609  *      parsed and added into this list of value pairs.
610  * @param shell_escape
611  * @return 0 if exec_wait==0, exit code if exec_wait!=0, -1 on error.
612  */
613 int radius_exec_program(const char *cmd, REQUEST *request,
614                         int exec_wait,
615                         char *user_msg, int msg_len,
616                         VALUE_PAIR *input_pairs,
617                         VALUE_PAIR **output_pairs,
618                         int shell_escape)
619 {
620         pid_t pid;
621         int from_child;
622 #ifndef __MINGW32__
623         VALUE_PAIR *vp;
624         char *p;
625         pid_t child_pid;
626         int comma = 0;
627         int status;
628         int n, done;
629         char answer[4096];
630 #endif
631
632         pid = radius_start_program(cmd, request, exec_wait, NULL, &from_child, input_pairs, shell_escape);
633         if (pid < 0) {
634                 return -1;
635         }
636
637         if (!exec_wait)
638                 return 0;
639
640 #ifndef __MINGW32__
641         done = radius_readfrom_program(from_child, pid, 10, answer, sizeof(answer));
642         if (done < 0) {
643                 /*
644                  * failure - radius_readfrom_program will
645                  * have called close(from_child) for us
646                  */
647                 DEBUG("failed to read from child output");
648                 return 1;
649
650         }
651         answer[done] = 0;
652
653
654         /*
655          *      Make sure that the writer can't block while writing to
656          *      a pipe that no one is reading from anymore.
657          */
658         close(from_child);
659
660         DEBUG2("Exec-Program output: %s", answer);
661
662         /*
663          *      Parse the output, if any.
664          */
665         if (done) {
666                 n = T_OP_INVALID;
667                 if (output_pairs) {
668                         /*
669                          *      For backwards compatibility, first check
670                          *      for plain text (user_msg).
671                          */
672                         vp = NULL;
673                         n = userparse(answer, &vp);
674                         if (vp) {
675                                 pairfree(&vp);
676                         }
677                 }
678
679                 if (n == T_OP_INVALID) {
680                         DEBUG("Exec-Program-Wait: plaintext: %s", answer);
681                         if (user_msg) {
682                                 strlcpy(user_msg, answer, msg_len);
683                         }
684                 } else {
685                         /*
686                          *      HACK: Replace '\n' with ',' so that
687                          *      userparse() can parse the buffer in
688                          *      one go (the proper way would be to
689                          *      fix userparse(), but oh well).
690                          */
691                         for (p = answer; *p; p++) {
692                                 if (*p == '\n') {
693                                         *p = comma ? ' ' : ',';
694                                         p++;
695                                         comma = 0;
696                                 }
697                                 if (*p == ',') comma++;
698                         }
699
700                         /*
701                          *      Replace any trailing comma by a NUL.
702                          */
703                         if (answer[strlen(answer) - 1] == ',') {
704                                 answer[strlen(answer) - 1] = '\0';
705                         }
706
707                         radlog(L_DBG,"Exec-Program-Wait: value-pairs: %s", answer);
708                         if (userparse(answer, &vp) == T_OP_INVALID) {
709                                 radlog(L_ERR, "Exec-Program-Wait: %s: unparsable reply", cmd);
710
711                         } else {
712                                 /*
713                                  *      Tell the caller about the value
714                                  *      pairs.
715                                  */
716                                 *output_pairs = vp;
717                         }
718                 } /* else the answer was a set of VP's, not a text message */
719         } /* else we didn't read anything from the child */
720
721         /*
722          *      Call rad_waitpid (should map to waitpid on non-threaded
723          *      or single-server systems).
724          */
725         child_pid = rad_waitpid(pid, &status);
726         if (child_pid == 0) {
727                 radlog(L_DBG, "Exec-Program: Timeout waiting for child");
728                 return 2;
729         }
730
731         if (child_pid == pid) {
732                 if (WIFEXITED(status)) {
733                         status = WEXITSTATUS(status);
734                         radlog(L_DBG, "Exec-Program: returned: %d", status);
735                         return status;
736                 }
737         }
738
739         radlog(L_ERR, "Exec-Program: Abnormal child exit: %s",
740                strerror(errno));
741 #endif  /* __MINGW32__ */
742
743         return 1;
744 }
745
746 static void time_free(void *data)
747 {
748         free(data);
749 }
750
751 void exec_trigger(REQUEST *request, CONF_SECTION *cs, const char *name, int quench)
752 {
753         CONF_SECTION *subcs;
754         CONF_ITEM *ci;
755         CONF_PAIR *cp;
756         const char *attr;
757         const char *value;
758         VALUE_PAIR *vp;
759
760         /*
761          *      Use global "trigger" section if no local config is given.
762          */
763         if (!cs) {
764                 cs = mainconfig.config;
765                 attr = name;
766         } else {
767                 /*
768                  *      Try to use pair name, rather than reference.
769                  */
770                 attr = strrchr(name, '.');
771                 if (attr) {
772                         attr++;
773                 } else {
774                         attr = name;
775                 }
776         }
777
778         /*
779          *      Find local "trigger" subsection.  If it isn't found,
780          *      try using the global "trigger" section, and reset the
781          *      reference to the full path, rather than the sub-path.
782          */
783         subcs = cf_section_sub_find(cs, "trigger");
784         if (!subcs && (cs != mainconfig.config)) {
785                 subcs = cf_section_sub_find(mainconfig.config, "trigger");
786                 attr = name;
787         }
788
789         if (!subcs) return;
790
791         ci = cf_reference_item(subcs, mainconfig.config, attr);
792         if (!ci) {
793                 DEBUG3("No such item in trigger section: %s", attr);
794                 return;
795         }
796
797         if (!cf_item_is_pair(ci)) {
798                 DEBUG2("Trigger is not a configuration variable: %s", attr);
799                 return;
800         }
801
802         cp = cf_itemtopair(ci);
803         if (!cp) return;
804
805         value = cf_pair_value(cp);
806         if (!value) {
807                 DEBUG2("Trigger has no value: %s", name);
808                 return;
809         }
810
811         /*
812          *      May be called for Status-Server packets.
813          */
814         vp = NULL;
815         if (request && request->packet) vp = request->packet->vps;
816
817         /*
818          *      Perform periodic quenching.
819          */
820         if (quench) {
821                 time_t *last_time;
822
823                 last_time = cf_data_find(cs, value);
824                 if (!last_time) {
825                         last_time = rad_malloc(sizeof(*last_time));
826                         *last_time = 0;
827
828                         if (cf_data_add(cs, value, last_time, time_free) < 0) {
829                                 free(last_time);
830                                 last_time = NULL;
831                         }
832                 }
833
834                 /*
835                  *      Send the quenched traps at most once per second.
836                  */
837                 if (last_time) {
838                         time_t now = time(NULL);
839                         if (*last_time == now) return;
840
841                         *last_time = now;
842                 }
843         }
844
845         DEBUG("Trigger %s -> %s", name, value);
846         radius_exec_program(value, request, 0, NULL, 0, vp, NULL, 1);
847 }