2 * radrelay.c This program tails a detail logfile, reads the log
3 * entries, forwards them to a remote radius server,
4 * and moves the processed records to another file.
6 * Used to replicate accounting records to one (central)
7 * server - works even if remote server has extended
8 * downtime, and/or if this program is restarted.
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * Copyright 2001 Cistron Internet Services B.V.
25 * Copyright 2002 Simon Ekstrand <simon@routemeister.net>
28 char radrelay_rcsid[] =
33 #include <sys/types.h>
34 #include <sys/socket.h>
37 #ifdef HAVE_NETINET_IN_H
38 #include <netinet/in.h>
60 const char *radlog_dir = NULL;
61 radlog_dest_t radlog_dest = RADLOG_FILES;
63 const char *radius_dir = NULL;
64 const char *radacct_dir = NULL;
65 const char *radlib_dir = NULL;
66 uint32_t myip = INADDR_ANY;
67 int log_stripped_names;
68 struct main_config_t mainconfig;
71 * Possible states for request->state
79 * Possible states for the loop() function.
82 #define STATE_BACKLOG 1
84 #define STATE_SHUTDOWN 3
88 #define DEFAULT_SLEEP 50
89 #define DEFAULT_SLEEP_EVERY 1
94 struct relay_request {
95 int state; /* REQ_* state */
96 time_t retrans; /* when to retrans */
97 unsigned int retrans_num; /* Number of retransmissions */
98 time_t timestamp; /* orig recv time */
99 uint32_t client_ip; /* Client-IP-Addr */
100 RADIUS_PACKET *req; /* Radius request */
104 int sockfd; /* Main socket descriptor */
105 uint32_t dst_addr; /* Destination address */
106 short dst_port; /* Destination port */
107 uint32_t src_addr; /* Source address */
108 char detail[1024]; /* Detail file */
109 char *secret; /* Secret */
110 char f_secret[256]; /* File secret */
111 int sleep_time; /* Time to sleep between sending packets */
112 int sleep_every; /* Sleep every so many packets */
113 int records_print; /* Print statistics after so many records */
118 uint32_t records_read; /* Records read */
119 uint32_t packets_sent; /* Packets sent */
120 uint32_t last_print_records; /* Records on last statistics printout */
124 * Used for reading the client configurations from the config files.
126 char *c_secret = NULL;
127 char *c_shortname = NULL;
129 struct relay_request slots[NR_SLOTS];
131 int request_head = 0;
136 int get_radius_id(void);
137 void sigterm_handler(int sig);
138 void ms_sleep(int msec);
139 int isdateline(char *d);
140 int read_one(FILE *fp, struct relay_request *req);
141 int do_recv(struct relay_misc *r_args);
142 int do_send(struct relay_request *r, char *secret);
143 int detail_move(char *from, char *to);
144 void loop(struct relay_misc *r_args);
145 int find_shortname(char *shortname, char **host, char **secret);
150 * Get a radius id which is not
151 * currently being used (outstanding request)
152 * Since NR_SLOTS < 256 we can't
153 * have more outstanding requests than radius ids
159 for(id = 0; id < 256; id++){
163 if (id == 256 || id_map[id] != 0){
164 fprintf(stdout, "get_radius_id(): No IDs available. Something is very wrong\n");
168 fprintf(stdout, "get_radius_id(): Assign RADIUS ID = %d\n",id);
173 void sigterm_handler(int sig)
175 signal(sig, sigterm_handler);
181 * Sleep a number of milli seconds
183 inline void ms_sleep(int msec)
187 tv.tv_sec = (msec / 1000);
188 tv.tv_usec = (msec % 1000) * 1000;
189 select(0, NULL, NULL, NULL, &tv);
193 * Does this (remotely) look like "Tue Jan 23 06:55:48 2001" ?
195 inline int isdateline(char *d)
199 return sscanf(d, "%*s %*s %*d %*d:%*d:%*d %d", &y);
204 * Read one request from the detail file.
205 * Note that the file is locked during the read, and that
206 * we return *with the file locked* if we reach end-of-file.
208 * STATE_EMPTY: Slot is empty.
209 * STATE_BUSY1: Looking for start of a detail record (timestamp)
210 * STATE_BUSY2: Reading the A/V pairs of a detail record.
211 * STATE_FULL: Read the complete record.
214 int read_one(FILE *fp, struct relay_request *r_req)
219 char key[32], val[32];
226 if (r_req->state == STATE_FULL)
229 if (r_req->state == STATE_EMPTY) {
230 r_req->state = STATE_BUSY1;
234 * Try to lock the detail-file.
235 * If lockf is used we want to lock the _whole_ file, hence the
236 * fseek to the start of the file.
239 fseek(fp, 0L, SEEK_SET);
241 x = rad_lockfd_nonblock(fileno(fp), 0);
244 } while (x == -1 && i++ < 20);
251 fseek(fp, fpos, SEEK_SET);
253 while ((s = fgets(buf, sizeof(buf), fp)) != NULL) {
255 * Eek! We've just read a broken attribute.
256 * This does seem to happen every once in a long while
257 * due to some quirk involving threading, multiple processes
258 * going for the detail file lock at once and writes not
259 * being flushed properly. Things should be ok next time
263 fprintf(stdout, "read_one: ZERO BYTE\n");
264 fseek(fp, fpos + 1, SEEK_SET);
266 } else if (buf[strlen(buf) - 1] != '\n') {
267 fprintf(stdout, "read_one: BROKEN ATTRIBUTE\n");
268 fseek(fp, fpos + strlen(buf), SEEK_SET);
271 if (r_req->state == STATE_BUSY1) {
272 if (isdateline(buf)) {
273 r_req->state = STATE_BUSY2;
275 } else if (r_req->state == STATE_BUSY2) {
276 if (buf[0] != ' ' && buf[0] != '\t') {
277 r_req->state = STATE_FULL;
281 * Found A/V pair, but we skip non-protocol
285 if (sscanf(buf, "%31s = %31s", key, val) == 2) {
286 if (!strcasecmp(key, "Timestamp")) {
287 r_req->timestamp = atoi(val);
290 if (!strcasecmp(key, "Client-IP-Address")) {
291 r_req->client_ip = ip_getaddr(val);
294 if (!strcasecmp(key, "Request-Authenticator"))
299 if (userparse(buf, &vp) > 0 &&
301 (vp->attribute < 256 ||
302 vp->attribute > 65535) &&
303 vp->attribute != PW_VENDOR_SPECIFIC) {
304 pairadd(&(r_req->req->vps), vp);
314 if (r_req->state == STATE_FULL) {
316 * w00 - we just completed reading a record in full.
320 * Check that we have an Acct-Status-Type attribute. If not
323 if (pairfind(r_req->req->vps, PW_ACCT_STATUS_TYPE) == NULL){
324 fprintf(stdout, "read_one: No Acct-Status-Type attribute present. Rejecting record.\n");
325 r_req->state = STATE_BUSY1;
326 if (r_req->req->vps != NULL) {
327 pairfree(&r_req->req->vps);
328 r_req->req->vps = NULL;
330 if (r_req->req->data != NULL) {
331 free (r_req->req->data);
332 r_req->req->data = NULL;
335 r_req->retrans_num = 0;
336 r_req->timestamp = 0;
337 r_req->client_ip = 0;
340 if (r_req->timestamp == 0)
341 r_req->timestamp = time(NULL);
342 if ((vp = pairfind(r_req->req->vps, PW_ACCT_DELAY_TIME)) != NULL) {
343 r_req->timestamp -= vp->lvalue;
346 r_req->req->id = get_radius_id();
351 * Apparently we reached end of file. If we didn't
352 * partially read a record, we let the caller know
353 * we're at end of file.
355 if (r_req->state == STATE_BUSY1) {
356 r_req->state = STATE_EMPTY;
358 if (r_req->state == STATE_EMPTY || r_req->state == STATE_FULL)
363 fseek(fp, 0L, SEEK_SET);
364 rad_unlockfd(fileno(fp), 0);
365 fseek(fp, fpos, SEEK_SET);
371 * Receive answers from the remote server.
373 int do_recv(struct relay_misc *r_args)
376 struct relay_request *r;
380 * Receive packet and validate it's length.
382 rep = rad_recv(r_args->sockfd);
384 librad_perror("radrelay:");
389 * Must be an accounting response.
390 * FIXME: check if this is the right server!
392 if (rep->code != PW_ACCOUNTING_RESPONSE) {
398 * Decode packet into radius attributes.
402 * Now find it in the outstanding requests.
404 for (i = 0; i < NR_SLOTS; i++) {
406 if (r->state == STATE_FULL && r->req->id == rep->id) {
407 if (rad_verify(rep, r->req, r_args->secret) != 0) {
408 librad_perror("rad_verify");
412 if (rad_decode(rep, r->req, r_args->secret) != 0) {
413 librad_perror("rad_decode");
418 * Got it. Clear slot.
419 * FIXME: check reponse digest ?
421 id_map[r->req->id] = 0;
422 fprintf(stdout, "do_recv: Free RADIUS ID = %d\n",r->req->id);
423 if (r->req->vps != NULL) {
424 pairfree(&r->req->vps);
427 if (r->req->data != NULL) {
431 r->state = STATE_EMPTY;
446 * Send accounting packet to remote server.
448 int do_send(struct relay_request *r, char *secret)
456 if (r->client_ip == r->req->dst_ipaddr) {
457 fprintf(stdout, "do_send: Client-IP == Dest-IP. Droping packet.\n");
458 fprintf(stdout, "do_send: Free RADIUS ID = %d\n",r->req->id);
459 id_map[r->req->id] = 0;
460 if (r->req->vps != NULL) {
461 pairfree(&r->req->vps);
464 if (r->req->data != NULL) {
468 r->state = STATE_EMPTY;
477 * Has the time come for this packet ?
480 if (r->retrans > now)
483 * If we are resending a packet we *need* to
484 * change the radius packet id since the request
485 * authenticator is different (due to different
486 * Acct-Delay-Time value).
487 * Otherwise the radius server may consider the
488 * packet a duplicate and we 'll get caught in a
492 id_map[r->req->id] = 0;
493 r->req->id = get_radius_id();
494 if (r->req->data != NULL){
500 if (r->retrans_num > 20)
501 r->retrans = now + 70;
503 r->retrans = now + 3 + (3 * r->retrans_num);
506 * Find the Acct-Delay-Time attribute. If it's
507 * not there, add one.
509 if ((vp = pairfind(r->req->vps, PW_ACCT_DELAY_TIME)) == NULL) {
510 vp = paircreate(PW_ACCT_DELAY_TIME, PW_TYPE_INTEGER);
511 pairadd(&(r->req->vps), vp);
513 vp->lvalue = (now - r->timestamp);
516 * Rebuild the entire packet every time from
517 * scratch - the signature changed because
518 * Acct-Delay-Time changed.
520 rad_send(r->req, NULL, secret);
526 * Rename a file, then recreate the old file with the
527 * same permissions and zero size.
529 int detail_move(char *from, char *to)
535 if (stat(from, &st) < 0)
537 if (rename(from, to) < 0)
541 if ((n = open(from, O_CREAT|O_RDWR, st.st_mode)) >= 0)
550 * Open detail file, collect records, send them to the
551 * remote accounting server, yadda yadda yadda.
553 * STATE_RUN: Reading from detail file, sending to server.
554 * STATE_BACKLOG: Reading from the detail.work file, for example
555 * after a crash or restart. Sending to server.
556 * STATE_WAIT: Waiting for all outstanding requests to be handled.
557 * STATE_CLOSE: Reached end of detail.work file, waiting for
558 * outstanding requests, and removing the file.
559 * STATE_SHUTDOWN: Got SIG_TERM, waiting for outstanding requests
560 * and exiting program.
562 void loop(struct relay_misc *r_args)
565 struct relay_request *r;
567 struct relay_stats stats;
570 time_t now, uptime, last_rename = 0;
572 int state = STATE_RUN;
576 strNcpy(work, r_args->detail, sizeof(work) - 6);
577 strcat(work, ".work");
579 id = ((int)getpid() & 0xff);
581 memset(&stats,0,sizeof(struct relay_stats));
582 stats.startup = time(NULL);
585 * Initialize all our slots, might as well do this right away.
587 for (i = 0; i < NR_SLOTS; i++) {
588 if ((slots[i].req = rad_alloc(1)) == NULL) {
589 librad_perror("radrelay");
592 slots[i].state = STATE_EMPTY;
593 slots[i].retrans = 0;
594 slots[i].retrans_num = 0;
595 slots[i].timestamp = 0;
596 slots[i].client_ip = 0;
597 slots[i].req->sockfd = r_args->sockfd;
598 slots[i].req->dst_ipaddr = r_args->dst_addr;
599 slots[i].req->dst_port = r_args->dst_port;
600 slots[i].req->src_ipaddr = r_args->src_addr;
601 slots[i].req->code = PW_ACCOUNTING_REQUEST;
602 slots[i].req->vps = NULL;
603 slots[i].req->data = NULL;
607 if (got_sigterm) state = STATE_SHUTDOWN;
610 * Open detail file - if needed, and if we can.
612 if (state == STATE_RUN && fp == NULL) {
613 if ((fp = fopen(work, "r+")) != NULL)
614 state = STATE_BACKLOG;
616 fp = fopen(r_args->detail, "r+");
618 fprintf(stderr, "%s: Unable to open detail file - %s\n", progname, r_args->detail);
626 * If "request_head" points to a free or not-completely-
627 * filled slot, we can read from the detail file.
629 r = &slots[request_head];
630 if (fp && (state == STATE_RUN || state == STATE_BACKLOG) &&
631 r->state != STATE_FULL) {
632 if (read_one(fp, r) == EOF) do {
635 * We've reached end of the <detail>.work
636 * It's going to be closed as soon as all
637 * outstanting requests are handled
639 if (state == STATE_BACKLOG) {
645 * End of file. See if the file has
646 * any size, and if we renamed less
647 * than 10 seconds ago or not.
650 if (ftell(fp) == 0 || now < last_rename + 10) {
652 fseek(fp, 0L, SEEK_SET);
653 rad_unlockfd(fileno(fp), 0);
654 fseek(fp, fpos, SEEK_SET);
660 * We rename the file to <file>.work
661 * and create an empty new file.
663 if (detail_move(r_args->detail, work) == 0) {
665 fprintf(stderr, "Moving %s to %s\n",
666 r_args->detail, work);
668 * rlm_detail might still write
669 * something to <detail>.work if
670 * it opens <detail> before it is
671 * renamed (race condition)
674 state = STATE_BACKLOG;
677 fseek(fp, 0L, SEEK_SET);
678 rad_unlockfd(fileno(fp), 0);
679 fseek(fp, fpos, SEEK_SET);
681 if (r_args->records_print && state == STATE_RUN){
682 stats.records_read++;
683 if (stats.last_print_records - stats.records_read >= r_args->records_print){
685 uptime = (stats.startup == now) ? 1 : now - stats.startup;
686 fprintf(stderr, "%s: Running and Processing Records.\n",progname);
687 fprintf(stderr, "Seconds since startup: %ld\n",uptime);
688 fprintf(stderr, "Records Read: %d\n",stats.records_read);
689 fprintf(stderr, "Packets Sent: %d\n",stats.packets_sent);
690 fprintf(stderr, "Record Rate since startup: %.2f\n",
691 (double)stats.records_read / uptime);
692 fprintf(stderr, "Packet Rate since startup: %.2f\n",
693 (double)stats.packets_sent / uptime);
694 stats.last_print_records = stats.records_read;
697 if (r->state == STATE_FULL)
698 request_head = (request_head + 1) % NR_SLOTS;
702 * Perhaps we can receive something.
707 FD_SET(r_args->sockfd, &readfds);
709 while (select(r_args->sockfd + 1, &readfds, NULL, NULL, &tv) > 0) {
711 if (n++ >= NR_SLOTS) break;
715 * If we're in STATE_WAIT and all slots are
716 * finally empty, we can remove the <detail>.work
718 if (state == STATE_WAIT || state == STATE_CLOSE || state == STATE_SHUTDOWN) {
719 for (i = 0; i < NR_SLOTS; i++)
720 if (slots[i].state != STATE_EMPTY)
723 if (state == STATE_CLOSE) {
727 fprintf(stderr, "Unlink file %s\n", work);
730 else if (state == STATE_SHUTDOWN) {
731 for (i = 0; i < NR_SLOTS; i++) {
732 rad_free(&slots[i].req);
741 * See if there's anything to send.
744 for (i = 0; i < NR_SLOTS; i++) {
745 if (slots[i].state == STATE_FULL) {
746 n += do_send(&slots[i], r_args->secret);
747 if ((n % r_args->sleep_every) == 0)
748 ms_sleep(r_args->sleep_time);
749 if (n > NR_SLOTS / 2)
753 if (r_args->records_print)
754 stats.packets_sent += n;
759 * Search through the "client" config sections (usually in clients.conf).
760 * This is an easy way to find a secret and an host.
762 int find_shortname(char *shortname, char **host, char **secret)
764 CONF_SECTION *maincs, *cs;
767 /* Lets go look for the new configuration files */
768 memset(&mainconfig, 0, sizeof(mainconfig)); /* for radlog() */
769 snprintf(buffer, sizeof(buffer), "%.200s/radiusd.conf", radius_dir);
770 if ((maincs = conf_read(NULL, 0, buffer, NULL)) == NULL) {
775 * Find the first 'client' section.
777 cs = cf_section_sub_find(maincs, "client");
779 c_shortname = cf_section_value_find(cs, "shortname");
780 c_secret = cf_section_value_find(cs, "secret");
782 * Keep searching for 'client' sections until they run out
783 * or we find one that matches.
785 while (cs && strcmp(shortname, c_shortname)) {
786 cs = cf_subsection_find_next(cs, cs, "client");
788 c_shortname = cf_section_value_find(cs, "shortname");
789 c_secret = cf_section_value_find(cs, "secret");
795 *host = cf_section_name2(cs);
806 fprintf(stderr, "Usage: radrelay [-a accounting_dir] [-d radius_dir] [-i local_ip] [-s secret]\n");
807 fprintf(stderr, "[-e sleep_every packets] [-t sleep_time (ms)] [-S secret_file] [-fx]\n");
808 fprintf(stderr, "[-R records_print] <[-n shortname] [-r remote-server[:port]]> detailfile\n");
809 fprintf(stderr, " -a accounting_dir Base accounting directory.\n");
810 fprintf(stderr, " -d radius_dir Base radius (raddb) directory.\n");
811 fprintf(stderr, " -f Stay in the foreground (don't fork).\n");
812 fprintf(stderr, " -h This help.\n");
813 fprintf(stderr, " -i local_ip Use local_ip as source address.\n");
814 fprintf(stderr, " -n shortname Use the [shortname] entry from clients.conf for\n");
815 fprintf(stderr, " ip-adress and secret.\n");
816 fprintf(stderr, " -t sleep_time Sleep so much time (in ms) between sending packets. Default: %dms.\n",
818 fprintf(stderr, " -e sleep_every Sleep after sending so many packets. Default: %d\n",
819 DEFAULT_SLEEP_EVERY);
820 fprintf(stderr, " -R records_print If in foreground mode, print statistics after so many records read.\n");
821 fprintf(stderr, " -r remote-server The destination address/hostname.\n");
822 fprintf(stderr, " -s secret Server secret.\n");
823 fprintf(stderr, " -S secret_file Read server secret from file.\n");
824 fprintf(stderr, " -x Debug mode (-xx gives more debugging).\n");
829 int main(int argc, char **argv)
838 struct relay_misc r_args;
847 memset((char *) r_args.detail, 0, 1024);
848 memset((char *) r_args.f_secret, 0, 256);
849 r_args.secret = NULL;
850 r_args.sleep_time = DEFAULT_SLEEP;
851 r_args.sleep_every = DEFAULT_SLEEP_EVERY;
856 radius_dir = strdup(RADIUS_DIR);
861 * Make sure there are stdin/stdout/stderr fds.
863 while ((c = open("/dev/null", O_RDWR)) < 3 && c >= 0);
864 if (c >= 3) close(c);
867 * Process the options.
869 while ((c = getopt(argc, argv, "a:d:fhi:t:e:n:r:R:s:S:x")) != EOF) switch(c) {
871 if (strlen(optarg) > 1021) {
872 fprintf(stderr, "%s: acct_dir to long\n", progname);
875 strncpy(r_args.detail, optarg, 1021);
880 radius_dir = strdup(optarg);
889 r_args.sleep_time = atoi(optarg);
892 r_args.sleep_every = atoi(optarg);
896 fprintf(stderr, "%s: Not in foreground mode. Can't print statistics.\n",progname);
899 r_args.records_print = atoi(optarg);
902 server_name = optarg;
905 r_args.secret = optarg;
909 * If -x is called once we enable internal radrelay
910 * debugging, if it's called twice we also active
911 * lib_rad debugging (fairly verbose).
919 sfile_fp = fopen(optarg, "r");
920 if (sfile_fp == NULL) {
921 fprintf(stderr, "Error opening %s: %s\n",
922 optarg, strerror(errno));
926 if (fgets(r_args.f_secret, 256, sfile_fp) == NULL) {
927 fprintf(stderr, "Error reading from %s: %s\n",
928 optarg, strerror(errno));
934 for (c = 0; c < strlen(r_args.f_secret); c++)
935 if (r_args.f_secret[c] == ' ' ||
936 r_args.f_secret[c] == '\n')
937 r_args.f_secret[c] = '\0';
939 if (strlen(r_args.f_secret) < 2) {
940 fprintf(stderr, "Secret in %s is to short\n",
945 r_args.secret = r_args.f_secret;
948 if ((r_args.src_addr = ip_getaddr(optarg)) == 0) {
949 fprintf(stderr, "%s: unknown host %s\n",
961 * No detail file: die.
963 if (argc == optind) {
967 argc -= (optind - 1);
968 argv += (optind - 1);
969 if (shortname && server_name)
971 if (!shortname && !server_name)
973 if (r_args.secret != NULL && shortname != NULL)
977 * If we've been given a shortname, try to fetch the secret and
978 * adress from the config files.
980 if (shortname != NULL) {
981 if (find_shortname(shortname, &server_name, &r_args.secret) == -1) {
982 fprintf(stderr, "Couldn't find %s in configuration files.\n", shortname);
988 * server_name should already be set either by the -r or the -s
989 * commandline argument.
991 if ((p = strrchr(server_name, ':')) != NULL) {
994 r_args.dst_port = ntohs(atoi(p));
996 if (r_args.dst_port == 0) {
997 svp = getservbyname ("radacct", "udp");
998 r_args.dst_port = svp ? ntohs(svp->s_port) : PW_ACCT_UDP_PORT;
1000 r_args.dst_port = ntohs(r_args.dst_port);
1002 r_args.dst_addr = ip_getaddr(server_name);
1003 if (r_args.dst_addr == 0) {
1004 fprintf(stderr, "%s: unknown host\n",
1009 if (r_args.secret == NULL || r_args.secret[0] == 0) {
1010 fprintf(stderr, "No secret available for server %s\n",
1016 * Find what detail file to read from.
1018 * FIXME: We should be able to expand dates etc. based on the pathname,
1019 * just like the detail module does.
1021 if (r_args.detail[0] == '\0') {
1022 if (strlen(RADIR) > 1021) {
1023 fprintf(stderr, "acct_dir to long\n");
1026 strncpy(r_args.detail, RADIR, 1021);
1028 if (chdir(r_args.detail) == -1) {
1033 if (strlen(argv[1]) + strlen(r_args.detail) > 1023) {
1034 fprintf(stderr, "Detail file path to long");
1037 if (r_args.detail[strlen(r_args.detail) - 1] != '/')
1038 r_args.detail[strlen(r_args.detail)] = '/';
1039 strncat (r_args.detail, argv[1], 1023 - strlen(r_args.detail));
1043 * Initialize dictionary.
1045 if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
1046 librad_perror("radrelay");
1051 * Open a socket to the remote server.
1053 if ((r_args.sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
1054 fprintf(stderr, "Error opening socket: %s", strerror(errno));
1058 signal(SIGTERM, sigterm_handler);
1066 (void)open("/dev/null", O_RDWR);
1069 signal(SIGHUP, SIG_IGN);
1070 signal(SIGINT, SIG_IGN);
1071 signal(SIGQUIT, SIG_IGN);
1078 * Initialize the radius id map
1084 * Call main processing loop.