New command sockets.
[freeradius.git] / src / main / command.c
1 /*
2  * command.c    Command socket processing.
3  *
4  * Version:     $Id$
5  *
6  *   This program 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 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program 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 this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2008 The FreeRADIUS server project
21  * Copyright 2008 Alan DeKok <aland@deployingradius.com>
22  */
23
24 #ifdef WITH_COMMAND_SOCKET
25
26 #include <freeradius-devel/modpriv.h>
27 /*
28  *      FIXME: configure checks.
29  */
30 #include <sys/un.h>
31
32 typedef struct fr_command_table_t fr_command_table_t;
33
34 typedef int (*fr_command_func_t)(rad_listen_t *, int, char *argv[]);
35
36 struct fr_command_table_t {
37         const char *command;
38         const char *help;
39         fr_command_func_t func;
40         fr_command_table_t *table;
41 };
42
43 #define COMMAND_BUFFER_SIZE (1024)
44
45 typedef struct fr_command_socket_t {
46         char    *path;
47         char user[256];
48         ssize_t offset;
49         ssize_t next;
50         char buffer[COMMAND_BUFFER_SIZE];
51 } fr_command_socket_t;
52
53
54 static ssize_t cprintf(rad_listen_t *listener, const char *fmt, ...)
55 #ifdef __GNUC__
56                 __attribute__ ((format (printf, 2, 3)))
57 #endif
58 ;
59
60
61 static int fr_server_domain_socket(const char *path)
62 {
63         int sockfd;
64         size_t len;
65         socklen_t socklen;
66         struct sockaddr_un salocal;
67
68         len = strlen(path);
69         if (len >= sizeof(salocal.sun_path)) {
70                 fprintf(stderr, "Path too long in filename\n");
71                 return -1;
72         }
73
74         if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
75                 fprintf(stderr, "Failed creating socket: %s\n",
76                         strerror(errno));
77                 return -1;
78         }
79
80         salocal.sun_family = AF_UNIX;
81         memcpy(salocal.sun_path, path, len); /* not zero terminated */
82         
83         socklen = sizeof(salocal.sun_family) + len;
84
85         /*
86          *      FIXME: stat it, first, to see who owns it,
87          *      and who owns the directory above it.
88          */
89         if (unlink(path) < 0) {
90                 fprintf(stderr, "Failed to delete %s: %s\n",
91                         path, strerror(errno));
92         }
93
94         if (bind(sockfd, (struct sockaddr *)&salocal, socklen) < 0) {
95                 fprintf(stderr, "Failed binding to %s: %s\n",
96                         path, strerror(errno));
97                 close(sockfd);
98                 return -1;
99         }
100
101         if (listen(sockfd, 8) < 0) {
102                 fprintf(stderr, "Failed listening to %s: %s\n",
103                         path, strerror(errno));
104                 close(sockfd);
105                 return -1;
106         }
107
108 #ifdef O_NONBLOCK
109         {
110                 int flags;
111                 
112                 if ((flags = fcntl(sockfd, F_GETFL, NULL)) < 0)  {
113                         fprintf(stderr, "Failure getting socket flags: %s",
114                                 strerror(errno));
115                         close(sockfd);
116                         return -1;
117                 }
118                 
119                 flags |= O_NONBLOCK;
120                 if( fcntl(sockfd, F_SETFL, flags) < 0) {
121                         fprintf(stderr, "Failure setting socket flags: %s",
122                                 strerror(errno));
123                         close(sockfd);
124                         return -1;
125                 }
126         }
127 #endif
128
129         return sockfd;
130 }
131
132
133 static ssize_t cprintf(rad_listen_t *listener, const char *fmt, ...)
134 {
135         ssize_t len;
136         va_list ap;
137         char buffer[256];
138
139         va_start(ap, fmt);
140         len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
141         va_end(ap);
142
143         if (listener->status == RAD_LISTEN_STATUS_CLOSED) return 0;
144
145         len = write(listener->fd, buffer, len);
146         if (len < 0) {
147                 listener->status = RAD_LISTEN_STATUS_CLOSED;
148                 event_new_fd(listener);
149         }
150
151         /*
152          *      FIXME: Keep writing until done?
153          */
154         return len;
155 }
156
157 static int command_hup(rad_listen_t *listener, int argc, char *argv[])
158 {
159         CONF_SECTION *cs;
160         module_instance_t *mi;
161
162         if (argc == 0) {
163                 radius_signal_self(RADIUS_SIGNAL_SELF_HUP);
164                 return 1;
165         }
166
167         cs = cf_section_find("modules");
168         if (!cs) return 0;
169
170         mi = find_module_instance(cs, argv[0], 0);
171         if (!mi) {
172                 cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
173                 return 0;
174         }
175
176         if (!module_hup_module(mi->cs, mi, time(NULL))) {
177                 cprintf(listener, "ERROR: Failed to reload module\n");
178                 return 0;
179         }
180
181         return 1;               /* success */
182 }
183
184 static int command_terminate(UNUSED rad_listen_t *listener,
185                              UNUSED int argc, UNUSED char *argv[])
186 {
187         radius_signal_self(RADIUS_SIGNAL_SELF_TERM);
188
189         return 1;               /* success */
190 }
191
192 extern time_t fr_start_time;
193
194 static int command_uptime(rad_listen_t *listener,
195                           UNUSED int argc, UNUSED char *argv[])
196 {
197         char buffer[128];
198
199         CTIME_R(&fr_start_time, buffer, sizeof(buffer));
200         cprintf(listener, "Up since %s", buffer); /* no \r\n */
201
202         return 1;               /* success */
203 }
204
205 static int command_show_config(UNUSED rad_listen_t *listener,
206                                UNUSED int argc, UNUSED char *argv[])
207 {
208
209         return 1;               /* success */
210 }
211
212 static const char *tabs = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
213
214 /*
215  *      FIXME: Recurse && indent?
216  */
217 static void cprint_conf_parser(rad_listen_t *listener, int indent, CONF_SECTION *cs,
218                                const void *base)
219                                
220 {
221         int i;
222         const void *data;
223         const char *name1 = cf_section_name1(cs);
224         const char *name2 = cf_section_name2(cs);
225         const CONF_PARSER *variables = cf_section_parse_table(cs);
226
227         if (name2) {
228                 cprintf(listener, "%.*s%s %s {\n", indent, tabs, name1, name2);
229         } else {
230                 cprintf(listener, "%.*s%s {\n", indent, tabs, name1);
231         }
232
233         indent++;
234         
235         /*
236          *      Print
237          */
238         if (variables) for (i = 0; variables[i].name != NULL; i++) {
239                 /*
240                  *      No base struct offset, data must be the pointer.
241                  *      If data doesn't exist, ignore the entry, there
242                  *      must be something wrong.
243                  */
244                 if (!base) {
245                         if (!variables[i].data) {
246                                 continue;
247                         }
248                         
249                         data = variables[i].data;;
250                         
251                 } else if (variables[i].data) {
252                         data = variables[i].data;;
253                         
254                 } else {
255                         data = (((char *)base) + variables[i].offset);
256                 }
257                 
258                 switch (variables[i].type) {
259                 default:
260                         cprintf(listener, "%.*s%s = ?\n", indent, tabs,
261                                 variables[i].name);
262                         break;
263                         
264                 case PW_TYPE_INTEGER:
265                         cprintf(listener, "%.*s%s = %u\n", indent, tabs,
266                                 variables[i].name, *(int *) data);
267                         break;
268                         
269                 case PW_TYPE_BOOLEAN:
270                         cprintf(listener, "%.*s%s = %s\n", indent, tabs,
271                                 variables[i].name, 
272                                 ((*(int *) data) == 0) ? "no" : "yes");
273                         break;
274                         
275                 case PW_TYPE_STRING_PTR:
276                 case PW_TYPE_FILENAME:
277                         /*
278                          *      FIXME: Escape things in the string!
279                          */
280                         if (*(char **) data) {
281                                 cprintf(listener, "%.*s%s = \"%s\"\n", indent, tabs,
282                                         variables[i].name, *(char **) data);
283                         } else {
284                                 cprintf(listener, "%.*s%s = \n", indent, tabs,
285                                         variables[i].name);
286                         }
287                                 
288                         break;
289                 }
290         }
291
292         indent--;
293
294         cprintf(listener, "%.*s}\n", indent, tabs);
295 }
296
297 static int command_show_module_config(rad_listen_t *listener, int argc, char *argv[])
298 {
299         CONF_SECTION *cs;
300         module_instance_t *mi;
301
302         if (argc != 1) {
303                 cprintf(listener, "ERROR: No module name was given\n");
304                 return 0;
305         }
306
307         cs = cf_section_find("modules");
308         if (!cs) return 0;
309
310         mi = find_module_instance(cs, argv[0], 0);
311         if (!mi) {
312                 cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
313                 return 0;
314         }
315
316         cprint_conf_parser(listener, 0, mi->cs, mi->insthandle);
317
318         return 1;               /* success */
319 }
320
321 static const char *method_names[RLM_COMPONENT_COUNT] = {
322         "authenticate",
323         "authorize",
324         "preacct",
325         "accounting",
326         "session",
327         "pre-proxy",
328         "post-proxy",
329         "post-auth"
330 };
331
332
333 static int command_show_module_methods(rad_listen_t *listener, int argc, char *argv[])
334 {
335         int i;
336         CONF_SECTION *cs;
337         const module_instance_t *mi;
338         const module_t *mod;
339
340         if (argc != 1) {
341                 cprintf(listener, "ERROR: No module name was given\n");
342                 return 0;
343         }
344
345         cs = cf_section_find("modules");
346         if (!cs) return 0;
347
348         mi = find_module_instance(cs, argv[0], 0);
349         if (!mi) {
350                 cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
351                 return 0;
352         }
353
354         mod = mi->entry->module;
355
356         for (i = 0; i < RLM_COMPONENT_COUNT; i++) {
357                 if (mod->methods[i]) cprintf(listener, "\t%s\n", method_names[i]);
358         }
359
360         return 1;               /* success */
361 }
362
363
364 static int command_show_module_flags(rad_listen_t *listener, int argc, char *argv[])
365 {
366         CONF_SECTION *cs;
367         const module_instance_t *mi;
368         const module_t *mod;
369
370         if (argc != 1) {
371                 cprintf(listener, "ERROR: No module name was given\n");
372                 return 0;
373         }
374
375         cs = cf_section_find("modules");
376         if (!cs) return 0;
377
378         mi = find_module_instance(cs, argv[0], 0);
379         if (!mi) {
380                 cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
381                 return 0;
382         }
383
384         mod = mi->entry->module;
385
386         if ((mod->type & RLM_TYPE_THREAD_SAFE) != 0)
387                 cprintf(listener, "\tthread-safe\n");
388
389
390         if ((mod->type & RLM_TYPE_CHECK_CONFIG_SAFE) != 0)
391                 cprintf(listener, "\twill-check-config\n");
392
393
394         if ((mod->type & RLM_TYPE_HUP_SAFE) != 0)
395                 cprintf(listener, "\treload-on-hup\n");
396
397         return 1;               /* success */
398 }
399
400
401 /*
402  *      Show all loaded modules
403  */
404 static int command_show_modules(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[])
405 {
406         CONF_SECTION *cs, *subcs;
407
408         cs = cf_section_find("modules");
409         if (!cs) return 0;
410
411         subcs = NULL;
412         while ((subcs = cf_subsection_find_next(cs, subcs, NULL)) != NULL) {
413                 const char *name1 = cf_section_name1(subcs);
414                 const char *name2 = cf_section_name2(subcs);
415
416                 module_instance_t *mi;
417
418                 if (name2) {
419                         mi = find_module_instance(cs, name2, 0);
420                         if (!mi) continue;
421
422                         cprintf(listener, "\t%s (%s)\n", name2, name1);
423                 } else {
424                         mi = find_module_instance(cs, name1, 0);
425                         if (!mi) continue;
426
427                         cprintf(listener, "\t%s\n", name1);
428                 }
429         }
430
431         return 1;               /* success */
432 }
433
434
435 static fr_command_table_t command_table_show_module[] = {
436         { "config",
437           "show module config <module> - show configuration for <module>",
438           command_show_module_config, NULL },
439         { "methods",
440           "show module methods <module> - show sections where <module> may be used",
441           command_show_module_methods, NULL },
442         { "flags",
443           "show module flags <module> - show other module properties",
444           command_show_module_flags, NULL },
445
446         { NULL, NULL, NULL, NULL }
447 };
448
449
450 static fr_command_table_t command_table_show[] = {
451         { "config",
452           "show config - show configuration stuff",
453           command_show_config, NULL },
454         { "module",
455           "show module <command> - do sub-command of module",
456           NULL, command_table_show_module },
457         { "modules",
458           "show modules - shows list of loaded modules",
459           command_show_modules, NULL },
460         { "uptime",
461           "show uptime - shows time at which server started",
462           command_uptime, NULL },
463
464         { NULL, NULL, NULL, NULL }
465 };
466
467
468 static int command_set_module_config(rad_listen_t *listener, int argc, char *argv[])
469 {
470         int i, rcode;
471         CONF_PAIR *cp;
472         CONF_SECTION *cs;
473         module_instance_t *mi;
474         const CONF_PARSER *variables;
475         void *data;
476
477         if (argc < 3) {
478                 cprintf(listener, "ERROR: No module name or variable was given\n");
479                 return 0;
480         }
481
482         cs = cf_section_find("modules");
483         if (!cs) return 0;
484
485         mi = find_module_instance(cs, argv[0], 0);
486         if (!mi) {
487                 cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
488                 return 0;
489         }
490
491         variables = cf_section_parse_table(mi->cs);
492         if (!variables) {
493                 cprintf(listener, "ERROR: Cannot find configuration for module\n");
494                 return 0;
495         }
496
497         rcode = -1;
498         for (i = 0; variables[i].name != NULL; i++) {
499                 /*
500                  *      FIXME: Recurse into sub-types somehow...
501                  */
502                 if (variables[i].type == PW_TYPE_SUBSECTION) continue;
503
504                 if (strcmp(variables[i].name, argv[1]) == 0) {
505                         rcode = i;
506                         break;
507                 }
508         }
509
510         if (rcode < 0) {
511                 cprintf(listener, "ERROR: No such variable \"%s\"\n", argv[1]);
512                 return 0;
513         }
514
515         i = rcode;              /* just to be safe */
516
517         /*
518          *      It's not part of the dynamic configuration.  The module
519          *      needs to re-parse && validate things.
520          */
521         if (variables[i].data) {
522                 cprintf(listener, "ERROR: Variable cannot be dynamically updated\n");
523                 return 0;
524         }
525
526         data = ((char *) mi->insthandle) + variables[i].offset;
527
528         cp = cf_pair_find(mi->cs, argv[1]);
529         if (!cp) return 0;
530
531         /*
532          *      Replace the OLD value in the configuration file with
533          *      the NEW value.
534          *
535          *      FIXME: Parse argv[2] depending on it's data type!
536          *      If it's a string, look for leading single/double quotes,
537          *      end then call tokenize functions???
538          */
539 #if 0
540         cf_pair_replace(mi->cs, cp, argv[2]);
541
542         rcode = cf_item_parse(mi->cs, argv[1], variables[i].type,
543                               data, argv[2]);
544         if (rcode < 0) {
545                 cprintf(listener, "ERROR: Failed to parse value\n");
546                 return 0;
547         }
548 #endif
549
550         return 1;               /* success */
551 }
552
553
554 static fr_command_table_t command_table_set_module[] = {
555         { "config",
556           "set module config <module> variable value - set configuration for <module>",
557           command_set_module_config, NULL },
558
559         { NULL, NULL, NULL, NULL }
560 };
561
562
563 static fr_command_table_t command_table_set[] = {
564         { "module", NULL, NULL, command_table_set_module },
565
566         { NULL, NULL, NULL, NULL }
567 };
568
569
570 static fr_command_table_t command_table[] = {
571         { "hup",
572           "hup [module] - sends a HUP signal to the server, or optionally to one module",
573           command_hup, NULL },
574         { "terminate",
575           "terminate - terminates the server, and causes it to exit",
576           command_terminate, NULL },
577         { "show", NULL, NULL, command_table_show },
578         { "set", NULL, NULL, command_table_set },
579
580         { NULL, NULL, NULL, NULL }
581 };
582
583
584 /*
585  *      FIXME: Unix domain sockets!
586  */
587 static int command_socket_parse(UNUSED CONF_SECTION *cs, rad_listen_t *this)
588 {
589         int rcode;
590         fr_command_socket_t *sock;
591
592         sock = this->data;
593
594         rcode = cf_item_parse(cs, "socket", PW_TYPE_STRING_PTR,
595                               &sock->path, "${run_dir}/radiusd.sock");
596         if (rcode < 0) return -1;
597
598         /*
599          *      FIXME: check for absolute pathnames?
600          *      check for uid/gid on the other end...    
601          */
602
603         this->fd = fr_server_domain_socket(sock->path);
604         if (this->fd < 0) {
605                 return -1;
606         }
607
608         return 0;
609 }
610
611 static int command_socket_print(rad_listen_t *this, char *buffer, size_t bufsize)
612 {
613         fr_command_socket_t *sock = this->data;
614
615         snprintf(buffer, bufsize, "command file %s", sock->path);
616         return 1;
617 }
618
619
620 /*
621  *      String split routine.  Splits an input string IN PLACE
622  *      into pieces, based on spaces.
623  */
624 static int str2argv(char *str, char **argv, int max_argc)
625 {
626         int argc = 0;
627
628         while (*str) {
629                 if (argc >= max_argc) return argc;
630
631                 /*
632                  *      Chop out comments early.
633                  */
634                 if (*str == '#') {
635                         *str = '\0';
636                         break;
637                 }
638
639                 while ((*str == ' ') ||
640                        (*str == '\t') ||
641                        (*str == '\r') ||
642                        (*str == '\n')) *(str++) = '\0';
643
644                 if (!*str) return argc;
645
646                 argv[argc] = str;
647                 argc++;
648
649                 while (*str &&
650                        (*str != ' ') &&
651                        (*str != '\t') &&
652                        (*str != '\r') &&
653                        (*str != '\n')) str++;
654         }
655
656         return argc;
657 }
658
659 #define MAX_ARGV (16)
660
661 /*
662  *      Check if an incoming request is "ok"
663  *
664  *      It takes packets, not requests.  It sees if the packet looks
665  *      OK.  If so, it does a number of sanity checks on it.
666  */
667 static int command_domain_recv(rad_listen_t *listener,
668                                UNUSED RAD_REQUEST_FUNP *pfun,
669                                UNUSED REQUEST **prequest)
670 {
671         int i, rcode;
672         ssize_t len;
673         int argc;
674         char *my_argv[MAX_ARGV], **argv;
675         fr_command_table_t *table;
676         fr_command_socket_t *co = listener->data;
677
678         do {
679                 ssize_t c;
680                 char *p;
681
682                 len = recv(listener->fd, co->buffer + co->offset,
683                            sizeof(co->buffer) - co->offset - 1, 0);
684                 if (len == 0) goto close_socket; /* clean close */
685
686                 if (len < 0) {
687                         if ((errno == EAGAIN) || (errno == EINTR)) {
688                                 return 0;
689                         }
690                         goto close_socket;
691                 }
692
693                 /*
694                  *      CTRL-D
695                  */
696                 if ((co->offset == 0) && (co->buffer[0] == 0x04)) {
697                 close_socket:
698                         listener->status = RAD_LISTEN_STATUS_CLOSED;
699                         event_new_fd(listener);
700                         return 0;
701                 }
702
703                 /*
704                  *      See if there are multiple lines in the buffer.
705                  */
706                 p = co->buffer + co->offset;
707                 rcode = 0;
708                 p[len] = '\0';
709                 for (c = 0; c < len; c++) {
710                         if ((*p == '\r') || (*p == '\n')) {
711                                 rcode = 1;
712                                 *p = '\0';
713
714                                 /*
715                                  *      FIXME: do real buffering...
716                                  *      handling of CTRL-C, etc.
717                                  */
718
719                         } else if (rcode) {
720                                 /*
721                                  *      \r \n followed by ASCII...
722                                  */
723                                 break;
724                         }
725
726                         p++;
727                 }
728
729                 co->offset += len;
730
731                 /*
732                  *      Saw CR/LF.  Set next element, and exit.
733                  */
734                 if (rcode) {
735                         co->next = p - co->buffer;
736                         break;
737                 }
738
739                 if (co->offset >= (ssize_t) (sizeof(co->buffer) - 1)) {
740                         radlog(L_ERR, "Line too long!");
741                         goto close_socket;
742                 }
743
744                 co->offset++;
745         } while (1);
746
747         argc = str2argv(co->buffer, my_argv, MAX_ARGV);
748         if (argc == 0) goto do_next;
749         argv = my_argv;
750
751         for (len = 0; len <= co->offset; len++) {
752                 if (co->buffer[len] < 0x20) {
753                         co->buffer[len] = '\0';
754                         break;
755                 }
756         }
757
758         /*
759          *      Hard-code exit && quit.
760          */
761         if ((strcmp(argv[0], "exit") == 0) ||
762             (strcmp(argv[0], "quit") == 0)) goto close_socket;
763
764 #if 0
765         if (!co->user[0]) {
766                 if (strcmp(argv[0], "login") != 0) {
767                         cprintf(listener, "ERROR: Login required\n");
768                         goto do_next;
769                 }
770
771                 if (argc < 3) {
772                         cprintf(listener, "ERROR: login <user> <password>\n");
773                         goto do_next;
774                 }
775
776                 /*
777                  *      FIXME: Generate && process fake RADIUS request.
778                  */
779                 if ((strcmp(argv[1], "root") == 0) &&
780                     (strcmp(argv[2], "password") == 0)) {
781                         strlcpy(co->user, argv[1], sizeof(co->user));
782                         goto do_next;
783                 }
784
785                 cprintf(listener, "ERROR: Login incorrect\n");
786                 goto do_next;
787         }
788 #endif
789
790         table = command_table;
791  retry:
792         len = 0;
793         for (i = 0; table[i].command != NULL; i++) {
794                 if (strcmp(table[i].command, argv[0]) == 0) {
795                         if (table[i].table) {
796                                 /*
797                                  *      This is the last argument, but
798                                  *      there's a sub-table.  Print help.
799                                  *      
800                                  */
801                                 if (argc == 1) {
802                                         table = table[i].table;
803                                         goto do_help;
804                                 }
805
806                                 argc--;
807                                 argv++;
808                                 table = table[i].table;
809                                 goto retry;
810                         }
811
812                         len = 1;
813                         rcode = table[i].func(listener,
814                                               argc - 1, argv + 1);
815                         break;
816                 }
817         }
818
819         /*
820          *      No such command
821          */
822         if (!len) {
823                 if (strcmp(argv[0], "help") == 0) {
824                 do_help:
825                         for (i = 0; table[i].command != NULL; i++) {
826                                 if (table[i].help) {
827                                         cprintf(listener, "%s\n",
828                                                 table[i].help);
829                                 } else {
830                                         cprintf(listener, "%s <command> - do sub-command of %s\n",
831                                                 table[i].command, table[i].command);
832                                 }
833                         }
834                         goto do_next;
835                 }
836
837                 cprintf(listener, "ERROR: Unknown command \"%s\"\r\n",
838                         argv[0]);
839         }
840
841  do_next:
842         cprintf(listener, "radmin> ");
843
844         if (co->next <= co->offset) {
845                 co->offset = 0;
846         } else {
847                 memmove(co->buffer, co->buffer + co->next,
848                         co->offset - co->next);
849                 co->offset -= co->next;
850         }
851
852         return 0;
853 }
854
855
856 static int command_domain_accept(rad_listen_t *listener,
857                                  UNUSED RAD_REQUEST_FUNP *pfun,
858                                  UNUSED REQUEST **prequest)
859 {
860         int newfd;
861         rad_listen_t *this;
862         socklen_t salen;
863         struct sockaddr_storage src;
864         listen_socket_t *sock;
865         fr_command_socket_t *co;
866         
867         salen = sizeof(src);
868
869         DEBUG2(" ... new connection request on command socket.");
870         
871         newfd = accept(listener->fd, (struct sockaddr *) &src, &salen);
872         if (newfd < 0) {
873                 /*
874                  *      Non-blocking sockets must handle this.
875                  */
876                 if (errno == EWOULDBLOCK) {
877                         return 0;
878                 }
879
880                 DEBUG2(" ... failed to accept connection.");
881                 return -1;
882         }
883
884 #if 0
885 struct ucred cr;
886  int cl=sizeof(cr);
887
888  if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl)==0) {
889    printf("Peer's pid=%d, uid=%d, gid=%d\n",
890            cr.pid, cr.uid, cr.gid);
891 #endif
892
893         /*
894          *      Add the new listener.
895          */
896         this = listen_alloc(listener->type);
897         if (!this) return -1;
898
899         /*
900          *      Copy everything, including the pointer to the socket
901          *      information.
902          */
903         free(this->data);
904         this->data = rad_malloc(sizeof(fr_command_socket_t));
905         sock = this->data;
906         memcpy(this->data, listener->data, sizeof(*sock));
907         memcpy(this, listener, sizeof(*this));
908         this->next = NULL;
909         this->data = sock;      /* fix it back */
910
911         co = this->data;
912         co->offset = 0;
913         co->user[0] = '\0';
914
915         this->fd = newfd;
916         this->recv = command_domain_recv;
917
918         /*
919          *      FIXME: set O_NONBLOCK on the accept'd fd.
920          *      See djb's portability rants for details.
921          */
922
923         /*
924          *      Tell the event loop that we have a new FD
925          */
926         event_new_fd(this);
927
928         return 0;
929 }
930
931
932 /*
933  *      Send an authentication response packet
934  */
935 static int command_domain_send(UNUSED rad_listen_t *listener,
936                                UNUSED REQUEST *request)
937 {
938         return 0;
939 }
940
941
942 static int command_socket_encode(UNUSED rad_listen_t *listener,
943                                  UNUSED REQUEST *request)
944 {
945         return 0;
946 }
947
948
949 static int command_socket_decode(UNUSED rad_listen_t *listener,
950                                  UNUSED REQUEST *request)
951 {
952         return 0;
953 }
954
955 #endif /* WITH_COMMAND_SOCKET */