+ if (request->reply->code != 0) {
+ const char *what = "reply";
+ char buffer[1024];
+
+ if (request->reply->code < FR_MAX_PACKET_CODE) {
+ what = fr_packet_codes[request->reply->code];
+ }
+
+ fprintf(fp, "%s\n", what);
+
+ if (debug_flag) {
+ request->radlog(L_DBG, 0, request,
+ "Injected %s packet to host %s port 0 code=%d, id=%d",
+ what,
+ inet_ntop(request->reply->src_ipaddr.af,
+ &request->reply->src_ipaddr.ipaddr,
+ buffer, sizeof(buffer)),
+ request->reply->code, request->reply->id);
+ }
+
+ for (vp = request->reply->vps; vp != NULL; vp = vp->next) {
+ vp_prints(buffer, sizeof(buffer), vp);
+ fprintf(fp, "%s\n", buffer);
+ if (debug_flag) {
+ request->radlog(L_DBG, 0, request, "\t%s",
+ buffer);
+ }
+ }
+ }
+ fclose(fp);
+
+ return 0;
+}
+
+static int command_inject_to(rad_listen_t *listener, int argc, char *argv[])
+{
+ int port;
+ RAD_LISTEN_TYPE type;
+ fr_command_socket_t *sock = listener->data;
+ fr_ipaddr_t ipaddr;
+ rad_listen_t *found = NULL;
+
+ if (argc < 1) {
+ cprintf(listener, "ERROR: Must specify [auth/acct]\n");
+ return 0;
+ }
+
+ if (strcmp(argv[0], "auth") == 0) {
+ type = RAD_LISTEN_AUTH;
+
+ } else if (strcmp(argv[0], "acct") == 0) {
+#ifdef WITH_ACCOUNTING
+ type = RAD_LISTEN_ACCT;
+#else
+ cprintf(listener, "ERROR: This server was built without accounting support.\n");
+ return 0;
+#endif
+
+ } else {
+ cprintf(listener, "ERROR: Unknown socket type\n");
+ return 0;
+ }
+
+ if (argc < 3) {
+ cprintf(listener, "ERROR: No <ipaddr> <port> was given\n");
+ return 0;
+ }
+
+ /*
+ * FIXME: Look for optional arg 4, and bind interface.
+ */
+
+ if (ip_hton(argv[1], AF_UNSPEC, &ipaddr) < 0) {
+ cprintf(listener, "ERROR: Failed parsing IP address; %s\n",
+ fr_strerror());
+ return 0;
+ }
+ port = atoi(argv[2]);
+
+ found = listener_find_byipaddr(&ipaddr, port);
+ if (!found) {
+ cprintf(listener, "ERROR: Could not find matching listener\n");
+ return 0;
+ }
+
+ sock->inject_listener = found;
+ sock->dst_ipaddr = ipaddr;
+ sock->dst_port = port;
+
+ return 1;
+}
+
+static int command_inject_from(rad_listen_t *listener, int argc, char *argv[])
+{
+ RADCLIENT *client;
+ fr_command_socket_t *sock = listener->data;
+
+ if (argc < 1) {
+ cprintf(listener, "ERROR: No <ipaddr> was given\n");
+ return 0;
+ }
+
+ if (!sock->inject_listener) {
+ cprintf(listener, "ERROR: You must specify \"inject to\" before using \"inject from\"\n");
+ return 0;
+ }
+
+ sock->src_ipaddr.af = AF_UNSPEC;
+ if (ip_hton(argv[0], AF_UNSPEC, &sock->src_ipaddr) < 0) {
+ cprintf(listener, "ERROR: Failed parsing IP address; %s\n",
+ fr_strerror());
+ return 0;
+ }
+
+ client = client_listener_find(sock->inject_listener, &sock->src_ipaddr,
+ 0);
+ if (!client) {
+ cprintf(listener, "ERROR: No such client %s\n", argv[0]);
+ return 0;
+ }
+ sock->inject_client = client;
+
+ return 1;
+}
+
+static int command_inject_file(rad_listen_t *listener, int argc, char *argv[])
+{
+ static int inject_id = 0;
+ int filedone;
+ fr_command_socket_t *sock = listener->data;
+ rad_listen_t *fake;
+ REQUEST *request = NULL;
+ RADIUS_PACKET *packet;
+ VALUE_PAIR *vp;
+ FILE *fp;
+ RAD_REQUEST_FUNP fun = NULL;
+ char buffer[2048];
+
+ if (argc < 2) {
+ cprintf(listener, "ERROR: You must specify <input-file> <output-file>\n");
+ return 0;
+ }
+
+ /*
+ * Output files always go to the logging directory.
+ */
+ snprintf(buffer, sizeof(buffer), "%s/%s", radlog_dir, argv[1]);
+
+ fp = fopen(argv[0], "r");
+ if (!fp ) {
+ cprintf(listener, "ERROR: Failed opening %s: %s\n",
+ argv[0], strerror(errno));
+ return 0;
+ }
+
+ vp = readvp2(fp, &filedone, "");
+ fclose(fp);
+ if (!vp) {
+ cprintf(listener, "ERROR: Failed reading attributes from %s: %s\n",
+ argv[0], fr_strerror());
+ return 0;
+ }
+
+ fake = rad_malloc(sizeof(*fake));
+ memcpy(fake, sock->inject_listener, sizeof(*fake));
+
+ /*
+ * Re-write the IO for the listener.
+ */
+ fake->encode = null_socket_dencode;
+ fake->decode = null_socket_dencode;
+ fake->send = null_socket_send;
+
+ packet = rad_alloc(0);
+ packet->src_ipaddr = sock->src_ipaddr;
+ packet->src_port = 0;
+
+ packet->dst_ipaddr = sock->dst_ipaddr;
+ packet->dst_port = sock->dst_port;
+ packet->vps = vp;
+ packet->id = inject_id++;
+
+ if (fake->type == RAD_LISTEN_AUTH) {
+ packet->code = PW_AUTHENTICATION_REQUEST;
+ fun = rad_authenticate;
+
+ } else {
+#ifdef WITH_ACCOUNTING
+ packet->code = PW_ACCOUNTING_REQUEST;
+ fun = rad_accounting;
+#else
+ cprintf(listener, "ERROR: This server was built without accounting support.\n");
+ rad_free(&packet);
+ free(fake);
+ return 0;
+#endif
+ }
+
+ if (!received_request(fake, packet, &request, sock->inject_client)) {
+ cprintf(listener, "ERROR: Failed to inject request. See log file for details\n");
+ rad_free(&packet);
+ free(fake);
+ return 0;
+ }
+
+ /*
+ * Remember what the output file is, and remember to
+ * delete the fake listener when done.
+ */
+ request_data_add(request, null_socket_send, 0, strdup(buffer), free);
+ request_data_add(request, null_socket_send, 1, fake, free);
+
+ if (debug_flag) {
+ request->radlog(L_DBG, 0, request,
+ "Injected %s packet from host %s port 0 code=%d, id=%d",
+ fr_packet_codes[packet->code],
+ inet_ntop(packet->src_ipaddr.af,
+ &packet->src_ipaddr.ipaddr,
+ buffer, sizeof(buffer)),
+ packet->code, packet->id);
+
+ for (vp = packet->vps; vp != NULL; vp = vp->next) {
+ vp_prints(buffer, sizeof(buffer), vp);
+ request->radlog(L_DBG, 0, request, "\t%s", buffer);
+ }
+ }
+
+ /*
+ * And go process it.
+ */
+ thread_pool_addrequest(request, fun);
+
+ return 1;
+}
+
+
+static fr_command_table_t command_table_inject[] = {
+ { "to", FR_WRITE,
+ "inject to <ipaddr> <port> - Inject packets to the destination IP and port.",
+ command_inject_to, NULL },
+
+ { "from", FR_WRITE,
+ "inject from <ipaddr> - Inject packets as if they came from <ipaddr>",
+ command_inject_from, NULL },
+
+ { "file", FR_WRITE,
+ "inject file <input-file> <output-file> - Inject packet from input-file>, with results sent to <output-file>",
+ command_inject_file, NULL },
+
+ { NULL, 0, NULL, NULL, NULL }
+};
+
+static fr_command_table_t command_table_debug[] = {
+ { "condition", FR_WRITE,
+ "debug condition [condition] - Enable debugging for requests matching [condition]",
+ command_debug_condition, NULL },
+
+ { "level", FR_WRITE,
+ "debug level <number> - Set debug level to <number>. Higher is more debugging.",
+ command_debug_level, NULL },
+
+ { "file", FR_WRITE,
+ "debug file [filename] - Send all debugging output to [filename]",
+ command_debug_file, NULL },
+
+ { NULL, 0, NULL, NULL, NULL }
+};
+
+static fr_command_table_t command_table_show_debug[] = {
+ { "condition", FR_READ,
+ "show debug condition - Shows current debugging condition.",
+ command_show_debug_condition, NULL },
+
+ { "level", FR_READ,
+ "show debug level - Shows current debugging level.",
+ command_show_debug_level, NULL },
+
+ { "file", FR_READ,
+ "show debug file - Shows current debugging file.",
+ command_show_debug_file, NULL },
+
+ { NULL, 0, NULL, NULL, NULL }
+};
+
+static fr_command_table_t command_table_show_module[] = {
+ { "config", FR_READ,
+ "show module config <module> - show configuration for given module",
+ command_show_module_config, NULL },
+ { "flags", FR_READ,
+ "show module flags <module> - show other module properties",
+ command_show_module_flags, NULL },
+ { "list", FR_READ,
+ "show module list - shows list of loaded modules",
+ command_show_modules, NULL },
+ { "methods", FR_READ,
+ "show module methods <module> - show sections where <module> may be used",
+ command_show_module_methods, NULL },
+
+ { NULL, 0, NULL, NULL, NULL }
+};
+
+static fr_command_table_t command_table_show_client[] = {
+ { "config", FR_READ,
+ "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",
+ command_show_clients, NULL },
+
+ { NULL, 0, NULL, NULL, NULL }
+};
+
+#ifdef WITH_PROXY
+static fr_command_table_t command_table_show_home[] = {
+ { "config", FR_READ,
+ "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> [proto] - shows state of given home server",
+ command_show_home_server_state, NULL },
+
+ { NULL, 0, NULL, NULL, NULL }
+};
+#endif
+
+
+static fr_command_table_t command_table_show[] = {
+ { "client", FR_READ,
+ "show client <command> - do sub-command of client",
+ NULL, command_table_show_client },
+ { "debug", FR_READ,
+ "show debug <command> - show debug properties",
+ NULL, command_table_show_debug },
+#ifdef WITH_PROXY
+ { "home_server", FR_READ,
+ "show home_server <command> - do sub-command of home_server",
+ NULL, command_table_show_home },
+#endif
+ { "module", FR_READ,
+ "show module <command> - do sub-command of module",
+ NULL, command_table_show_module },
+ { "uptime", FR_READ,
+ "show uptime - shows time at which server started",
+ command_uptime, NULL },
+ { "version", FR_READ,
+ "show version - Prints version of the running server",
+ command_show_version, NULL },
+ { "xml", FR_READ,
+ "show xml <reference> - Prints out configuration as XML",
+ command_show_xml, NULL },
+ { NULL, 0, NULL, NULL, NULL }
+};
+
+
+static int command_set_module_config(rad_listen_t *listener, int argc, char *argv[])
+{
+ int i, rcode;
+ CONF_PAIR *cp;
+ CONF_SECTION *cs;
+ module_instance_t *mi;
+ const CONF_PARSER *variables;
+ void *data;
+
+ if (argc < 3) {
+ cprintf(listener, "ERROR: No module name or variable 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 ((mi->entry->module->type & RLM_TYPE_HUP_SAFE) == 0) {
+ cprintf(listener, "ERROR: Cannot change configuration of module as it is cannot be HUP'd.\n");
+ return 0;
+ }
+
+ variables = cf_section_parse_table(mi->cs);
+ if (!variables) {
+ cprintf(listener, "ERROR: Cannot find configuration for module\n");
+ return 0;
+ }
+
+ rcode = -1;
+ for (i = 0; variables[i].name != NULL; i++) {
+ /*
+ * FIXME: Recurse into sub-types somehow...
+ */
+ if (variables[i].type == PW_TYPE_SUBSECTION) continue;
+
+ if (strcmp(variables[i].name, argv[1]) == 0) {
+ rcode = i;
+ break;
+ }
+ }
+
+ if (rcode < 0) {
+ cprintf(listener, "ERROR: No such variable \"%s\"\n", argv[1]);
+ return 0;
+ }
+
+ i = rcode; /* just to be safe */
+
+ /*
+ * It's not part of the dynamic configuration. The module
+ * needs to re-parse && validate things.
+ */
+ if (variables[i].data) {
+ cprintf(listener, "ERROR: Variable cannot be dynamically updated\n");
+ return 0;
+ }
+
+ data = ((char *) mi->insthandle) + variables[i].offset;
+
+ cp = cf_pair_find(mi->cs, argv[1]);
+ if (!cp) return 0;
+
+ /*
+ * Replace the OLD value in the configuration file with
+ * the NEW value.
+ *
+ * FIXME: Parse argv[2] depending on it's data type!
+ * If it's a string, look for leading single/double quotes,
+ * end then call tokenize functions???
+ */
+ cf_pair_replace(mi->cs, cp, argv[2]);
+
+ rcode = cf_item_parse(mi->cs, argv[1], variables[i].type,
+ data, argv[2]);
+ if (rcode < 0) {
+ cprintf(listener, "ERROR: Failed to parse value\n");
+ return 0;
+ }
+
+ 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%u\n", stats->total_requests);
+ cprintf(listener, "\tresponses\t%u\n", stats->total_responses);
+
+ if (auth) {
+ cprintf(listener, "\taccepts\t\t%u\n",
+ stats->total_access_accepts);
+ cprintf(listener, "\trejects\t\t%u\n",
+ stats->total_access_rejects);
+ cprintf(listener, "\tchallenges\t%u\n",
+ stats->total_access_challenges);
+ }
+
+ 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[])
+{
+ home_server *home;
+
+ if (argc == 0) {
+ cprintf(listener, "ERROR: Must specify [auth/acct] OR <ipaddr> <port>\n");
+ return 0;
+ }
+
+ if (argc == 1) {
+#ifdef WITH_ACCOUNTING
+ if (strcmp(argv[0], "acct") == 0) {
+ return command_print_stats(listener,
+ &proxy_acct_stats, 0);
+ }
+#endif
+ if (strcmp(argv[0], "auth") == 0) {
+ return command_print_stats(listener,
+ &proxy_auth_stats, 1);
+ }
+
+ cprintf(listener, "ERROR: Should specify [auth/acct]\n");
+ return 0;
+ }
+
+ home = get_home_server(listener, argc, argv, NULL);
+ if (!home) {
+ return 0;
+ }
+
+ command_print_stats(listener, &home->stats,
+ (home->type == HOME_TYPE_AUTH));
+ cprintf(listener, "\toutstanding\t%d\n", home->currently_outstanding);
+ return 1;
+}
+#endif
+
+static int command_stats_client(rad_listen_t *listener, int argc, char *argv[])
+{
+ int auth = TRUE;
+ RADCLIENT *client;
+
+ if (argc < 1) {
+ cprintf(listener, "ERROR: Must specify [auth/acct]\n");
+ return 0;
+ }
+
+ if (strcmp(argv[0], "auth") == 0) {
+ auth = TRUE;
+
+ } else if (strcmp(argv[0], "acct") == 0) {
+#ifdef WITH_ACCOUNTING
+ auth = FALSE;
+#else
+ cprintf(listener, "ERROR: This server was built without accounting support.\n");
+ return 0;
+#endif
+
+ } else {
+ cprintf(listener, "ERROR: Unknown statistics type\n");
+ return 0;
+ }
+
+ /*
+ * Global results for all client.
+ */
+ if (argc == 1) {
+#ifdef WITH_ACCOUNTING
+ if (!auth) {
+ return command_print_stats(listener,
+ &radius_acct_stats, auth);
+ }
+#endif
+ return command_print_stats(listener, &radius_auth_stats, auth);
+ }
+
+ client = get_client(listener, argc - 1, argv + 1);
+ if (!client) {
+ return 0;
+ }
+
+#ifdef WITH_ACCOUNTING
+ if (!auth) {
+ return command_print_stats(listener, client->acct, auth);
+ }
+#endif
+
+ return command_print_stats(listener, client->auth, auth);
+}
+
+
+static int command_add_client_file(rad_listen_t *listener, int argc, char *argv[])
+{
+ RADCLIENT *c;
+
+ if (argc < 1) {
+ cprintf(listener, "ERROR: <file> is required\n");
+ return 0;
+ }
+
+ /*
+ * Read the file and generate the client.
+ */
+ c = client_read(argv[0], FALSE, FALSE);
+ if (!c) {
+ cprintf(listener, "ERROR: Unknown error reading client file.\n");
+ return 0;
+ }
+
+ if (!client_add(NULL, c)) {
+ cprintf(listener, "ERROR: Unknown error inserting new client.\n");
+ client_free(c);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+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>",
+ command_add_client_file, NULL },
+
+ { NULL, 0, NULL, NULL, NULL }
+};
+
+
+static fr_command_table_t command_table_add[] = {
+ { "client", FR_WRITE,
+ "add client <command> - Add client configuration commands",
+ NULL, command_table_add_client },
+
+ { NULL, 0, NULL, NULL, NULL }
+};
+
+
+#ifdef WITH_PROXY
+static fr_command_table_t command_table_set_home[] = {
+ { "state", FR_WRITE,
+ "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 }
+};
+#endif
+
+static fr_command_table_t command_table_set_module[] = {
+ { "config", FR_WRITE,
+ "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 }
+};
+
+
+static fr_command_table_t command_table_set[] = {
+ { "module", FR_WRITE,
+ "set module <command> - set module commands",
+ NULL, command_table_set_module },
+#ifdef WITH_PROXY
+ { "home_server", FR_WRITE,
+ "set home_server <command> - set home server commands",
+ NULL, command_table_set_home },
+#endif
+
+ { NULL, 0, NULL, NULL, NULL }
+};
+
+
+static fr_command_table_t command_table_stats[] = {
+ { "client", FR_READ,
+ "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,
+ "stats home_server [<ipaddr>/auth/acct] <port> - show statistics for given home server (ipaddr and port), or for all home servers (auth or acct)",
+ 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 }
+};
+
+static fr_command_table_t command_table[] = {
+ { "add", FR_WRITE, NULL, NULL, command_table_add },
+ { "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 },
+ { "inject", FR_WRITE,
+ "inject <command> - commands to inject packets into a running server",
+ NULL, command_table_inject },
+ { "reconnect", FR_READ,
+ "reconnect - reconnect to a running server",
+ NULL, NULL }, /* just here for "help" */
+ { "terminate", FR_WRITE,
+ "terminate - terminates the server, and cause it to exit",
+ command_terminate, NULL },
+ { "set", FR_WRITE, NULL, NULL, command_table_set },
+ { "show", FR_READ, NULL, NULL, command_table_show },
+ { "stats", FR_READ, NULL, NULL, command_table_stats },
+
+ { NULL, 0, NULL, NULL, NULL }
+};
+
+
+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.
+ *
+ * FIXME: TCP + SSL, after RadSec is in.
+ */
+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;
+
+ pw = getpwnam(sock->uid_name);
+ if (!pw) {
+ radlog(L_ERR, "Failed getting uid for %s: %s",