1 /* This file is part of GNU Radius.
2 Copyright (C) 2003,2004,2006,2007,2008 Free Software Foundation, Inc.
3
4 Written by Sergey Poznyakoff
5
6 GNU Radius is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 GNU Radius is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Radius; if not, write to the Free Software Foundation,
18 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
19
20 /* RPP stands for Radius Process Pool */
21
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25 #include <sys/types.h>
26 #include <sys/wait.h>
27 #include <unistd.h>
28 #include <errno.h>
29
30 #include <radiusd.h>
31
32 static int rpp_stdin;
33 static int rpp_stdout;
34
35 /* Process intercommunication primitives */
36
37 enum process_status {
38 process_busy, /* Process is busy handling a request */
39 process_ready, /* Precess is idle and ready for input */
40 process_finished /* Process has finished */
41 };
42
43 typedef struct {
44 pid_t pid; /* PID of the handler process */
45 int p[2]; /* IPC descriptors. p[0] - input, p[1] - output */
46 enum process_status status; /* Current process status */
47 int exit_status; /* Process exit status if status==process_finished */
48 } rpp_proc_t;
49
50
51 /* Low-level calls */
52
53 /* Write into the pipe (represented by FD) SIZE bytes from PTR. TV
54 sets timeout. TV==NULL means no timeout is imposed.
55
56 Returns number of bytes written */
57 static int
pipe_write(int fd,void * ptr,size_t size,struct timeval * tv)58 pipe_write(int fd, void *ptr, size_t size, struct timeval *tv)
59 {
60 errno = 0;
61 if (!tv)
62 return write(fd, ptr, size);
63 else {
64 char *data = ptr;
65 int rc;
66 struct timeval tval, start;
67 fd_set wr_set;
68 size_t n;
69
70 gettimeofday(&start, NULL);
71 for (n = 0; n < size;) {
72
73 FD_ZERO(&wr_set);
74 FD_SET(fd, &wr_set);
75
76 tval = *tv;
77 if (grad_recompute_timeout (&start, &tval)) {
78 errno = ETIMEDOUT;
79 break;
80 }
81
82 rc = select(fd + 1, NULL, &wr_set, NULL, &tval);
83 if (rc == 0) {
84 errno = ETIMEDOUT;
85 GRAD_DEBUG(100, "rc = 0");
86 break;
87 } else if (rc < 0) {
88 if (errno == EINTR)
89 continue;
90 GRAD_DEBUG2(100, "rc = %d, errno = %d",
91 rc, errno);
92 break;
93 } else if (rc > 0) {
94 rc = write(fd, data, 1);
95 if (rc != 1) {
96 GRAD_DEBUG2(100, "rc = %d, errno = %d",
97 rc, errno);
98 break;
99 }
100 data++;
101 n++;
102 }
103 }
104 GRAD_DEBUG1(100,"n = %d",n);
105 return n;
106 }
107 }
108
109 /* Read from the pipe (represented by FD) at most SIZE bytes into PTR. TV
110 sets timeout. TV==NULL means no timeout is imposed.
111
112 Returns number of bytes read */
113 static int
pipe_read(int fd,void * ptr,size_t size,struct timeval * tv)114 pipe_read(int fd, void *ptr, size_t size, struct timeval *tv)
115 {
116 char *data = ptr;
117 int rc;
118
119 errno = 0;
120 if (!tv) {
121 int rdbytes = 0;
122 do {
123 rc = read(fd, data, size);
124 if (rc > 0) {
125 data += rc;
126 size -= rc;
127 rdbytes += rc;
128 } else if (errno != EINTR)
129 break;
130 } while (size > 0);
131 return rdbytes;
132 } else {
133 struct timeval tval, start;
134 fd_set rd_set;
135 size_t n;
136
137 gettimeofday(&start, NULL);
138 for (n = 0; n < size;) {
139
140 FD_ZERO(&rd_set);
141 FD_SET(fd, &rd_set);
142
143 tval = *tv;
144 if (grad_recompute_timeout (&start, &tval)) {
145 errno = ETIMEDOUT;
146 break;
147 }
148
149 rc = select(fd + 1, &rd_set, NULL, NULL, &tval);
150 if (rc == 0) {
151 errno = ETIMEDOUT;
152 break;
153 }
154 if (rc < 0) {
155 if (errno == EINTR)
156 continue;
157 break;
158 } else if (rc > 0) {
159 rc = read(fd, data, 1);
160 if (rc != 1)
161 break;
162 data++;
163 n++;
164 }
165 }
166 return n;
167 }
168 }
169
170
171 /* RPP layer I/O. Each packet transmitted over a pipe is preceeded by
172 its length (a size_t number in host order) */
173
174 /* Read SIZE bytes from the pipe (FD) into DATA. TV sets timeout */
175 static int
rpp_fd_read(int fd,void * data,size_t size,struct timeval * tv)176 rpp_fd_read(int fd, void *data, size_t size, struct timeval *tv)
177 {
178 size_t sz, nbytes = 0;
179
180 sz = pipe_read(fd, &nbytes, sizeof(nbytes), tv);
181 if (sz == 0)
182 return 0; /* eof */
183 GRAD_DEBUG1(100,"nbytes=%lu",nbytes);
184 if (sz != sizeof(nbytes))
185 return -1;
186 sz = nbytes > size ? size : nbytes;
187 if (pipe_read (fd, data, sz, tv) != sz)
188 return -2;
189 for (;nbytes > size; nbytes--) {
190 char c;
191 if (pipe_read(fd, &c, 1, tv) != 1)
192 return -3;
193 }
194
195 GRAD_DEBUG1(100,"return %lu", (unsigned long)sz);
196 return sz;
197 }
198
199 /* Write SIZE bytes from DATA to the pipe FD. TV sets timeout */
200 static int
rpp_fd_write(int fd,void * data,size_t size,struct timeval * tv)201 rpp_fd_write(int fd, void *data, size_t size, struct timeval *tv)
202 {
203 int rc;
204 GRAD_DEBUG1(100,"size=%lu",size);
205 if (pipe_write(fd, &size, sizeof(size), tv) != sizeof(size))
206 return -1;
207 if (pipe_write(fd, data, size, tv) != size)
208 return -2;
209 GRAD_DEBUG1(1,"return %lu", (unsigned long)size);
210 return size;
211 }
212
213
214
215 /* Start a handler process. PROC_MAIN is the handler function (process'
216 main loop). DATA is passed to PROC_MAIN verbatim.
217
218 On success return 0 and fill in PROC structure. */
219 int
rpp_start_process(rpp_proc_t * proc,int (* proc_main)(void *),void * data)220 rpp_start_process(rpp_proc_t *proc, int (*proc_main)(void *), void *data)
221 {
222 int inp[2];
223 int outp[2];
224 pid_t pid;
225
226 if (pipe(inp)) {
227 grad_log(GRAD_LOG_ERR, "pipe(inp): %s", strerror(errno));
228 return -1;
229 }
230
231 if (pipe(outp)) {
232 grad_log (GRAD_LOG_ERR, "pipe(outp): %s", strerror(errno));
233 return -1;
234 }
235
236 pid = fork();
237 if (pid == -1) {
238 grad_log (GRAD_LOG_ERR, "fork: %s", strerror(errno));
239 return -1;
240 }
241 if (pid == 0) {
242 /* Child */
243 /* Close remote side of pipes */
244 close(inp[0]);
245 close(outp[1]);
246 /* Close stdin */
247 close(0);
248 /* Redirect stdout to stderr */
249 dup2(2, 1);
250
251 rpp_stdin = outp[0];
252 rpp_stdout = inp[1];
253
254 /* Run the main process */
255 exit(proc_main(data));
256 }
257
258 /* Parent */
259 close (inp[1]);
260 close (outp[0]);
261
262 proc->pid = pid;
263 proc->p[0] = inp[0];
264 proc->p[1] = outp[1];
265 proc->status = process_ready;
266 return 0;
267 }
268
269
270
271 static grad_list_t *process_list; /* List of rpp_proc_t */
272
273 /* Look up the rpp_proc_t whose input descriptor is FD */
274 rpp_proc_t *
rpp_lookup_fd(int fd)275 rpp_lookup_fd(int fd)
276 {
277 rpp_proc_t *p;
278 grad_iterator_t *itr = grad_iterator_create(process_list);
279 for (p = grad_iterator_first(itr); p; p = grad_iterator_next(itr))
280 if (p->p[0] == fd)
281 break;
282 grad_iterator_destroy(&itr);
283 return p;
284 }
285
286 /* Find an rpp_proc_t ready for handling the reqest. If none is found,
287 start a new one. PROC_MAIN and DATA have the same meaning as in
288 rpp_start_process() */
289 rpp_proc_t *
rpp_lookup_ready(int (* proc_main)(void *),void * data)290 rpp_lookup_ready(int (*proc_main)(void *), void *data)
291 {
292 rpp_proc_t *p;
293
294 if (process_list) {
295 grad_iterator_t *itr = grad_iterator_create(process_list);
296 for (p = grad_iterator_first(itr);
297 p && p->status != process_ready;
298 p = grad_iterator_next(itr))
299 ;
300 grad_iterator_destroy(&itr);
301 } else {
302 process_list = grad_list_create();
303 p = NULL;
304 }
305
306 if (!p) {
307 rpp_proc_t proc;
308 if (grad_list_count(process_list) == max_children)
309 return NULL;
310 if (rpp_start_process(&proc, proc_main, data))
311 return NULL;
312 radiusd_register_input_fd("rpp", proc.p[0], NULL);
313 p = grad_emalloc(sizeof(*p));
314 *p = proc;
315 grad_list_append(process_list, p);
316 }
317 return p;
318 }
319
320 /* Find the rpp_proc_t with a given PID */
321 static int
rpp_comparator(const void * item,const void * data)322 rpp_comparator(const void *item, const void *data)
323 {
324 const rpp_proc_t *p = item;
325 const pid_t *pid = data;
326 return p->pid != *pid;
327 }
328
329 /* NOTE: Do not use any memory allocation calls */
330 rpp_proc_t *
rpp_lookup_pid(pid_t pid)331 rpp_lookup_pid(pid_t pid)
332 {
333 return grad_list_locate(process_list, &pid, rpp_comparator);
334 }
335
336 /* Remove the given rpp_proc_t from the list. The underlying process
337 is supposed to have finished. No diagnostics is output. */
338 static void
_rpp_remove(rpp_proc_t * p)339 _rpp_remove(rpp_proc_t *p)
340 {
341 close(p->p[0]);
342 close(p->p[1]);
343 radiusd_close_channel(p->p[0]);
344 if (grad_list_remove(process_list, p, NULL))
345 grad_free(p);
346 }
347
348 /* Remove the given rpp_proc_t from the list. Output diagnostics about
349 exit status of the handler */
350 void
rpp_remove(rpp_proc_t * p)351 rpp_remove(rpp_proc_t *p)
352 {
353 char buffer[128];
354 format_exit_status(buffer, sizeof buffer, p->exit_status);
355 grad_log(GRAD_LOG_NOTICE, _("child %lu %s"),
356 (unsigned long) p->pid, buffer);
357 _rpp_remove(p);
358 }
359
360 /* Remove the rpp entry with given PID */
361 void
rpp_remove_pid(pid_t pid)362 rpp_remove_pid(pid_t pid)
363 {
364 rpp_proc_t *p = rpp_lookup_pid(pid);
365 if (p)
366 rpp_remove(p);
367 }
368
369 /* Reflect the change of state of the rpp handler process.
370
371 The function is safe to be used from signal handlers */
372 void
rpp_status_changed(pid_t pid,int exit_status)373 rpp_status_changed(pid_t pid, int exit_status)
374 {
375 rpp_proc_t *p = rpp_lookup_pid(pid);
376 if (p) {
377 p->status = process_finished;
378 p->exit_status = exit_status;
379 }
380 }
381
382 /* Collect rpp_proc's whose handler have exited. Free any associated
383 resources */
384 void
rpp_collect_exited()385 rpp_collect_exited()
386 {
387 rpp_proc_t *p;
388 grad_iterator_t *itr = grad_iterator_create(process_list);
389 for (p = grad_iterator_first(itr); p; p = grad_iterator_next(itr)) {
390 if (p->status == process_finished)
391 rpp_remove(p);
392 }
393 grad_iterator_destroy(&itr);
394 }
395
396
397 static int rpp_request_handler(void *arg);
398
399 /* Return 1 if rpp with the given PID is ready for input */
400 int
rpp_ready(pid_t pid)401 rpp_ready(pid_t pid)
402 {
403 if (pid == 0) {
404 if (rpp_lookup_ready(rpp_request_handler, NULL))
405 return 1;
406 } else {
407 rpp_proc_t *p;
408 grad_iterator_t *itr = grad_iterator_create(process_list);
409
410 for (p = grad_iterator_first(itr); p; p = grad_iterator_next(itr)) {
411 if (p->pid == pid) {
412 break;
413 }
414 }
415 grad_iterator_destroy(&itr);
416 if (p && p->status == process_ready)
417 return 1;
418 }
419 return 0;
420 }
421
422 /* Traverse the rpp list until FUN returns non-zero or there are no
423 more busy handlers. While traversing, remove finished handlers. */
424 void
rpp_flush(int (* fun)(void *),void * closure)425 rpp_flush(int (*fun)(void*), void *closure)
426 {
427 time_t t;
428 unsigned count;
429 grad_iterator_t *itr = grad_iterator_create(process_list);
430
431 time(&t);
432
433 do {
434 rpp_proc_t *p;
435 for (count = 0, p = grad_iterator_first(itr);
436 p;
437 p = grad_iterator_next(itr))
438 switch (p->status) {
439 case process_ready:
440 break;
441
442 case process_busy:
443 count++;
444 break;
445
446 case process_finished:
447 rpp_remove (p);
448 }
449 } while (count > 0 && (*fun)(closure) == 0);
450 grad_iterator_destroy(&itr);
451 }
452
453 static int
_kill_itr(void * item,void * data)454 _kill_itr(void *item, void *data)
455 {
456 rpp_proc_t *p = item;
457 kill(p->pid, *(int*)data);
458 return 0;
459 }
460
461 /* Deliver signal SIGNO to the handler with pid PID */
462 int
rpp_kill(pid_t pid,int signo)463 rpp_kill(pid_t pid, int signo)
464 {
465 if (pid > 0) {
466 rpp_proc_t *p = rpp_lookup_pid(pid);
467 if (p) {
468 kill(p->pid, signo);
469 rpp_check_pid(p->pid);
470 } else
471 return 1;
472 } else
473 grad_list_iterate(process_list, _kill_itr, &signo);
474 return 0;
475 }
476
477 pid_t
rpp_check_pid(pid_t pid)478 rpp_check_pid(pid_t pid)
479 {
480 int status;
481 rpp_proc_t *p = rpp_lookup_pid(pid);
482 if (!p)
483 return -1;
484 if (p->status != process_finished) {
485 pid_t npid = waitpid(pid, &status, WNOHANG);
486 if (npid <= 0)
487 return pid;
488 rpp_status_changed(pid, status);
489 }
490 rpp_remove (p);
491 return 0;
492 }
493
494 /* Slay the child */
495 static void
_rpp_slay(rpp_proc_t * p,char * msg)496 _rpp_slay(rpp_proc_t *p, char *msg)
497 {
498 grad_log(GRAD_LOG_NOTICE, _("Killing process %lu: %s"),
499 (u_long) p->pid, msg);
500 kill(p->pid, SIGKILL);
501 _rpp_remove(p);
502 }
503
504 /* Return number of the handlers registered in the process list */
505 size_t
rpp_count()506 rpp_count()
507 {
508 return grad_list_count(process_list);
509 }
510
511
512
513 struct rpp_request {
514 int type; /* Request type */
515 struct sockaddr_in srv_addr; /* Server address */
516 struct sockaddr_in clt_addr; /* Sender address */
517 int fd; /* Source descriptor */
518 size_t size; /* Size of the raw data */
519 /* Raw data follow */
520 };
521
522 #define RPP_COMPLETE 0 /* Completion reply */
523 #define RPP_UPDATE 1 /* Update reply */
524
525 struct rpp_reply {
526 int code;
527 size_t size;
528 /* Data follows */
529 };
530
531 /* Master: Forward a request to the child */
532 int
rpp_forward_request(REQUEST * req)533 rpp_forward_request(REQUEST *req)
534 {
535 rpp_proc_t *p;
536 struct rpp_request frq;
537 struct timeval tv, *tvp;
538
539 if (req->child_id)
540 p = rpp_lookup_pid(req->child_id);
541 else
542 p = rpp_lookup_ready(rpp_request_handler, NULL);
543
544 if (!p)
545 return 1;
546 GRAD_DEBUG1(1, "sending request to %d", p->pid);
547
548 frq.type = req->type;
549 frq.srv_addr = req->srv_addr;
550 frq.clt_addr = req->addr;
551 frq.fd = req->fd;
552 frq.size = req->rawsize;
553
554 p->status = process_busy;
555 req->child_id = p->pid;
556
557 if (radiusd_write_timeout) {
558 tv.tv_sec = radiusd_write_timeout;
559 tv.tv_usec = 0;
560 tvp = &tv;
561 } else
562 tvp = NULL;
563
564 if (rpp_fd_write(p->p[1], &frq, sizeof frq, tvp) != sizeof frq) {
565 _rpp_slay(p, _("error writing header"));
566 return 1;
567 }
568 if (rpp_fd_write(p->p[1], req->rawdata, req->rawsize, tvp) != req->rawsize) {
569 _rpp_slay(p, _("error writing data"));
570 return 1;
571 }
572 return 0;
573 }
574
575 static void
child_cleanup()576 child_cleanup()
577 {
578 pid_t pid;
579 int status;
580
581 for (;;) {
582 pid = waitpid((pid_t)-1, &status, WNOHANG);
583 if (pid <= 0)
584 break;
585 filter_cleanup(pid, status);
586 }
587 }
588
589 static RETSIGTYPE
sig_handler(int sig)590 sig_handler(int sig)
591 {
592 switch (sig) {
593 case SIGHUP:
594 case SIGUSR1:
595 case SIGUSR2:
596 /*Ignored*/
597 break;
598
599 case SIGALRM:
600 grad_log(GRAD_LOG_INFO, _("Child exiting on timeout."));
601 /*FALLTHRU*/
602
603 case SIGTERM:
604 case SIGQUIT:
605 /* FIXME: Possibly unsafe, it calls free() etc. */
606 radiusd_exit0();
607
608 case SIGCHLD:
609 child_cleanup();
610 break;
611
612 case SIGPIPE:
613 /*FIXME: Any special action? */
614 break;
615
616 default:
617 abort();
618 }
619 grad_reset_signal(sig, sig_handler);
620 }
621
622 /* Main loop for a child process */
623 int
rpp_request_handler(void * arg ARG_UNUSED)624 rpp_request_handler(void *arg ARG_UNUSED)
625 {
626 struct rpp_request frq;
627 struct rpp_reply repl;
628 char *data = NULL;
629 size_t datasize = 0;
630 REQUEST *req;
631
632 radiusd_signal_init(sig_handler);
633 grad_set_signal(SIGALRM, sig_handler);
634 request_init_queue();
635
636 while (1) {
637 int rc;
638 int len;
639
640 alarm(process_timeout);
641 len = rpp_fd_read(rpp_stdin, &frq, sizeof frq, NULL);
642 alarm(0);
643 if (len != sizeof frq) {
644 if (errno == EINTR)
645 continue;
646 grad_log(GRAD_LOG_ERR,
647 _("Child received malformed header (len = %d, error = %s)"),
648 len, strerror(errno));
649 radiusd_exit0();
650 }
651
652 if (datasize < frq.size) {
653 datasize = frq.size;
654 data = grad_erealloc(data, datasize);
655 }
656
657 if (rpp_fd_read(rpp_stdin, data, frq.size, NULL) != frq.size) {
658 grad_log(GRAD_LOG_ERR,
659 _("Child received malformed data"));
660 radiusd_exit0();
661 }
662
663 req = request_create(frq.type, frq.fd,
664 &frq.srv_addr, &frq.clt_addr,
665 data, frq.size);
666
667 req->status = RS_COMPLETED;
668 rc = request_handle(req, request_respond);
669
670 /* Inform the master */
671 GRAD_DEBUG(1, "notifying the master");
672 repl.code = RPP_COMPLETE;
673 repl.size = 0;
674 rpp_fd_write(rpp_stdout, &repl, sizeof repl, NULL);
675 if (rc)
676 request_free(req);
677 }
678 return 0;
679 }
680
681 /* Master: Read notification from the child and update the request queue */
682 int
rpp_input_handler(int fd,void * data)683 rpp_input_handler(int fd, void *data)
684 {
685 struct rpp_reply repl;
686 rpp_proc_t *p = rpp_lookup_fd(fd);
687 struct timeval tv, *tvp;
688 int sz;
689
690 grad_insist(p != NULL);
691
692 if (radiusd_read_timeout) {
693 tv.tv_sec = radiusd_read_timeout;
694 tv.tv_usec = 0;
695 tvp = &tv;
696 } else
697 tvp = NULL;
698
699 sz = rpp_fd_read(fd, &repl, sizeof(repl), tvp);
700 if (sz == sizeof(repl)) {
701 void *data = NULL;
702
703 if (repl.size) {
704 data = grad_emalloc(repl.size);
705 if (rpp_fd_read(fd, data, repl.size, tvp)
706 != repl.size) {
707 _rpp_slay(p, _("error reading data"));
708 grad_free(data);
709 return 1;
710 }
711 }
712
713 if (p) {
714 GRAD_DEBUG1(1, "updating pid %d", p->pid);
715 p->status = process_ready;
716 request_update(p->pid, RS_COMPLETED, data);
717 }
718 grad_free(data);
719 } else if (sz != 0) {
720 _rpp_slay(p, _("error reading data; wrong data size returned"));
721 return 1;
722 }
723
724 return 0;
725 }
726
727 /* Client: Send an update to the master */
728 int
rpp_update(void * data,size_t size)729 rpp_update(void *data, size_t size)
730 {
731 struct rpp_reply repl;
732
733 repl.code = RPP_UPDATE;
734 repl.size = size;
735 rpp_fd_write(rpp_stdout, &repl, sizeof repl, NULL);
736 rpp_fd_write(rpp_stdout, data, size, NULL);
737 return 0;
738 }
739
740 int
rpp_input_close(int fd,void * data)741 rpp_input_close(int fd, void *data)
742 {
743 rpp_proc_t *p = rpp_lookup_fd(fd);
744 if (p)
745 _rpp_remove(p);
746 return 0;
747 }
748
749
750
751 /* Don't use it. It's a debugging hook */
752 int
wd()753 wd()
754 {
755 int volatile _st=0;
756 while (!_st)
757 _st=_st;
758 }
759