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