Added "del client <ipaddr>" command for dynamic clients
[freeradius.git] / src / main / command.c
index 1f0d960..1b6e9c3 100644 (file)
@@ -66,6 +66,7 @@ struct fr_command_table_t {
 
 typedef struct fr_command_socket_t {
        char    *path;
+       char    *copy;          /* <sigh> */
        uid_t   uid;
        gid_t   gid;
        int     mode;
@@ -254,6 +255,7 @@ static int fr_server_domain_socket(const char *path)
        return sockfd;
 }
 
+
 static void command_close_socket(rad_listen_t *this)
 {
        this->status = RAD_LISTEN_STATUS_CLOSED;
@@ -263,8 +265,6 @@ static void command_close_socket(rad_listen_t *this)
         *      will be calling us any more.
         */
        event_new_fd(this);
-
-       listen_free(&this);
 }
 
 
@@ -387,7 +387,7 @@ static void cprint_conf_parser(rad_listen_t *listener, int indent, CONF_SECTION
                        data = variables[i].data;;
                        
                } else {
-                       data = (((char *)base) + variables[i].offset);
+                       data = (((const char *)base) + variables[i].offset);
                }
 
                switch (variables[i].type) {
@@ -398,7 +398,7 @@ static void cprint_conf_parser(rad_listen_t *listener, int indent, CONF_SECTION
                        
                case PW_TYPE_INTEGER:
                        cprintf(listener, "%.*s%s = %u\n", indent, tabs,
-                               variables[i].name, *(int *) data);
+                               variables[i].name, *(const int *) data);
                        break;
                        
                case PW_TYPE_IPADDR:
@@ -412,7 +412,7 @@ static void cprint_conf_parser(rad_listen_t *listener, int indent, CONF_SECTION
                case PW_TYPE_BOOLEAN:
                        cprintf(listener, "%.*s%s = %s\n", indent, tabs,
                                variables[i].name, 
-                               ((*(int *) data) == 0) ? "no" : "yes");
+                               ((*(const int *) data) == 0) ? "no" : "yes");
                        break;
                        
                case PW_TYPE_STRING_PTR:
@@ -420,9 +420,9 @@ static void cprint_conf_parser(rad_listen_t *listener, int indent, CONF_SECTION
                        /*
                         *      FIXME: Escape things in the string!
                         */
-                       if (*(char **) data) {
+                       if (*(const char * const *) data) {
                                cprintf(listener, "%.*s%s = \"%s\"\n", indent, tabs,
-                                       variables[i].name, *(char **) data);
+                                       variables[i].name, *(const char * const *) data);
                        } else {
                                cprintf(listener, "%.*s%s = \n", indent, tabs,
                                        variables[i].name);
@@ -579,7 +579,7 @@ static int command_show_home_servers(rad_listen_t *listener, UNUSED int argc, UN
 {
        int i;
        home_server *home;
-       const char *type, *state;
+       const char *type, *state, *proto;
 
        char buffer[256];
 
@@ -600,6 +600,16 @@ static int command_show_home_servers(rad_listen_t *listener, UNUSED int argc, UN
 
                } else continue;
 
+               if (home->proto == IPPROTO_UDP) {
+                       proto = "udp";
+               }
+#ifdef WITH_TCP
+               else if (home->proto == IPPROTO_TCP) {
+                       proto = "tcp";
+               }
+#endif
+               else proto = "??";
+
                if (home->state == HOME_STATE_ALIVE) {
                        state = "alive";
 
@@ -611,9 +621,9 @@ static int command_show_home_servers(rad_listen_t *listener, UNUSED int argc, UN
 
                } else continue;
 
-               cprintf(listener, "%s\t%d\t%s\t%s\t%d\n",
+               cprintf(listener, "%s\t%d\t%s\t%s\t%s\t%d\n",
                        ip_ntoh(&home->ipaddr, buffer, sizeof(buffer)),
-                       home->port, type, state,
+                       home->port, proto, type, state,
                        home->currently_outstanding);
        }
 
@@ -802,6 +812,7 @@ static RADCLIENT *get_client(rad_listen_t *listener, int argc, char *argv[])
 {
        RADCLIENT *client;
        fr_ipaddr_t ipaddr;
+       int proto = IPPROTO_UDP;
 
        if (argc < 1) {
                cprintf(listener, "ERROR: Must specify <ipaddr>\n");
@@ -814,7 +825,23 @@ static RADCLIENT *get_client(rad_listen_t *listener, int argc, char *argv[])
                return NULL;
        }
 
-       client = client_find(NULL, &ipaddr);
+#ifdef WITH_TCP
+       if (argc >= 2) {
+               if (strcmp(argv[1], "tcp") == 0) {
+                       proto = IPPROTO_TCP;
+
+               } else if (strcmp(argv[1], "udp") == 0) {
+                       proto = IPPROTO_UDP;
+
+               } else {
+                       cprintf(listener, "ERROR: Unknown protocol %s.  Please use \"udp\" or \"tcp\"\n",
+                               argv[1]);
+                       return NULL;
+               }
+       }
+#endif
+
+       client = client_find(NULL, &ipaddr, proto);
        if (!client) {
                cprintf(listener, "ERROR: No such client\n");
                return NULL;
@@ -849,14 +876,16 @@ static int command_show_client_config(rad_listen_t *listener, int argc, char *ar
 }
 
 #ifdef WITH_PROXY
-static home_server *get_home_server(rad_listen_t *listener, int argc, char *argv[])
+static home_server *get_home_server(rad_listen_t *listener, int argc,
+                                   char *argv[], int *last)
 {
        home_server *home;
        int port;
+       int proto = IPPROTO_UDP;
        fr_ipaddr_t ipaddr;
 
        if (argc < 2) {
-               cprintf(listener, "ERROR: Must specify <ipaddr> <port>\n");
+               cprintf(listener, "ERROR: Must specify <ipaddr> <port> [proto]\n");
                return NULL;
        }
 
@@ -868,7 +897,21 @@ static home_server *get_home_server(rad_listen_t *listener, int argc, char *argv
 
        port = atoi(argv[1]);
 
-       home = home_server_find(&ipaddr, port);
+       if (last) *last = 2;
+       if (argc > 2) {
+               if (strcmp(argv[2], "udp") == 0) {
+                       proto = IPPROTO_UDP;
+                       if (last) *last = 3;
+               }
+#ifdef WITH_TCP
+               if (strcmp(argv[2], "tcp") == 0) {
+                       proto = IPPROTO_TCP;
+                       if (last) *last = 3;
+               }
+#endif
+       }
+
+       home = home_server_find(&ipaddr, port, proto);
        if (!home) {
                cprintf(listener, "ERROR: No such home server\n");
                return NULL;
@@ -882,7 +925,7 @@ static int command_show_home_server_config(rad_listen_t *listener, int argc, cha
        home_server *home;
        FILE *fp;
 
-       home = get_home_server(listener, argc, argv);
+       home = get_home_server(listener, argc, argv, NULL);
        if (!home) {
                return 0;
        }
@@ -906,29 +949,30 @@ extern void mark_home_server_dead(home_server *home, struct timeval *when);
 
 static int command_set_home_server_state(rad_listen_t *listener, int argc, char *argv[])
 {
+       int last;
        home_server *home;
 
        if (argc < 3) {
-               cprintf(listener, "ERROR: Must specify <ipaddr> <port> <state>\n");
+               cprintf(listener, "ERROR: Must specify <ipaddr> <port> [proto] <state>\n");
                return 0;
        }
 
-       home = get_home_server(listener, argc, argv);
+       home = get_home_server(listener, argc, argv, &last);
        if (!home) {
                return 0;
        }
 
-       if (strcmp(argv[2], "alive") == 0) {
+       if (strcmp(argv[last], "alive") == 0) {
                revive_home_server(home);
 
-       } else if (strcmp(argv[2], "dead") == 0) {
+       } else if (strcmp(argv[last], "dead") == 0) {
                struct timeval now;
 
                gettimeofday(&now, NULL); /* we do this WAY too ofetn */
                mark_home_server_dead(home, &now);
 
        } else {
-               cprintf(listener, "ERROR: Unknown state \"%s\"\n", argv[2]);
+               cprintf(listener, "ERROR: Unknown state \"%s\"\n", argv[last]);
                return 0;
        }
 
@@ -939,7 +983,7 @@ static int command_show_home_server_state(rad_listen_t *listener, int argc, char
 {
        home_server *home;
 
-       home = get_home_server(listener, argc, argv);
+       home = get_home_server(listener, argc, argv, NULL);
        if (!home) {
                return 0;
        }
@@ -1297,7 +1341,11 @@ static fr_command_table_t command_table_show_module[] = {
 
 static fr_command_table_t command_table_show_client[] = {
        { "config", FR_READ,
-         "show client config <ipaddr> - show configuration for given client",
+         "show client config <ipaddr> "
+#ifdef WITH_TCP
+         "[proto] "
+#endif
+         "- show configuration for given client",
          command_show_client_config, NULL },
        { "list", FR_READ,
          "show client list - shows list of global clients",
@@ -1309,13 +1357,13 @@ static fr_command_table_t command_table_show_client[] = {
 #ifdef WITH_PROXY
 static fr_command_table_t command_table_show_home[] = {
        { "config", FR_READ,
-         "show home_server config <ipaddr> <port> - show configuration for given home server",
+         "show home_server config <ipaddr> <port> [proto] - show configuration for given home server",
          command_show_home_server_config, NULL },
        { "list", FR_READ,
          "show home_server list - shows list of home servers",
          command_show_home_servers, NULL },
        { "state", FR_READ,
-         "show home_server state <ipaddr> <port> - shows state of given home server",
+         "show home_server state <ipaddr> <port> [proto] - shows state of given home server",
          command_show_home_server_state, NULL },
 
        { NULL, 0, NULL, NULL, NULL }
@@ -1439,32 +1487,129 @@ static int command_set_module_config(rad_listen_t *listener, int argc, char *arg
        return 1;               /* success */
 }
 
+static int command_set_module_status(rad_listen_t *listener, int argc, char *argv[])
+{
+       CONF_SECTION *cs;
+       module_instance_t *mi;
+
+       if (argc < 2) {
+               cprintf(listener, "ERROR: No module name or status was given\n");
+               return 0;
+       }
+
+       cs = cf_section_find("modules");
+       if (!cs) return 0;
+
+       mi = find_module_instance(cs, argv[0], 0);
+       if (!mi) {
+               cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
+               return 0;
+       }
+
+
+       if (strcmp(argv[1], "alive") == 0) {
+               mi->dead = FALSE;
+
+       } else if (strcmp(argv[1], "dead") == 0) {
+               mi->dead = TRUE;
+
+       } else {
+               cprintf(listener, "ERROR: Unknown status \"%s\"\n", argv[2]);
+               return 0;
+       }
+
+       return 1;               /* success */
+}
+
 static int command_print_stats(rad_listen_t *listener, fr_stats_t *stats,
                               int auth)
 {
-       cprintf(listener, "\trequests\t%d\n", stats->total_requests);
-       cprintf(listener, "\tresponses\t%d\n", stats->total_responses);
+       cprintf(listener, "\trequests\t%u\n", stats->total_requests);
+       cprintf(listener, "\tresponses\t%u\n", stats->total_responses);
        
        if (auth) {
-               cprintf(listener, "\taccepts\t\t%d\n",
+               cprintf(listener, "\taccepts\t\t%u\n",
                        stats->total_access_accepts);
-               cprintf(listener, "\trejects\t\t%d\n",
+               cprintf(listener, "\trejects\t\t%u\n",
                        stats->total_access_rejects);
-               cprintf(listener, "\tchallenges\t%d\n",
+               cprintf(listener, "\tchallenges\t%u\n",
                        stats->total_access_challenges);
        }
 
-       cprintf(listener, "\tdup\t\t%d\n", stats->total_dup_requests);
-       cprintf(listener, "\tinvalid\t\t%d\n", stats->total_invalid_requests);
-       cprintf(listener, "\tmalformed\t%d\n", stats->total_malformed_requests);
-       cprintf(listener, "\tbad_signature\t%d\n", stats->total_bad_authenticators);
-       cprintf(listener, "\tdropped\t\t%d\n", stats->total_packets_dropped);
-       cprintf(listener, "\tunknown_types\t%d\n", stats->total_unknown_types);
+       cprintf(listener, "\tdup\t\t%u\n", stats->total_dup_requests);
+       cprintf(listener, "\tinvalid\t\t%u\n", stats->total_invalid_requests);
+       cprintf(listener, "\tmalformed\t%u\n", stats->total_malformed_requests);
+       cprintf(listener, "\tbad_signature\t%u\n", stats->total_bad_authenticators);
+       cprintf(listener, "\tdropped\t\t%u\n", stats->total_packets_dropped);
+       cprintf(listener, "\tunknown_types\t%u\n", stats->total_unknown_types);
        
        return 1;
 }
 
 
+#ifdef WITH_DETAIL
+static FR_NAME_NUMBER state_names[] = {
+       { "unopened", STATE_UNOPENED },
+       { "unlocked", STATE_UNLOCKED },
+       { "header", STATE_HEADER },
+       { "reading", STATE_READING },
+       { "queued", STATE_QUEUED },
+       { "running", STATE_RUNNING },
+       { "no-reply", STATE_NO_REPLY },
+       { "replied", STATE_REPLIED },
+
+       { NULL, 0 }
+};
+
+static int command_stats_detail(rad_listen_t *listener, int argc, char *argv[])
+{
+       rad_listen_t *this;
+       listen_detail_t *data;
+       struct stat buf;
+
+       if (argc == 0) {
+               cprintf(listener, "ERROR: Must specify <filename>\n");
+               return 0;
+       }
+
+       data = NULL;
+       for (this = mainconfig.listen; this != NULL; this = this->next) {
+               if (this->type != RAD_LISTEN_DETAIL) continue;
+
+               data = this->data;
+               if (strcmp(argv[1], data->filename) != 0) continue;
+
+               break;
+       }
+
+       cprintf(listener, "\tstate\t%s\n",
+               fr_int2str(state_names, data->state, "?"));
+
+       if ((data->state == STATE_UNOPENED) ||
+           (data->state == STATE_UNLOCKED)) {
+               return 1;
+       }
+
+       /*
+        *      Race conditions: file might not exist.
+        */
+       if (stat(data->filename_work, &buf) < 0) {
+               cprintf(listener, "packets\t0\n");
+               cprintf(listener, "tries\t0\n");
+               cprintf(listener, "offset\t0\n");
+               cprintf(listener, "size\t0\n");
+               return 1;
+       }
+
+       cprintf(listener, "packets\t%d\n", data->packets);
+       cprintf(listener, "tries\t%d\n", data->tries);
+       cprintf(listener, "offset\t%u\n", (unsigned int) data->offset);
+       cprintf(listener, "size\t%u\n", (unsigned int) buf.st_size);
+
+       return 1;
+}
+#endif
+
 #ifdef WITH_PROXY
 static int command_stats_home_server(rad_listen_t *listener, int argc, char *argv[])
 {
@@ -1491,7 +1636,7 @@ static int command_stats_home_server(rad_listen_t *listener, int argc, char *arg
                return 0;
        }
 
-       home = get_home_server(listener, argc, argv);
+       home = get_home_server(listener, argc, argv, NULL);
        if (!home) {
                return 0;
        }
@@ -1585,6 +1730,54 @@ static int command_add_client_file(rad_listen_t *listener, int argc, char *argv[
 }
 
 
+static int command_del_client(rad_listen_t *listener, int argc, char *argv[])
+{
+#ifdef WITH_DYNAMIC_CLIENTS
+       RADCLIENT *client;
+
+       client = get_client(listener, argc - 1, argv + 1);
+       if (!client) return 0;
+
+       if (!client->dynamic) {
+               cprintf(listener, "ERROR: Client %s was not dynamically defined.\n", argv[1]);
+               return 0;
+       }
+
+       /*
+        *      DON'T delete it.  Instead, mark it as "dead now".  The
+        *      next time we receive a packet for the client, it will
+        *      be deleted.
+        *
+        *      If we don't receive a packet from it, the client
+        *      structure will stick around for a while.  Oh well...
+        */
+       client->lifetime = 1;
+#else
+       cprintf(listener, "ERROR: Dynamic clients are not supported.\n");
+#endif
+
+       return 1;
+}
+
+
+static fr_command_table_t command_table_del_client[] = {
+       { "ipaddr", FR_WRITE,
+         "del client ipaddr <ipaddr> - Delete a dynamically created client",
+         command_del_client, NULL },
+
+       { NULL, 0, NULL, NULL, NULL }
+};
+
+
+static fr_command_table_t command_table_del[] = {
+       { "client", FR_WRITE,
+         "del client <command> - Delete client configuration commands",
+         NULL, command_table_del_client },
+
+       { NULL, 0, NULL, NULL, NULL }
+};
+
+
 static fr_command_table_t command_table_add_client[] = {
        { "file", FR_WRITE,
          "add client file <filename> - Add new client definition from <filename>",
@@ -1606,7 +1799,7 @@ static fr_command_table_t command_table_add[] = {
 #ifdef WITH_PROXY
 static fr_command_table_t command_table_set_home[] = {
        { "state", FR_WRITE,
-         "set home_server state <ipaddr> <port> [alive|dead] - set state for given home server",
+         "set home_server state <ipaddr> <port> [proto] [alive|dead] - set state for given home server",
          command_set_home_server_state, NULL },
 
        { NULL, 0, NULL, NULL, NULL }
@@ -1618,6 +1811,10 @@ static fr_command_table_t command_table_set_module[] = {
          "set module config <module> variable value - set configuration for <module>",
          command_set_module_config, NULL },
 
+       { "status", FR_WRITE,
+         "set module status [alive|dead] - set the module to be alive or dead (always return \"fail\")",
+         command_set_module_status, NULL },
+
        { NULL, 0, NULL, NULL, NULL }
 };
 
@@ -1638,7 +1835,11 @@ static fr_command_table_t command_table_set[] = {
 
 static fr_command_table_t command_table_stats[] = {
        { "client", FR_READ,
-         "stats client [auth/acct] <ipaddr> - show statistics for given client, or for all clients (auth or acct)",
+         "stats client [auth/acct] <ipaddr> "
+#ifdef WITH_TCP
+         "[proto] "
+#endif
+         "- show statistics for given client, or for all clients (auth or acct)",
          command_stats_client, NULL },
 #ifdef WITH_PROXY
        { "home_server", FR_READ,
@@ -1646,6 +1847,12 @@ static fr_command_table_t command_table_stats[] = {
          command_stats_home_server, NULL },
 #endif
 
+#ifdef WITH_DETAIL
+       { "detail", FR_READ,
+         "stats detail <filename> - show statistics for the given detail file",
+         command_stats_detail, NULL },
+#endif
+
        { NULL, 0, NULL, NULL, NULL }
 };
 
@@ -1654,6 +1861,7 @@ static fr_command_table_t command_table[] = {
        { "debug", FR_WRITE,
          "debug <command> - debugging commands",
          NULL, command_table_debug },
+       { "del", FR_WRITE, NULL, NULL, command_table_del },
        { "hup", FR_WRITE,
          "hup [module] - sends a HUP signal to the server, or optionally to one module",
          command_hup, NULL },
@@ -1674,6 +1882,16 @@ static fr_command_table_t command_table[] = {
 };
 
 
+static void command_socket_free(rad_listen_t *this)
+{
+       fr_command_socket_t *sock = this->data;
+
+       unlink(sock->copy);
+       free(sock->copy);
+       sock->copy = NULL;
+}
+
+
 /*
  *     Parse the unix domain sockets.
  *
@@ -1683,12 +1901,17 @@ static int command_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
 {
        fr_command_socket_t *sock;
 
+       if (check_config) return 0;
+
        sock = this->data;
 
        if (cf_section_parse(cs, sock, command_config) < 0) {
                return -1;
        }
 
+       sock->copy = NULL;
+       if (sock->path) sock->copy = strdup(sock->path);
+
 #if defined(HAVE_GETPEEREID) || defined (SO_PEERCRED)
        if (sock->uid_name) {
                struct passwd *pw;
@@ -1930,6 +2153,8 @@ static int command_domain_recv(rad_listen_t *listener,
                co->offset++;
        } while (1);
 
+       DEBUG("radmin> %s", co->buffer);
+
        argc = str2argv(co->buffer, my_argv, MAX_ARGV);
        if (argc == 0) goto do_next; /* empty strings are OK */
 
@@ -1989,7 +2214,7 @@ static int command_domain_recv(rad_listen_t *listener,
                         */
                        if (((co->mode & FR_WRITE) == 0) &&
                            ((table[i].mode & FR_WRITE) != 0)) {
-                               cprintf(listener, "ERROR: You do not have write permission.\n");
+                               cprintf(listener, "ERROR: You do not have write permission.  See \"mode = rw\" in the \"listen\" section for this socket.\n");
                                goto do_next;
                        }
 
@@ -2010,6 +2235,8 @@ static int command_domain_recv(rad_listen_t *listener,
                                goto retry;
                        }
 
+                       if ((argc == 2) && (strcmp(argv[1], "?") == 0)) goto do_help;
+
                        if (!table[i].func) {
                                cprintf(listener, "ERROR: Invalid command\n");
                                goto do_next;
@@ -2028,11 +2255,13 @@ static int command_domain_recv(rad_listen_t *listener,
        if (!len) {
                if ((strcmp(argv[0], "help") == 0) ||
                    (strcmp(argv[0], "?") == 0)) {
-                       int recursive = 0;
+                       int recursive;
 
                do_help:
                        if ((argc > 1) && (strcmp(argv[1], "-r") == 0)) {
                                recursive = TRUE;
+                       } else {
+                               recursive = FALSE;
                        }
 
                        print_help(listener, table, recursive);
@@ -2072,6 +2301,9 @@ static int command_domain_accept(rad_listen_t *listener,
        salen = sizeof(src);
 
        DEBUG2(" ... new connection request on command socket.");
+
+       *pfun = NULL;
+       *prequest = NULL;
        
        newfd = accept(listener->fd, (struct sockaddr *) &src, &salen);
        if (newfd < 0) {
@@ -2083,7 +2315,7 @@ static int command_domain_accept(rad_listen_t *listener,
                }
 
                DEBUG2(" ... failed to accept connection.");
-               return -1;
+               return 0;
        }
 
        /*
@@ -2097,21 +2329,21 @@ static int command_domain_accept(rad_listen_t *listener,
                        radlog(L_ERR, "Failed getting peer credentials for %s: %s",
                               sock->path, strerror(errno));
                        close(newfd);
-                       return -1;
+                       return 0;
                }
 
                if (sock->uid_name && (sock->uid != uid)) {
                        radlog(L_ERR, "Unauthorized connection to %s from uid %ld",
                               sock->path, (long int) uid);
                        close(newfd);
-                       return -1;
+                       return 0;
                }
 
                if (sock->gid_name && (sock->gid != gid)) {
                        radlog(L_ERR, "Unauthorized connection to %s from gid %ld",
                               sock->path, (long int) gid);
                        close(newfd);
-                       return -1;
+                       return 0;
                }
        }
 
@@ -2123,14 +2355,14 @@ static int command_domain_accept(rad_listen_t *listener,
                radlog(L_ERR, "Failed writing initial data to socket: %s",
                       strerror(errno));
                close(newfd);
-               return -1;
+               return 0;
        }
        magic = htonl(1);       /* protocol version */
        if (write(newfd, &magic, 4) < 0) {
                radlog(L_ERR, "Failed writing initial data to socket: %s",
                       strerror(errno));
                close(newfd);
-               return -1;
+               return 0;
        }
 
 
@@ -2138,7 +2370,7 @@ static int command_domain_accept(rad_listen_t *listener,
         *      Add the new listener.
         */
        this = listen_alloc(listener->type);
-       if (!this) return -1;
+       if (!this) return 0;
 
        /*
         *      Copy everything, including the pointer to the socket