2 * command.c Command socket processing.
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.
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.
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
20 * Copyright 2008 The FreeRADIUS server project
21 * Copyright 2008 Alan DeKok <aland@deployingradius.com>
24 #ifdef WITH_COMMAND_SOCKET
26 #include <freeradius-devel/modpriv.h>
28 * FIXME: configure checks.
32 typedef struct fr_command_table_t fr_command_table_t;
34 typedef int (*fr_command_func_t)(rad_listen_t *, int, char *argv[]);
36 struct fr_command_table_t {
39 fr_command_func_t func;
40 fr_command_table_t *table;
43 #define COMMAND_BUFFER_SIZE (1024)
45 typedef struct fr_command_socket_t {
50 char buffer[COMMAND_BUFFER_SIZE];
51 } fr_command_socket_t;
54 static ssize_t cprintf(rad_listen_t *listener, const char *fmt, ...)
56 __attribute__ ((format (printf, 2, 3)))
61 static int fr_server_domain_socket(const char *path)
66 struct sockaddr_un salocal;
69 if (len >= sizeof(salocal.sun_path)) {
70 fprintf(stderr, "Path too long in filename\n");
74 if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
75 fprintf(stderr, "Failed creating socket: %s\n",
80 salocal.sun_family = AF_UNIX;
81 memcpy(salocal.sun_path, path, len); /* not zero terminated */
83 socklen = sizeof(salocal.sun_family) + len;
86 * FIXME: stat it, first, to see who owns it,
87 * and who owns the directory above it.
89 if (unlink(path) < 0) {
90 fprintf(stderr, "Failed to delete %s: %s\n",
91 path, strerror(errno));
94 if (bind(sockfd, (struct sockaddr *)&salocal, socklen) < 0) {
95 fprintf(stderr, "Failed binding to %s: %s\n",
96 path, strerror(errno));
101 if (listen(sockfd, 8) < 0) {
102 fprintf(stderr, "Failed listening to %s: %s\n",
103 path, strerror(errno));
112 if ((flags = fcntl(sockfd, F_GETFL, NULL)) < 0) {
113 fprintf(stderr, "Failure getting socket flags: %s",
120 if( fcntl(sockfd, F_SETFL, flags) < 0) {
121 fprintf(stderr, "Failure setting socket flags: %s",
133 static ssize_t cprintf(rad_listen_t *listener, const char *fmt, ...)
140 len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
143 if (listener->status == RAD_LISTEN_STATUS_CLOSED) return 0;
145 len = write(listener->fd, buffer, len);
147 listener->status = RAD_LISTEN_STATUS_CLOSED;
148 event_new_fd(listener);
152 * FIXME: Keep writing until done?
157 static int command_hup(rad_listen_t *listener, int argc, char *argv[])
160 module_instance_t *mi;
163 radius_signal_self(RADIUS_SIGNAL_SELF_HUP);
167 cs = cf_section_find("modules");
170 mi = find_module_instance(cs, argv[0], 0);
172 cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
176 if (!module_hup_module(mi->cs, mi, time(NULL))) {
177 cprintf(listener, "ERROR: Failed to reload module\n");
181 return 1; /* success */
184 static int command_terminate(UNUSED rad_listen_t *listener,
185 UNUSED int argc, UNUSED char *argv[])
187 radius_signal_self(RADIUS_SIGNAL_SELF_TERM);
189 return 1; /* success */
192 extern time_t fr_start_time;
194 static int command_uptime(rad_listen_t *listener,
195 UNUSED int argc, UNUSED char *argv[])
199 CTIME_R(&fr_start_time, buffer, sizeof(buffer));
200 cprintf(listener, "Up since %s", buffer); /* no \r\n */
202 return 1; /* success */
205 static int command_show_config(UNUSED rad_listen_t *listener,
206 UNUSED int argc, UNUSED char *argv[])
209 return 1; /* success */
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";
215 * FIXME: Recurse && indent?
217 static void cprint_conf_parser(rad_listen_t *listener, int indent, CONF_SECTION *cs,
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);
228 cprintf(listener, "%.*s%s %s {\n", indent, tabs, name1, name2);
230 cprintf(listener, "%.*s%s {\n", indent, tabs, name1);
238 if (variables) for (i = 0; variables[i].name != NULL; i++) {
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.
245 if (!variables[i].data) {
249 data = variables[i].data;;
251 } else if (variables[i].data) {
252 data = variables[i].data;;
255 data = (((char *)base) + variables[i].offset);
258 switch (variables[i].type) {
260 cprintf(listener, "%.*s%s = ?\n", indent, tabs,
264 case PW_TYPE_INTEGER:
265 cprintf(listener, "%.*s%s = %u\n", indent, tabs,
266 variables[i].name, *(int *) data);
269 case PW_TYPE_BOOLEAN:
270 cprintf(listener, "%.*s%s = %s\n", indent, tabs,
272 ((*(int *) data) == 0) ? "no" : "yes");
275 case PW_TYPE_STRING_PTR:
276 case PW_TYPE_FILENAME:
278 * FIXME: Escape things in the string!
280 if (*(char **) data) {
281 cprintf(listener, "%.*s%s = \"%s\"\n", indent, tabs,
282 variables[i].name, *(char **) data);
284 cprintf(listener, "%.*s%s = \n", indent, tabs,
294 cprintf(listener, "%.*s}\n", indent, tabs);
297 static int command_show_module_config(rad_listen_t *listener, int argc, char *argv[])
300 module_instance_t *mi;
303 cprintf(listener, "ERROR: No module name was given\n");
307 cs = cf_section_find("modules");
310 mi = find_module_instance(cs, argv[0], 0);
312 cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
316 cprint_conf_parser(listener, 0, mi->cs, mi->insthandle);
318 return 1; /* success */
321 static const char *method_names[RLM_COMPONENT_COUNT] = {
333 static int command_show_module_methods(rad_listen_t *listener, int argc, char *argv[])
337 const module_instance_t *mi;
341 cprintf(listener, "ERROR: No module name was given\n");
345 cs = cf_section_find("modules");
348 mi = find_module_instance(cs, argv[0], 0);
350 cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
354 mod = mi->entry->module;
356 for (i = 0; i < RLM_COMPONENT_COUNT; i++) {
357 if (mod->methods[i]) cprintf(listener, "\t%s\n", method_names[i]);
360 return 1; /* success */
364 static int command_show_module_flags(rad_listen_t *listener, int argc, char *argv[])
367 const module_instance_t *mi;
371 cprintf(listener, "ERROR: No module name was given\n");
375 cs = cf_section_find("modules");
378 mi = find_module_instance(cs, argv[0], 0);
380 cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
384 mod = mi->entry->module;
386 if ((mod->type & RLM_TYPE_THREAD_SAFE) != 0)
387 cprintf(listener, "\tthread-safe\n");
390 if ((mod->type & RLM_TYPE_CHECK_CONFIG_SAFE) != 0)
391 cprintf(listener, "\twill-check-config\n");
394 if ((mod->type & RLM_TYPE_HUP_SAFE) != 0)
395 cprintf(listener, "\treload-on-hup\n");
397 return 1; /* success */
402 * Show all loaded modules
404 static int command_show_modules(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[])
406 CONF_SECTION *cs, *subcs;
408 cs = cf_section_find("modules");
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);
416 module_instance_t *mi;
419 mi = find_module_instance(cs, name2, 0);
422 cprintf(listener, "\t%s (%s)\n", name2, name1);
424 mi = find_module_instance(cs, name1, 0);
427 cprintf(listener, "\t%s\n", name1);
431 return 1; /* success */
435 static fr_command_table_t command_table_show_module[] = {
437 "show module config <module> - show configuration for <module>",
438 command_show_module_config, NULL },
440 "show module methods <module> - show sections where <module> may be used",
441 command_show_module_methods, NULL },
443 "show module flags <module> - show other module properties",
444 command_show_module_flags, NULL },
446 { NULL, NULL, NULL, NULL }
450 static fr_command_table_t command_table_show[] = {
452 "show config - show configuration stuff",
453 command_show_config, NULL },
455 "show module <command> - do sub-command of module",
456 NULL, command_table_show_module },
458 "show modules - shows list of loaded modules",
459 command_show_modules, NULL },
461 "show uptime - shows time at which server started",
462 command_uptime, NULL },
464 { NULL, NULL, NULL, NULL }
468 static int command_set_module_config(rad_listen_t *listener, int argc, char *argv[])
473 module_instance_t *mi;
474 const CONF_PARSER *variables;
478 cprintf(listener, "ERROR: No module name or variable was given\n");
482 cs = cf_section_find("modules");
485 mi = find_module_instance(cs, argv[0], 0);
487 cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
491 variables = cf_section_parse_table(mi->cs);
493 cprintf(listener, "ERROR: Cannot find configuration for module\n");
498 for (i = 0; variables[i].name != NULL; i++) {
500 * FIXME: Recurse into sub-types somehow...
502 if (variables[i].type == PW_TYPE_SUBSECTION) continue;
504 if (strcmp(variables[i].name, argv[1]) == 0) {
511 cprintf(listener, "ERROR: No such variable \"%s\"\n", argv[1]);
515 i = rcode; /* just to be safe */
518 * It's not part of the dynamic configuration. The module
519 * needs to re-parse && validate things.
521 if (variables[i].data) {
522 cprintf(listener, "ERROR: Variable cannot be dynamically updated\n");
526 data = ((char *) mi->insthandle) + variables[i].offset;
528 cp = cf_pair_find(mi->cs, argv[1]);
532 * Replace the OLD value in the configuration file with
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???
540 cf_pair_replace(mi->cs, cp, argv[2]);
542 rcode = cf_item_parse(mi->cs, argv[1], variables[i].type,
545 cprintf(listener, "ERROR: Failed to parse value\n");
550 return 1; /* success */
554 static fr_command_table_t command_table_set_module[] = {
556 "set module config <module> variable value - set configuration for <module>",
557 command_set_module_config, NULL },
559 { NULL, NULL, NULL, NULL }
563 static fr_command_table_t command_table_set[] = {
564 { "module", NULL, NULL, command_table_set_module },
566 { NULL, NULL, NULL, NULL }
570 static fr_command_table_t command_table[] = {
572 "hup [module] - sends a HUP signal to the server, or optionally to one module",
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 },
580 { NULL, NULL, NULL, NULL }
585 * FIXME: Unix domain sockets!
587 static int command_socket_parse(UNUSED CONF_SECTION *cs, rad_listen_t *this)
590 fr_command_socket_t *sock;
594 rcode = cf_item_parse(cs, "socket", PW_TYPE_STRING_PTR,
595 &sock->path, "${run_dir}/radiusd.sock");
596 if (rcode < 0) return -1;
599 * FIXME: check for absolute pathnames?
600 * check for uid/gid on the other end...
603 this->fd = fr_server_domain_socket(sock->path);
611 static int command_socket_print(rad_listen_t *this, char *buffer, size_t bufsize)
613 fr_command_socket_t *sock = this->data;
615 snprintf(buffer, bufsize, "command file %s", sock->path);
621 * String split routine. Splits an input string IN PLACE
622 * into pieces, based on spaces.
624 static int str2argv(char *str, char **argv, int max_argc)
629 if (argc >= max_argc) return argc;
632 * Chop out comments early.
639 while ((*str == ' ') ||
642 (*str == '\n')) *(str++) = '\0';
644 if (!*str) return argc;
653 (*str != '\n')) str++;
659 #define MAX_ARGV (16)
662 * Check if an incoming request is "ok"
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.
667 static int command_domain_recv(rad_listen_t *listener,
668 UNUSED RAD_REQUEST_FUNP *pfun,
669 UNUSED REQUEST **prequest)
674 char *my_argv[MAX_ARGV], **argv;
675 fr_command_table_t *table;
676 fr_command_socket_t *co = listener->data;
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 */
687 if ((errno == EAGAIN) || (errno == EINTR)) {
696 if ((co->offset == 0) && (co->buffer[0] == 0x04)) {
698 listener->status = RAD_LISTEN_STATUS_CLOSED;
699 event_new_fd(listener);
704 * See if there are multiple lines in the buffer.
706 p = co->buffer + co->offset;
709 for (c = 0; c < len; c++) {
710 if ((*p == '\r') || (*p == '\n')) {
715 * FIXME: do real buffering...
716 * handling of CTRL-C, etc.
721 * \r \n followed by ASCII...
732 * Saw CR/LF. Set next element, and exit.
735 co->next = p - co->buffer;
739 if (co->offset >= (ssize_t) (sizeof(co->buffer) - 1)) {
740 radlog(L_ERR, "Line too long!");
747 argc = str2argv(co->buffer, my_argv, MAX_ARGV);
748 if (argc == 0) goto do_next;
751 for (len = 0; len <= co->offset; len++) {
752 if (co->buffer[len] < 0x20) {
753 co->buffer[len] = '\0';
759 * Hard-code exit && quit.
761 if ((strcmp(argv[0], "exit") == 0) ||
762 (strcmp(argv[0], "quit") == 0)) goto close_socket;
766 if (strcmp(argv[0], "login") != 0) {
767 cprintf(listener, "ERROR: Login required\n");
772 cprintf(listener, "ERROR: login <user> <password>\n");
777 * FIXME: Generate && process fake RADIUS request.
779 if ((strcmp(argv[1], "root") == 0) &&
780 (strcmp(argv[2], "password") == 0)) {
781 strlcpy(co->user, argv[1], sizeof(co->user));
785 cprintf(listener, "ERROR: Login incorrect\n");
790 table = command_table;
793 for (i = 0; table[i].command != NULL; i++) {
794 if (strcmp(table[i].command, argv[0]) == 0) {
795 if (table[i].table) {
797 * This is the last argument, but
798 * there's a sub-table. Print help.
802 table = table[i].table;
808 table = table[i].table;
813 rcode = table[i].func(listener,
823 if (strcmp(argv[0], "help") == 0) {
825 for (i = 0; table[i].command != NULL; i++) {
827 cprintf(listener, "%s\n",
830 cprintf(listener, "%s <command> - do sub-command of %s\n",
831 table[i].command, table[i].command);
837 cprintf(listener, "ERROR: Unknown command \"%s\"\r\n",
842 cprintf(listener, "radmin> ");
844 if (co->next <= co->offset) {
847 memmove(co->buffer, co->buffer + co->next,
848 co->offset - co->next);
849 co->offset -= co->next;
856 static int command_domain_accept(rad_listen_t *listener,
857 UNUSED RAD_REQUEST_FUNP *pfun,
858 UNUSED REQUEST **prequest)
863 struct sockaddr_storage src;
864 listen_socket_t *sock;
865 fr_command_socket_t *co;
869 DEBUG2(" ... new connection request on command socket.");
871 newfd = accept(listener->fd, (struct sockaddr *) &src, &salen);
874 * Non-blocking sockets must handle this.
876 if (errno == EWOULDBLOCK) {
880 DEBUG2(" ... failed to accept connection.");
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);
894 * Add the new listener.
896 this = listen_alloc(listener->type);
897 if (!this) return -1;
900 * Copy everything, including the pointer to the socket
904 this->data = rad_malloc(sizeof(fr_command_socket_t));
906 memcpy(this->data, listener->data, sizeof(*sock));
907 memcpy(this, listener, sizeof(*this));
909 this->data = sock; /* fix it back */
916 this->recv = command_domain_recv;
919 * FIXME: set O_NONBLOCK on the accept'd fd.
920 * See djb's portability rants for details.
924 * Tell the event loop that we have a new FD
933 * Send an authentication response packet
935 static int command_domain_send(UNUSED rad_listen_t *listener,
936 UNUSED REQUEST *request)
942 static int command_socket_encode(UNUSED rad_listen_t *listener,
943 UNUSED REQUEST *request)
949 static int command_socket_decode(UNUSED rad_listen_t *listener,
950 UNUSED REQUEST *request)
955 #endif /* WITH_COMMAND_SOCKET */