Make it clearer about debug log file
[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 #include <freeradius-devel/conffile.h>
28 #
29 #ifdef HAVE_SYS_UN_H
30 #include <sys/un.h>
31 #endif
32
33 #ifdef HAVE_SYS_STAT_H
34 #include <sys/stat.h>
35 #endif
36
37 #ifdef HAVE_PWD_H
38 #include <pwd.h>
39 #endif
40
41 #ifdef HAVE_GRP_H
42 #include <grp.h>
43 #endif
44
45 typedef struct fr_command_table_t fr_command_table_t;
46
47 typedef int (*fr_command_func_t)(rad_listen_t *, int, char *argv[]);
48
49 struct fr_command_table_t {
50         const char *command;
51         const char *help;
52         fr_command_func_t func;
53         fr_command_table_t *table;
54 };
55
56 #define COMMAND_BUFFER_SIZE (1024)
57
58 typedef struct fr_command_socket_t {
59         char    *path;
60         uid_t   uid;
61         gid_t   gid;
62         char    *uid_name;
63         char    *gid_name;
64         char user[256];
65         ssize_t offset;
66         ssize_t next;
67         char buffer[COMMAND_BUFFER_SIZE];
68 } fr_command_socket_t;
69
70 static const CONF_PARSER command_config[] = {
71   { "socket",  PW_TYPE_STRING_PTR,
72     offsetof(fr_command_socket_t, path), NULL, "${run_dir}/radiusd.sock"},
73   { "uid",  PW_TYPE_STRING_PTR,
74     offsetof(fr_command_socket_t, uid_name), NULL, NULL},
75   { "gid",  PW_TYPE_STRING_PTR,
76     offsetof(fr_command_socket_t, gid_name), NULL, NULL},
77
78   { NULL, -1, 0, NULL, NULL }           /* end the list */
79 };
80
81 static ssize_t cprintf(rad_listen_t *listener, const char *fmt, ...)
82 #ifdef __GNUC__
83                 __attribute__ ((format (printf, 2, 3)))
84 #endif
85 ;
86
87 #ifndef HAVE_GETPEEREID
88 static int getpeereid(int s, uid_t *euid, gid_t *egid)
89 {
90 #ifndef SO_PEERCRED
91         return -1;
92 #else
93         struct ucred cr;
94         socklen_t cl = sizeof(cr);
95         
96         if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cl) < 0) {
97                 return -1;
98         }
99
100         *euid = cr.uid;
101         *egid = cr.gid;
102         return 0;
103 #endif /* SO_PEERCRED */
104 }
105 #endif /* HAVE_GETPEEREID */
106
107
108 static int fr_server_domain_socket(const char *path)
109 {
110         int sockfd;
111         size_t len;
112         socklen_t socklen;
113         struct sockaddr_un salocal;
114         struct stat buf;
115
116         len = strlen(path);
117         if (len >= sizeof(salocal.sun_path)) {
118                 radlog(L_ERR, "Path too long in socket filename.");
119                 return -1;
120         }
121
122         if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
123                 radlog(L_ERR, "Failed creating socket: %s",
124                         strerror(errno));
125                 return -1;
126         }
127
128         memset(&salocal, 0, sizeof(salocal));
129         salocal.sun_family = AF_UNIX;
130         memcpy(salocal.sun_path, path, len); /* not zero terminated */
131         
132         socklen = sizeof(salocal.sun_family) + len;
133
134         /*
135          *      Check the path.
136          */
137         if (stat(path, &buf) < 0) {
138                 if (errno != ENOENT) {
139                         radlog(L_ERR, "Failed to stat %s: %s",
140                                path, strerror(errno));
141                         return -1;
142                 }
143
144                 /*
145                  *      FIXME: Check the enclosing directory?
146                  */
147         } else {                /* it exists */
148                 if (!S_ISREG(buf.st_mode)
149 #ifdef S_ISSOCK
150                     && !S_ISSOCK(buf.st_mode)
151 #endif
152                         ) {
153                         radlog(L_ERR, "Cannot turn %s into socket", path);
154                         return -1;                     
155                 }
156
157                 /*
158                  *      Refuse to open sockets not owned by us.
159                  */
160                 if (buf.st_uid != geteuid()) {
161                         radlog(L_ERR, "We do not own %s", path);
162                         return -1;
163                 }
164
165                 if (unlink(path) < 0) {
166                         radlog(L_ERR, "Failed to delete %s: %s",
167                                path, strerror(errno));
168                         return -1;
169                 }
170         }
171
172         if (bind(sockfd, (struct sockaddr *)&salocal, socklen) < 0) {
173                 radlog(L_ERR, "Failed binding to %s: %s",
174                         path, strerror(errno));
175                 close(sockfd);
176                 return -1;
177         }
178
179         /*
180          *      FIXME: There's a race condition here.  But Linux
181          *      doesn't seem to permit fchmod on domain sockets.
182          */
183         if (chmod(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) < 0) {
184                 radlog(L_ERR, "Failed setting permissions on %s: %s",
185                        path, strerror(errno));
186                 close(sockfd);
187                 return -1;
188         }
189
190         if (listen(sockfd, 8) < 0) {
191                 radlog(L_ERR, "Failed listening to %s: %s",
192                         path, strerror(errno));
193                 close(sockfd);
194                 return -1;
195         }
196
197 #ifdef O_NONBLOCK
198         {
199                 int flags;
200                 
201                 if ((flags = fcntl(sockfd, F_GETFL, NULL)) < 0)  {
202                         radlog(L_ERR, "Failure getting socket flags: %s",
203                                 strerror(errno));
204                         close(sockfd);
205                         return -1;
206                 }
207                 
208                 flags |= O_NONBLOCK;
209                 if( fcntl(sockfd, F_SETFL, flags) < 0) {
210                         radlog(L_ERR, "Failure setting socket flags: %s",
211                                 strerror(errno));
212                         close(sockfd);
213                         return -1;
214                 }
215         }
216 #endif
217
218         return sockfd;
219 }
220
221
222 static ssize_t cprintf(rad_listen_t *listener, const char *fmt, ...)
223 {
224         ssize_t len;
225         va_list ap;
226         char buffer[256];
227
228         va_start(ap, fmt);
229         len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
230         va_end(ap);
231
232         if (listener->status == RAD_LISTEN_STATUS_CLOSED) return 0;
233
234         len = write(listener->fd, buffer, len);
235         if (len < 0) {
236                 listener->status = RAD_LISTEN_STATUS_CLOSED;
237                 event_new_fd(listener);
238         }
239
240         /*
241          *      FIXME: Keep writing until done?
242          */
243         return len;
244 }
245
246 static int command_hup(rad_listen_t *listener, int argc, char *argv[])
247 {
248         CONF_SECTION *cs;
249         module_instance_t *mi;
250
251         if (argc == 0) {
252                 radius_signal_self(RADIUS_SIGNAL_SELF_HUP);
253                 return 1;
254         }
255
256         cs = cf_section_find("modules");
257         if (!cs) return 0;
258
259         mi = find_module_instance(cs, argv[0], 0);
260         if (!mi) {
261                 cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
262                 return 0;
263         }
264
265         if ((mi->entry->module->type & RLM_TYPE_HUP_SAFE) == 0) {
266                 cprintf(listener, "ERROR: Module %s cannot be hup'd\n",
267                         argv[0]);
268                 return 0;
269         }
270
271         if (!module_hup_module(mi->cs, mi, time(NULL))) {
272                 cprintf(listener, "ERROR: Failed to reload module\n");
273                 return 0;
274         }
275
276         return 1;               /* success */
277 }
278
279 static int command_terminate(UNUSED rad_listen_t *listener,
280                              UNUSED int argc, UNUSED char *argv[])
281 {
282         radius_signal_self(RADIUS_SIGNAL_SELF_TERM);
283
284         return 1;               /* success */
285 }
286
287 extern time_t fr_start_time;
288
289 static int command_uptime(rad_listen_t *listener,
290                           UNUSED int argc, UNUSED char *argv[])
291 {
292         char buffer[128];
293
294         CTIME_R(&fr_start_time, buffer, sizeof(buffer));
295         cprintf(listener, "Up since %s", buffer); /* no \r\n */
296
297         return 1;               /* success */
298 }
299
300 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";
301
302 /*
303  *      FIXME: Recurse && indent?
304  */
305 static void cprint_conf_parser(rad_listen_t *listener, int indent, CONF_SECTION *cs,
306                                const void *base)
307                                
308 {
309         int i;
310         const void *data;
311         const char *name1 = cf_section_name1(cs);
312         const char *name2 = cf_section_name2(cs);
313         const CONF_PARSER *variables = cf_section_parse_table(cs);
314         char buffer[256];
315
316         if (name2) {
317                 cprintf(listener, "%.*s%s %s {\n", indent, tabs, name1, name2);
318         } else {
319                 cprintf(listener, "%.*s%s {\n", indent, tabs, name1);
320         }
321
322         indent++;
323         
324         /*
325          *      Print
326          */
327         if (variables) for (i = 0; variables[i].name != NULL; i++) {
328                 /*
329                  *      No base struct offset, data must be the pointer.
330                  *      If data doesn't exist, ignore the entry, there
331                  *      must be something wrong.
332                  */
333                 if (!base) {
334                         if (!variables[i].data) {
335                                 continue;
336                         }
337                         
338                         data = variables[i].data;;
339                         
340                 } else if (variables[i].data) {
341                         data = variables[i].data;;
342                         
343                 } else {
344                         data = (((char *)base) + variables[i].offset);
345                 }
346
347                 switch (variables[i].type) {
348                 default:
349                         cprintf(listener, "%.*s%s = ?\n", indent, tabs,
350                                 variables[i].name);
351                         break;
352                         
353                 case PW_TYPE_INTEGER:
354                         cprintf(listener, "%.*s%s = %u\n", indent, tabs,
355                                 variables[i].name, *(int *) data);
356                         break;
357                         
358                 case PW_TYPE_IPADDR:
359                         inet_ntop(AF_INET, data, buffer, sizeof(buffer));
360                         break;
361
362                 case PW_TYPE_IPV6ADDR:
363                         inet_ntop(AF_INET6, data, buffer, sizeof(buffer));
364                         break;
365
366                 case PW_TYPE_BOOLEAN:
367                         cprintf(listener, "%.*s%s = %s\n", indent, tabs,
368                                 variables[i].name, 
369                                 ((*(int *) data) == 0) ? "no" : "yes");
370                         break;
371                         
372                 case PW_TYPE_STRING_PTR:
373                 case PW_TYPE_FILENAME:
374                         /*
375                          *      FIXME: Escape things in the string!
376                          */
377                         if (*(char **) data) {
378                                 cprintf(listener, "%.*s%s = \"%s\"\n", indent, tabs,
379                                         variables[i].name, *(char **) data);
380                         } else {
381                                 cprintf(listener, "%.*s%s = \n", indent, tabs,
382                                         variables[i].name);
383                         }
384                                 
385                         break;
386                 }
387         }
388
389         indent--;
390
391         cprintf(listener, "%.*s}\n", indent, tabs);
392 }
393
394 static int command_show_module_config(rad_listen_t *listener, int argc, char *argv[])
395 {
396         CONF_SECTION *cs;
397         module_instance_t *mi;
398
399         if (argc != 1) {
400                 cprintf(listener, "ERROR: No module name was given\n");
401                 return 0;
402         }
403
404         cs = cf_section_find("modules");
405         if (!cs) return 0;
406
407         mi = find_module_instance(cs, argv[0], 0);
408         if (!mi) {
409                 cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
410                 return 0;
411         }
412
413         cprint_conf_parser(listener, 0, mi->cs, mi->insthandle);
414
415         return 1;               /* success */
416 }
417
418 static const char *method_names[RLM_COMPONENT_COUNT] = {
419         "authenticate",
420         "authorize",
421         "preacct",
422         "accounting",
423         "session",
424         "pre-proxy",
425         "post-proxy",
426         "post-auth"
427 };
428
429
430 static int command_show_module_methods(rad_listen_t *listener, int argc, char *argv[])
431 {
432         int i;
433         CONF_SECTION *cs;
434         const module_instance_t *mi;
435         const module_t *mod;
436
437         if (argc != 1) {
438                 cprintf(listener, "ERROR: No module name was given\n");
439                 return 0;
440         }
441
442         cs = cf_section_find("modules");
443         if (!cs) return 0;
444
445         mi = find_module_instance(cs, argv[0], 0);
446         if (!mi) {
447                 cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
448                 return 0;
449         }
450
451         mod = mi->entry->module;
452
453         for (i = 0; i < RLM_COMPONENT_COUNT; i++) {
454                 if (mod->methods[i]) cprintf(listener, "\t%s\n", method_names[i]);
455         }
456
457         return 1;               /* success */
458 }
459
460
461 static int command_show_module_flags(rad_listen_t *listener, int argc, char *argv[])
462 {
463         CONF_SECTION *cs;
464         const module_instance_t *mi;
465         const module_t *mod;
466
467         if (argc != 1) {
468                 cprintf(listener, "ERROR: No module name was given\n");
469                 return 0;
470         }
471
472         cs = cf_section_find("modules");
473         if (!cs) return 0;
474
475         mi = find_module_instance(cs, argv[0], 0);
476         if (!mi) {
477                 cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
478                 return 0;
479         }
480
481         mod = mi->entry->module;
482
483         if ((mod->type & RLM_TYPE_THREAD_SAFE) != 0)
484                 cprintf(listener, "\tthread-safe\n");
485
486
487         if ((mod->type & RLM_TYPE_CHECK_CONFIG_SAFE) != 0)
488                 cprintf(listener, "\twill-check-config\n");
489
490
491         if ((mod->type & RLM_TYPE_HUP_SAFE) != 0)
492                 cprintf(listener, "\treload-on-hup\n");
493
494         return 1;               /* success */
495 }
496
497
498 /*
499  *      Show all loaded modules
500  */
501 static int command_show_modules(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[])
502 {
503         CONF_SECTION *cs, *subcs;
504
505         cs = cf_section_find("modules");
506         if (!cs) return 0;
507
508         subcs = NULL;
509         while ((subcs = cf_subsection_find_next(cs, subcs, NULL)) != NULL) {
510                 const char *name1 = cf_section_name1(subcs);
511                 const char *name2 = cf_section_name2(subcs);
512
513                 module_instance_t *mi;
514
515                 if (name2) {
516                         mi = find_module_instance(cs, name2, 0);
517                         if (!mi) continue;
518
519                         cprintf(listener, "\t%s (%s)\n", name2, name1);
520                 } else {
521                         mi = find_module_instance(cs, name1, 0);
522                         if (!mi) continue;
523
524                         cprintf(listener, "\t%s\n", name1);
525                 }
526         }
527
528         return 1;               /* success */
529 }
530
531 static int command_show_xml(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[])
532 {
533         CONF_ITEM *ci;
534         FILE *fp = fdopen(dup(listener->fd), "a");
535
536         if (!fp) {
537                 cprintf(listener, "ERROR: Can't dup %s\n", strerror(errno));
538                 return 0;
539         }
540
541         if (argc == 0) {
542                 cprintf(listener, "ERROR: <reference> is required\n");
543                 return 0;
544         }
545         
546         ci = cf_reference_item(mainconfig.config, mainconfig.config, argv[0]);
547         if (!ci) {
548                 cprintf(listener, "ERROR: No such item <reference>\n");
549                 return 0;
550         }
551
552         if (cf_item_is_section(ci)) {
553                 cf_section2xml(fp, cf_itemtosection(ci));
554
555         } else if (cf_item_is_pair(ci)) {
556                 cf_pair2xml(fp, cf_itemtopair(ci));
557
558         } else {
559                 cprintf(listener, "ERROR: No such item <reference>\n");
560                 fclose(fp);
561                 return 0;
562         }
563
564         fclose(fp);
565
566         return 1;               /* success */
567 }
568
569 static int command_debug_level(rad_listen_t *listener, int argc, char *argv[])
570 {
571         int number;
572
573         if (argc == 0) {
574                 cprintf(listener, "ERROR: Must specify <number>\n");
575                 return -1;
576         }
577
578         number = atoi(argv[0]);
579         if ((number < 0) || (number > 4)) {
580                 cprintf(listener, "ERROR: <number> must be between 0 and 4\n");
581                 return -1;
582         }
583
584         fr_debug_flag = debug_flag = number;
585
586         return 0;
587 }
588
589 extern char *debug_log_file;
590 static int command_debug_file(rad_listen_t *listener, int argc, char *argv[])
591 {
592         if (argc == 0) {
593                 cprintf(listener, "ERROR: Must specify <filename>\n");
594                 return -1;
595         }
596
597         if (debug_log_file) {
598                 free(debug_log_file);
599                 debug_log_file = NULL;
600         }
601         debug_log_file = strdup(argv[0]);
602
603         return 0;
604 }
605
606 extern char *debug_condition;
607 static int command_debug_condition(rad_listen_t *listener, int argc, char *argv[])
608 {
609         /*
610          *      Delete old condition.
611          *
612          *      This is thread-safe because the condition is evaluated
613          *      in the main server thread, as is this code.
614          */
615         free(debug_condition);
616         debug_condition = NULL;
617
618         /*
619          *      Disable it.
620          */
621         if (argc == 0) {
622                 return 0;
623         }
624
625         debug_condition = strdup(argv[0]);
626
627         return 0;
628 }
629
630 static fr_command_table_t command_table_debug[] = {
631         { "condition",
632           "debug condition <condition> - Enable debugging for requests matching <condition>",
633           command_debug_condition, NULL },
634
635         { "level",
636           "debug level <number> - Set debug level to <number>.  Higher is more debugging.",
637           command_debug_level, NULL },
638
639         { "file",
640           "debug file <filename> - Send all debuggin output to <filename>",
641           command_debug_file, NULL },
642
643         { NULL, NULL, NULL, NULL }
644 };
645
646 static fr_command_table_t command_table_show_module[] = {
647         { "config",
648           "show module config <module> - show configuration for <module>",
649           command_show_module_config, NULL },
650         { "flags",
651           "show module flags <module> - show other module properties",
652           command_show_module_flags, NULL },
653         { "list",
654           "shows list of loaded modules",
655           command_show_modules, NULL },
656         { "methods",
657           "show module methods <module> - show sections where <module> may be used",
658           command_show_module_methods, NULL },
659
660         { NULL, NULL, NULL, NULL }
661 };
662
663
664 static fr_command_table_t command_table_show[] = {
665         { "config",
666           "show config <module> - show configuration for module",
667           command_show_module_config, NULL },
668         { "module",
669           "show module <command> - do sub-command of module",
670           NULL, command_table_show_module },
671         { "modules",
672           "show modules - shows list of loaded modules",
673           command_show_modules, NULL },
674         { "uptime",
675           "show uptime - shows time at which server started",
676           command_uptime, NULL },
677         { "xml",
678           "show xml <reference> - Prints out configuration as XML",
679           command_show_xml, NULL },
680         { NULL, NULL, NULL, NULL }
681 };
682
683
684 static int command_set_module_config(rad_listen_t *listener, int argc, char *argv[])
685 {
686         int i, rcode;
687         CONF_PAIR *cp;
688         CONF_SECTION *cs;
689         module_instance_t *mi;
690         const CONF_PARSER *variables;
691         void *data;
692
693         if (argc < 3) {
694                 cprintf(listener, "ERROR: No module name or variable was given\n");
695                 return 0;
696         }
697
698         cs = cf_section_find("modules");
699         if (!cs) return 0;
700
701         mi = find_module_instance(cs, argv[0], 0);
702         if (!mi) {
703                 cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
704                 return 0;
705         }
706
707         if ((mi->entry->module->type & RLM_TYPE_HUP_SAFE) == 0) {
708                 cprintf(listener, "ERROR: Cannot change configuration of module as it is cannot be HUP'd.\n");
709                 return 0;
710         }
711
712         variables = cf_section_parse_table(mi->cs);
713         if (!variables) {
714                 cprintf(listener, "ERROR: Cannot find configuration for module\n");
715                 return 0;
716         }
717
718         rcode = -1;
719         for (i = 0; variables[i].name != NULL; i++) {
720                 /*
721                  *      FIXME: Recurse into sub-types somehow...
722                  */
723                 if (variables[i].type == PW_TYPE_SUBSECTION) continue;
724
725                 if (strcmp(variables[i].name, argv[1]) == 0) {
726                         rcode = i;
727                         break;
728                 }
729         }
730
731         if (rcode < 0) {
732                 cprintf(listener, "ERROR: No such variable \"%s\"\n", argv[1]);
733                 return 0;
734         }
735
736         i = rcode;              /* just to be safe */
737
738         /*
739          *      It's not part of the dynamic configuration.  The module
740          *      needs to re-parse && validate things.
741          */
742         if (variables[i].data) {
743                 cprintf(listener, "ERROR: Variable cannot be dynamically updated\n");
744                 return 0;
745         }
746
747         data = ((char *) mi->insthandle) + variables[i].offset;
748
749         cp = cf_pair_find(mi->cs, argv[1]);
750         if (!cp) return 0;
751
752         /*
753          *      Replace the OLD value in the configuration file with
754          *      the NEW value.
755          *
756          *      FIXME: Parse argv[2] depending on it's data type!
757          *      If it's a string, look for leading single/double quotes,
758          *      end then call tokenize functions???
759          */
760         cf_pair_replace(mi->cs, cp, argv[2]);
761
762         rcode = cf_item_parse(mi->cs, argv[1], variables[i].type,
763                               data, argv[2]);
764         if (rcode < 0) {
765                 cprintf(listener, "ERROR: Failed to parse value\n");
766                 return 0;
767         }
768
769         return 1;               /* success */
770 }
771
772
773 static fr_command_table_t command_table_set_module[] = {
774         { "config",
775           "set module config <module> variable value - set configuration for <module>",
776           command_set_module_config, NULL },
777
778         { NULL, NULL, NULL, NULL }
779 };
780
781
782 static fr_command_table_t command_table_set[] = {
783         { "module", NULL, NULL, command_table_set_module },
784
785         { NULL, NULL, NULL, NULL }
786 };
787
788
789 static fr_command_table_t command_table[] = {
790         { "debug",
791           "debug <command> - debugging commands",
792           NULL, command_table_debug },
793         { "hup",
794           "hup [module] - sends a HUP signal to the server, or optionally to one module",
795           command_hup, NULL },
796         { "terminate",
797           "terminate - terminates the server, and causes it to exit",
798           command_terminate, NULL },
799         { "show", NULL, NULL, command_table_show },
800         { "set", NULL, NULL, command_table_set },
801
802         { NULL, NULL, NULL, NULL }
803 };
804
805
806 /*
807  *      FIXME: Unix domain sockets!
808  */
809 static int command_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
810 {
811         fr_command_socket_t *sock;
812
813         sock = this->data;
814
815         if (cf_section_parse(cs, sock, command_config) < 0) {
816                 return -1;
817         }
818
819 #if defined(HAVE_GETPEEREID) || defined (SO_PEERCRED)
820         if (sock->uid_name) {
821                 struct passwd *pw;
822                 
823                 pw = getpwnam(sock->uid_name);
824                 if (!pw) {
825                         radlog(L_ERR, "Failed getting uid for %s: %s",
826                                sock->uid_name, strerror(errno));
827                         return -1;
828                 }
829
830                 sock->uid = pw->pw_uid;
831         }
832
833         if (sock->gid_name) {
834                 struct group *gr;
835
836                 gr = getgrnam(sock->gid_name);
837                 if (!gr) {
838                         radlog(L_ERR, "Failed getting gid for %s: %s",
839                                sock->gid_name, strerror(errno));
840                         return -1;
841                 }
842                 sock->gid = gr->gr_gid; 
843         }
844
845 #else  /* can't get uid or gid of connecting user */
846
847         if (sock->uid_name || sock->gid_name) {
848                 radlog(L_ERR, "System does not support uid or gid authentication for sockets");
849                 return -1;
850         }
851
852 #endif
853
854         /*
855          *      FIXME: check for absolute pathnames?
856          *      check for uid/gid on the other end...    
857          */
858
859         this->fd = fr_server_domain_socket(sock->path);
860         if (this->fd < 0) {
861                 return -1;
862         }
863
864         return 0;
865 }
866
867 static int command_socket_print(rad_listen_t *this, char *buffer, size_t bufsize)
868 {
869         fr_command_socket_t *sock = this->data;
870
871         snprintf(buffer, bufsize, "command file %s", sock->path);
872         return 1;
873 }
874
875
876 /*
877  *      String split routine.  Splits an input string IN PLACE
878  *      into pieces, based on spaces.
879  */
880 static int str2argv(char *str, char **argv, int max_argc)
881 {
882         int argc = 0;
883         size_t len;
884         char buffer[1024];
885
886         while (*str) {
887                 if (argc >= max_argc) return argc;
888
889                 /*
890                  *      Chop out comments early.
891                  */
892                 if (*str == '#') {
893                         *str = '\0';
894                         break;
895                 }
896
897                 while ((*str == ' ') ||
898                        (*str == '\t') ||
899                        (*str == '\r') ||
900                        (*str == '\n')) *(str++) = '\0';
901
902                 if (!*str) return argc;
903
904                 if ((*str == '\'') || (*str == '"')) {
905                         char *p = str;
906                         FR_TOKEN token;
907
908                         token = gettoken((const char **) &p, buffer,
909                                          sizeof(buffer));
910                         if ((token != T_SINGLE_QUOTED_STRING) &&
911                             (token != T_DOUBLE_QUOTED_STRING)) {
912                                 return -1;
913                         }
914
915                         len = strlen(buffer);
916                         if (len >= (size_t) (p - str)) {
917                                 return -1;
918                         }
919
920                         memcpy(str, buffer, len + 1);
921                         argv[argc] = str;
922                         str = p;
923
924                 } else {
925                         argv[argc] = str;
926                 }
927                 argc++;
928
929                 while (*str &&
930                        (*str != ' ') &&
931                        (*str != '\t') &&
932                        (*str != '\r') &&
933                        (*str != '\n')) str++;
934         }
935
936         return argc;
937 }
938
939 #define MAX_ARGV (16)
940
941 /*
942  *      Check if an incoming request is "ok"
943  *
944  *      It takes packets, not requests.  It sees if the packet looks
945  *      OK.  If so, it does a number of sanity checks on it.
946  */
947 static int command_domain_recv(rad_listen_t *listener,
948                                UNUSED RAD_REQUEST_FUNP *pfun,
949                                UNUSED REQUEST **prequest)
950 {
951         int i, rcode;
952         ssize_t len;
953         int argc;
954         char *my_argv[MAX_ARGV], **argv;
955         fr_command_table_t *table;
956         fr_command_socket_t *co = listener->data;
957
958         *pfun = NULL;
959         *prequest = NULL;
960
961         do {
962                 ssize_t c;
963                 char *p;
964
965                 len = recv(listener->fd, co->buffer + co->offset,
966                            sizeof(co->buffer) - co->offset - 1, 0);
967                 if (len == 0) goto close_socket; /* clean close */
968
969                 if (len < 0) {
970                         if ((errno == EAGAIN) || (errno == EINTR)) {
971                                 return 0;
972                         }
973                         goto close_socket;
974                 }
975
976                 /*
977                  *      CTRL-D
978                  */
979                 if ((co->offset == 0) && (co->buffer[0] == 0x04)) {
980                 close_socket:
981                         listener->status = RAD_LISTEN_STATUS_CLOSED;
982                         event_new_fd(listener);
983                         return 0;
984                 }
985
986                 /*
987                  *      See if there are multiple lines in the buffer.
988                  */
989                 p = co->buffer + co->offset;
990                 rcode = 0;
991                 p[len] = '\0';
992                 for (c = 0; c < len; c++) {
993                         if ((*p == '\r') || (*p == '\n')) {
994                                 rcode = 1;
995                                 *p = '\0';
996
997                                 /*
998                                  *      FIXME: do real buffering...
999                                  *      handling of CTRL-C, etc.
1000                                  */
1001
1002                         } else if (rcode) {
1003                                 /*
1004                                  *      \r \n followed by ASCII...
1005                                  */
1006                                 break;
1007                         }
1008
1009                         p++;
1010                 }
1011
1012                 co->offset += len;
1013
1014                 /*
1015                  *      Saw CR/LF.  Set next element, and exit.
1016                  */
1017                 if (rcode) {
1018                         co->next = p - co->buffer;
1019                         break;
1020                 }
1021
1022                 if (co->offset >= (ssize_t) (sizeof(co->buffer) - 1)) {
1023                         radlog(L_ERR, "Line too long!");
1024                         goto close_socket;
1025                 }
1026
1027                 co->offset++;
1028         } while (1);
1029
1030         argc = str2argv(co->buffer, my_argv, MAX_ARGV);
1031         if (argc == 0) goto do_next; /* empty strings are OK */
1032
1033         if (argc < 0) {
1034                 cprintf(listener, "ERROR: Failed parsing command.\n");
1035                 goto do_next;
1036         }
1037
1038         argv = my_argv;
1039
1040         for (len = 0; len <= co->offset; len++) {
1041                 if (co->buffer[len] < 0x20) {
1042                         co->buffer[len] = '\0';
1043                         break;
1044                 }
1045         }
1046
1047         /*
1048          *      Hard-code exit && quit.
1049          */
1050         if ((strcmp(argv[0], "exit") == 0) ||
1051             (strcmp(argv[0], "quit") == 0)) goto close_socket;
1052
1053 #if 0
1054         if (!co->user[0]) {
1055                 if (strcmp(argv[0], "login") != 0) {
1056                         cprintf(listener, "ERROR: Login required\n");
1057                         goto do_next;
1058                 }
1059
1060                 if (argc < 3) {
1061                         cprintf(listener, "ERROR: login <user> <password>\n");
1062                         goto do_next;
1063                 }
1064
1065                 /*
1066                  *      FIXME: Generate && process fake RADIUS request.
1067                  */
1068                 if ((strcmp(argv[1], "root") == 0) &&
1069                     (strcmp(argv[2], "password") == 0)) {
1070                         strlcpy(co->user, argv[1], sizeof(co->user));
1071                         goto do_next;
1072                 }
1073
1074                 cprintf(listener, "ERROR: Login incorrect\n");
1075                 goto do_next;
1076         }
1077 #endif
1078
1079         table = command_table;
1080  retry:
1081         len = 0;
1082         for (i = 0; table[i].command != NULL; i++) {
1083                 if (strcmp(table[i].command, argv[0]) == 0) {
1084                         if (table[i].table) {
1085                                 /*
1086                                  *      This is the last argument, but
1087                                  *      there's a sub-table.  Print help.
1088                                  *      
1089                                  */
1090                                 if (argc == 1) {
1091                                         table = table[i].table;
1092                                         goto do_help;
1093                                 }
1094
1095                                 argc--;
1096                                 argv++;
1097                                 table = table[i].table;
1098                                 goto retry;
1099                         }
1100
1101                         len = 1;
1102                         rcode = table[i].func(listener,
1103                                               argc - 1, argv + 1);
1104                         break;
1105                 }
1106         }
1107
1108         /*
1109          *      No such command
1110          */
1111         if (!len) {
1112                 if ((strcmp(argv[0], "help") == 0) ||
1113                     (strcmp(argv[0], "?") == 0)) {
1114                 do_help:
1115                         for (i = 0; table[i].command != NULL; i++) {
1116                                 if (table[i].help) {
1117                                         cprintf(listener, "%s\n",
1118                                                 table[i].help);
1119                                 } else {
1120                                         cprintf(listener, "%s <command> - do sub-command of %s\n",
1121                                                 table[i].command, table[i].command);
1122                                 }
1123                         }
1124                         goto do_next;
1125                 }
1126
1127                 cprintf(listener, "ERROR: Unknown command \"%s\"\r\n",
1128                         argv[0]);
1129         }
1130
1131  do_next:
1132         cprintf(listener, "radmin> ");
1133
1134         if (co->next <= co->offset) {
1135                 co->offset = 0;
1136         } else {
1137                 memmove(co->buffer, co->buffer + co->next,
1138                         co->offset - co->next);
1139                 co->offset -= co->next;
1140         }
1141
1142         return 0;
1143 }
1144
1145
1146 static int command_domain_accept(rad_listen_t *listener,
1147                                  UNUSED RAD_REQUEST_FUNP *pfun,
1148                                  UNUSED REQUEST **prequest)
1149 {
1150         int newfd;
1151         uint32_t magic;
1152         rad_listen_t *this;
1153         socklen_t salen;
1154         struct sockaddr_storage src;
1155         fr_command_socket_t *sock = listener->data;
1156         
1157         salen = sizeof(src);
1158
1159         DEBUG2(" ... new connection request on command socket.");
1160         
1161         newfd = accept(listener->fd, (struct sockaddr *) &src, &salen);
1162         if (newfd < 0) {
1163                 /*
1164                  *      Non-blocking sockets must handle this.
1165                  */
1166                 if (errno == EWOULDBLOCK) {
1167                         return 0;
1168                 }
1169
1170                 DEBUG2(" ... failed to accept connection.");
1171                 return -1;
1172         }
1173
1174         /*
1175          *      Perform user authentication.
1176          */
1177         if (sock->uid_name || sock->gid_name) {
1178                 uid_t uid;
1179                 gid_t gid;
1180
1181                 if (getpeereid(listener->fd, &uid, &gid) < 0) {
1182                         radlog(L_ERR, "Failed getting peer credentials for %s: %s",
1183                                sock->path, strerror(errno));
1184                         close(newfd);
1185                         return -1;
1186                 }
1187
1188                 if (sock->uid_name && (sock->uid != uid)) {
1189                         radlog(L_ERR, "Unauthorized connection to %s from uid %ld",
1190                                sock->path, (long int) uid);
1191                         close(newfd);
1192                         return -1;
1193                 }
1194
1195                 if (sock->gid_name && (sock->gid != gid)) {
1196                         radlog(L_ERR, "Unauthorized connection to %s from gid %ld",
1197                                sock->path, (long int) gid);
1198                         close(newfd);
1199                         return -1;
1200                 }
1201         }
1202
1203         /*
1204          *      Write 32-bit magic number && version information.
1205          */
1206         magic = htonl(0xf7eead15);
1207         if (write(newfd, &magic, 4) < 0) {
1208                 radlog(L_ERR, "Failed writing initial data to socket: %s",
1209                        strerror(errno));
1210                 close(newfd);
1211                 return -1;
1212         }
1213         magic = htonl(1);       /* protocol version */
1214         if (write(newfd, &magic, 4) < 0) {
1215                 radlog(L_ERR, "Failed writing initial data to socket: %s",
1216                        strerror(errno));
1217                 close(newfd);
1218                 return -1;
1219         }
1220
1221
1222         /*
1223          *      Add the new listener.
1224          */
1225         this = listen_alloc(listener->type);
1226         if (!this) return -1;
1227
1228         /*
1229          *      Copy everything, including the pointer to the socket
1230          *      information.
1231          */
1232         sock = this->data;
1233         memcpy(this, listener, sizeof(*this));
1234         this->status = RAD_LISTEN_STATUS_INIT;
1235         this->next = NULL;
1236         this->data = sock;      /* fix it back */
1237
1238         sock->offset = 0;
1239         sock->user[0] = '\0';
1240         sock->path = ((fr_command_socket_t *) listener->data)->path;
1241
1242         this->fd = newfd;
1243         this->recv = command_domain_recv;
1244
1245         /*
1246          *      Tell the event loop that we have a new FD
1247          */
1248         event_new_fd(this);
1249
1250         return 0;
1251 }
1252
1253
1254 /*
1255  *      Send an authentication response packet
1256  */
1257 static int command_domain_send(UNUSED rad_listen_t *listener,
1258                                UNUSED REQUEST *request)
1259 {
1260         return 0;
1261 }
1262
1263
1264 static int command_socket_encode(UNUSED rad_listen_t *listener,
1265                                  UNUSED REQUEST *request)
1266 {
1267         return 0;
1268 }
1269
1270
1271 static int command_socket_decode(UNUSED rad_listen_t *listener,
1272                                  UNUSED REQUEST *request)
1273 {
1274         return 0;
1275 }
1276
1277 #endif /* WITH_COMMAND_SOCKET */