The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
radmin.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: d61a9887bc9afc7ccbcdf3014e8059498660148f $
19 *
20 * @file src/bin/radmin.c
21 * @brief Internal implementation of radmin
22 *
23 * @copyright 2018 The FreeRADIUS server project
24 * @copyright 2018 Alan DeKok (aland@freeradius.org)
25 */
26RCSID("$Id: d61a9887bc9afc7ccbcdf3014e8059498660148f $")
27
28#include <freeradius-devel/io/thread.h>
29#include <freeradius-devel/server/base.h>
30#include <freeradius-devel/util/debug.h>
31#include <freeradius-devel/server/radmin.h>
32
33#include <freeradius-devel/util/misc.h>
34#include <freeradius-devel/util/socket.h>
35
36#ifdef HAVE_LIBREADLINE
37
38/*
39 * Readline headers aren't compliant
40 */
41DIAG_OFF(strict-prototypes)
42#if defined(HAVE_READLINE_READLINE_H)
43# include <readline/readline.h>
44# define USE_READLINE (1)
45#elif defined(HAVE_READLINE_H)
46# include <readline.h>
47# define USE_READLINE (1)
48#endif /* !defined(HAVE_READLINE_H) */
49
50#ifdef HAVE_READLINE_HISTORY
51# if defined(HAVE_READLINE_HISTORY_H)
52# include <readline/history.h>
53# define USE_READLINE_HISTORY (1)
54# elif defined(HAVE_HISTORY_H)
55# include <history.h>
56# define USE_READLINE_HISTORY (1)
57#endif /* defined(HAVE_READLINE_HISTORY_H) */
58#endif /* HAVE_READLINE_HISTORY */
59#endif /* HAVE_LIBREADLINE */
60DIAG_ON(strict-prototypes)
61
62#ifdef HAVE_GPERFTOOLS_PROFILER_H
63#include <gperftools/profiler.h>
64#endif
65
66static pthread_t cli_pthread_id;
67static bool cli_started = false;
68static bool stop = false;
69static int context = 0;
71static TALLOC_CTX *radmin_ctx = NULL;
72static FILE *my_stdin = NULL;
73static FILE *my_stdout = NULL;
74static FILE *my_stderr = NULL;
75
76#ifndef USE_READLINE
77/*
78 * @todo - use thread-local storage
79 */
80static char readline_buffer[1024];
81
82static char *readline(char const *prompt)
83{
84 char *line, *p;
85
86 if (prompt && *prompt) puts(prompt);
87 fflush(my_stdout);
88
89 line = fgets(readline_buffer, sizeof(readline_buffer), stdin);
90 if (!line) return NULL;
91
92 p = strchr(line, '\n');
93 if (!p) {
94 fprintf(my_stderr, "Input line too long\n");
95 return NULL;
96 }
97
98 *p = '\0';
99
100 /*
101 * Strip off leading spaces.
102 */
103 for (p = line; *p != '\0'; p++) {
104 if ((p[0] == ' ') ||
105 (p[0] == '\t')) {
106 line = p + 1;
107 continue;
108 }
109
110 if (p[0] == '#') {
111 line = NULL;
112 break;
113 }
114
115 break;
116 }
117
118 /*
119 * Comments: keep going.
120 */
121 if (!line) return line;
122
123 /*
124 * Strip off CR / LF
125 */
126 for (p = line; *p != '\0'; p++) {
127 if ((p[0] == '\r') ||
128 (p[0] == '\n')) {
129 p[0] = '\0';
130 break;
131 }
132 }
133
134 return line;
135}
136#define radmin_free(_x)
137#else
138#define radmin_free free
139#endif
140
141#ifndef USE_READLINE_HISTORY
142static void add_history(UNUSED char *line)
143{
144}
145#endif
146
147static fr_cmd_t *radmin_cmd = NULL;
148static char *radmin_partial_line = NULL;
149static char *radmin_buffer = NULL;
150
151#define CMD_MAX_ARGV (32)
152#define CMD_MAX_EXPANSIONS (128)
153
154static int cmd_exit(FILE *fp, FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info);
156
157#ifdef USE_READLINE
158/*
159 * Global variables because readline() is stupid.
160 */
161static int radmin_num_expansions;
162static char *radmin_expansions[CMD_MAX_EXPANSIONS] = {0};
163
164static char *
165radmin_expansion_walk(UNUSED const char *text, int state)
166{
167 static int current;
168 char *name;
169
170 if (!state) {
171 current = 0;
172 }
173
174 /*
175 * fr_command_completions() takes care of comparing things to
176 * suppress expansions which don't match "text"
177 */
178 if (current >= radmin_num_expansions) return NULL;
179
180 name = radmin_expansions[current];
181 radmin_expansions[current++] = NULL;
182 return name;
183}
184
185
186static char **
187radmin_completion(const char *text, int start, UNUSED int end)
188{
189 int num;
190 size_t offset;
191 char **expansions = &radmin_expansions[0];
192 char const **expansions_const;
193
194 rl_attempted_completion_over = 1;
195
196 fr_assert(radmin_buffer != NULL);
200
202
203 strlcpy(radmin_partial_line, rl_line_buffer, 8192 - offset);
204
205 memcpy(&expansions_const, &expansions, sizeof(expansions)); /* const issues */
206 num = fr_command_complete(radmin_cmd, radmin_buffer, start + offset,
207 CMD_MAX_EXPANSIONS, expansions_const);
208 if (num <= 0) return NULL;
209
210 radmin_num_expansions = num;
211
212 return rl_completion_matches(text, radmin_expansion_walk);
213}
214
215static int radmin_help(UNUSED int count, UNUSED int key)
216{
217 size_t offset;
218 fprintf(my_stdout, "\n");
219
221 strlcpy(radmin_partial_line, rl_line_buffer, 8192 - offset);
222
224 rl_on_new_line();
225 return 0;
226}
227
228#endif /* USE_READLINE */
229
230
231static void *fr_radmin(UNUSED void *input_ctx)
232{
233 int argc = 0;
234 int *context_exit, *context_offset;
235 char const *prompt;
236 size_t size;
237 TALLOC_CTX *ctx;
238 fr_cmd_info_t *info = &radmin_info;
239
240 context = 0;
241 prompt = "radmin> ";
242
243 ctx = talloc_init_const("radmin");
244
245 size = 8192;
246 radmin_buffer = talloc_zero_array(ctx, char, size);
247
248 fr_command_info_init(ctx, info);
249
250 context_exit = talloc_zero_array(ctx, int, CMD_MAX_ARGV + 1);
251 context_offset = talloc_zero_array(ctx, int, CMD_MAX_ARGV + 1);
252 context_offset[0] = 0;
253
254 fflush(my_stdout);
255
256#ifdef USE_READLINE
257 rl_attempted_completion_function = radmin_completion;
258
259 (void) rl_bind_key('?', radmin_help);
260#endif
261
262 while (true) {
263 char *line;
264
265 fr_assert(context >= 0);
266 fr_assert(context_offset[context] >= 0);
267 radmin_partial_line = radmin_buffer + context_offset[context];
268 line = readline(prompt);
269 if (stop) break;
270
271 /*
272 * NULL means "read hit EOF"
273 */
274 if (!line) {
275 INFO("radmin input was closed - exiting");
277 break;
278 }
279
280 /*
281 * non-NULL, but empty line means "no input"
282 */
283 if (!*line) {
285 continue;
286 }
287
288 /*
289 * Special-case commands in sub-contexts.
290 */
291 if (context > 0) {
292 /*
293 * Special-case "quit", which works everywhere.
294 * It closes the CLI immediately.
295 */
296 if (strcmp(line, "quit") == 0) {
297 cmd_exit(my_stdout, my_stderr, NULL, info);
298 goto next;
299 }
300
301 /*
302 * Allow exiting from the current context.
303 */
304 if (strcmp(line, "exit") == 0) {
305 talloc_const_free(prompt);
306 context = context_exit[context];
307 if (context == 0) {
308 prompt = "radmin> ";
309 } else {
310 prompt = talloc_asprintf(ctx, "... %s> ", info->argv[context - 1]);
311 }
312 info->runnable = false;
313 goto next;
314 }
315 }
316
317 /*
318 * "line" is dynamically allocated and we don't
319 * want argv[] pointing to it. Also, splitting
320 * the line mangles it in-place. So we need to
321 * copy the line to "current_str" for splitting.
322 * We also copy it to "current_line" for adding
323 * to the history.
324 *
325 * @todo - we need a smart history which adds the
326 * FULL line to the history, and then on
327 * up-arrow, only produces the RELEVANT line from
328 * the current context.
329 */
330 strlcpy(radmin_buffer + context_offset[context], line,
331 size - context_offset[context]);
333
334 /*
335 * Parse error! Oops..
336 */
337 if (argc < 0) {
338 fr_perror("Failed parsing line");
339 add_history(line); /* let them up-arrow and retype it */
340 goto next;
341 }
342
343 /*
344 * Skip blank lines.
345 */
346 if (argc == context) {
348 continue;
349 }
350
351 /*
352 * It's a partial command. Add it to the context
353 * and continue.
354 *
355 * Note that we have to update `current_str`, because
356 * argv[context] currently points there...
357 */
358 if (!info->runnable) {
359 size_t len;
360
361 fr_assert(argc > 0);
362 len = strlen(line);
363
364 /*
365 * Not enough room for more commands, refuse to do it.
366 */
367 if ((context_offset[context] + len + 80) >= size) {
368 fprintf(my_stderr, "Too many commands!\n");
369 goto next;
370 }
371
372 /*
373 * Move the pointer down the buffer and
374 * keep reading more.
375 */
376
377 if (context > 0) {
378 talloc_const_free(prompt);
379 }
380
381 /*
382 * Remember how many arguments we
383 * added in this context, and go back up
384 * that number of arguments when entering
385 * 'exit'.
386 *
387 * Otherwise, entering a partial command
388 * "foo bar baz" would require you to
389 * type "exit" 3 times in order to get
390 * back to the root.
391 */
392 context_exit[argc] = context;
393 context_offset[argc] = context_offset[context] + len + 1;
394 radmin_buffer[context_offset[context] + len] = ' ';
395 context = argc;
396 prompt = talloc_asprintf(ctx, "... %s> ", info->argv[context - 1]);
397 goto next;
398 }
399
400 /*
401 * Else it's a info->runnable command. Add it to the
402 * history.
403 */
405
406 if (fr_command_run(my_stdout, my_stderr, info, false) < 0) {
407 fprintf(my_stderr, "Failed running command.\n");
408 }
409
410 next:
411 /*
412 * Reset this to the current context.
413 */
414 if (fr_command_clear(context, info) < 0) {
415 fr_perror("Failing clearing buffers");
416 break;
417 }
418
420
421 if (stop) break;
422 }
423
424 fclose(my_stdin);
425 fclose(my_stdout);
426 fclose(my_stderr);
427
428 talloc_free(ctx);
429 radmin_buffer = NULL;
430 radmin_partial_line = NULL;
431
432 return NULL;
433}
434
435
436/** radmin functions, tables, and callbacks
437 *
438 */
440
441static int cmd_exit(UNUSED FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, UNUSED fr_cmd_info_t const *info)
442{
444 stop = true;
445
446 return 0;
447}
448
449static int cmd_help(FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
450{
451 int max = 1;
452 int options = FR_COMMAND_OPTION_HELP;
453
454 if (info->argc > 0) {
455 if (strcmp(info->argv[0], "all") == 0) {
456 max = CMD_MAX_ARGV;
457 }
458 else if (strcmp(info->argv[0], "commands") == 0) {
459 max = CMD_MAX_ARGV;
460 options = FR_COMMAND_OPTION_NONE;
461 }
462 }
463
464 fr_command_list(fp, max, radmin_cmd, options);
465
466 return 0;
467}
468
469static int cmd_terminate(UNUSED FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, UNUSED fr_cmd_info_t const *info)
470{
472 return 0;
473}
474
475static int cmd_uptime(FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, UNUSED fr_cmd_info_t const *info)
476{
477 fr_time_delta_t uptime;
478
479 uptime = fr_time_sub(fr_time(), start_time);
480
481 fr_fprintf(fp, "Uptime: %pVs seconds\n", fr_box_time_delta(uptime));
482
483 return 0;
484}
485
486static int cmd_stats_memory(FILE *fp, FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
487{
489 fprintf(fp, "Statistics are only available when the server is started with '-M'.\n");
490 return -1;
491 }
492
493 if (strcmp(info->argv[0], "total") == 0) {
494 fprintf(fp, "%zd\n", talloc_total_size(NULL));
495 return 0;
496 }
497
498 if (strcmp(info->argv[0], "blocks") == 0) {
499 fprintf(fp, "%zd\n", talloc_total_blocks(NULL));
500 return 0;
501 }
502
503 if (strcmp(info->argv[0], "full") == 0) {
504 fprintf(fp, "see stdout of the server for the full report.\n");
506 return 0;
507 }
508
509 /*
510 * Should never reach here. The command parser will
511 * ensure that.
512 */
513 fprintf(fp_err, "Must use 'stats memory (blocks|full|total)'\n");
514 return -1;
515}
516
517static int cmd_set_debug_level(UNUSED FILE *fp, FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
518{
519 int level = atoi(info->argv[0]);
520
521 if ((level < 0) || level > 5) {
522 fprintf(fp_err, "Invalid debug level '%s'\n", info->argv[0]);
523 return -1;
524 }
525
526 fr_debug_lvl = level;
527 return 0;
528}
529
530static int cmd_show_debug_level(FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, UNUSED fr_cmd_info_t const *info)
531{
532 fprintf(fp, "%d\n", fr_debug_lvl);
533 return 0;
534}
535
536#ifdef HAVE_GPERFTOOLS_PROFILER_H
537static int cmd_set_profile_status(UNUSED FILE *fp, FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
538{
539 fr_value_box_t box;
540 struct ProfilerState state;
541
542 if (fr_value_box_from_str(NULL, &box, FR_TYPE_BOOL, NULL,
543 info->argv[0], strlen(info->argv[0]),
544 NULL) <= 0) {
545 fprintf(fp_err, "Failed setting profile status '%s' - %s\n", info->argv[0], fr_strerror());
546 return -1;
547 }
548
549 ProfilerGetCurrentState(&state);
550
551 if (box.vb_bool) {
552 char *filename;
553
554 if (state.enabled) {
555 fprintf(fp_err, "Profiling is already on, to file %s\n", state.profile_name);
556 return -1;
557 }
558
559 if (info->argc >= 2) {
560 filename = UNCONST(char *, info->argv[1]);
561 } else {
562 filename = getenv("FR_PROFILE_FILENAME");
563 }
564
565 if (filename) {
566 ProfilerStart(filename);
567 } else {
568 pid_t pid = getpid();
569 MEM(filename = talloc_asprintf(NULL, "/tmp/freeradius-profile.%u.prof", pid));
570 ProfilerStart(filename);
571 talloc_free(filename);
572 }
573
574 } else if (state.enabled) {
575 ProfilerFlush();
576 ProfilerStop();
577 }
578 /*
579 * Else profiling is already off, allow the admin to turn
580 * it off again without producing an error
581 */
582
583 return 0;
584}
585
586static int cmd_show_profile_status(FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, UNUSED fr_cmd_info_t const *info)
587{
588 struct ProfilerState state;
589 ProfilerGetCurrentState(&state);
590
591 if (!state.enabled) {
592 fprintf(fp, "off\n");
593 return 0;
594 }
595
596 fprintf(fp, "on %s\n", state.profile_name);
597 return 0;
598}
599#endif
600
601static int tab_expand_config_thing(TALLOC_CTX *talloc_ctx, UNUSED void *ctx, fr_cmd_info_t *info, int max_expansions, char const **expansions,
602 bool want_section)
603{
604 int count;
605 size_t reflen, offset;
606 char *ref;
607 char const *text;
608 CONF_ITEM *ci;
609 CONF_SECTION *cs;
610
611 if (info->argc <= 0) return 0;
612
613 ref = talloc_strdup(talloc_ctx, info->argv[info->argc - 1]);
614 text = strrchr(ref, '.');
615 if (!text) {
617 reflen = 0;
618 offset = 0;
619 text = ref;
620
621 /*
622 * If it's a good ref, use that for expansions.
623 */
625 if (ci && cf_item_is_section(ci)) {
626 cs = cf_item_to_section(ci);
627 text = "";
628 reflen = strlen(ref);
629 offset = 1;
630 }
631
632 } else {
633 reflen = (text - ref);
634 offset = 1;
635 ref[reflen] = '\0';
636 text++;
637
639 if (!ci) {
640 none:
641 talloc_free(ref);
642 return 0;
643 }
644
645 /*
646 * The ref is to a pair. Don't allow further
647 * expansions.
648 */
649 if (cf_item_is_pair(ci)) goto none;
650 cs = cf_item_to_section(ci);
651 }
652
653 count = 0;
654
655 /*
656 * Walk the reference, allowing for additional expansions.
657 */
658 for (ci = cf_item_next(cs, NULL);
659 ci != NULL;
660 ci = cf_item_next(cs, ci)) {
661 char const *name1, *check;
662 char *str;
663 char buffer[256];
664
665 /*
666 * @todo - if we want a config pair, AND we have
667 * partial input, THEN check if the section name
668 * matches the partial input. If so, allow it as
669 * an expansion.
670 */
671 if (cf_item_is_section(ci)) {
672 char const *name2;
673
676
677 if (name2) {
678 snprintf(buffer, sizeof(buffer), "%s[%s]", name1, name2);
679 check = buffer;
680 } else {
681 check = name1;
682 }
683
684 if (!want_section) {
685 if (*text && fr_command_strncmp(text, check)) {
686 // @todo - expand the pairs in this section
687 goto add;
688 }
689
690 continue;
691 }
692
693 } else if (!cf_item_is_pair(ci)) {
694 continue;
695
696 } else {
697 if (want_section) continue;
698
699 name1 = cf_pair_attr(cf_item_to_pair(ci));
700 check = name1;
701 }
702
703 /*
704 * Check for a matching name.
705 */
706 if (!fr_command_strncmp(text, check)) continue;
707
708 add:
709 MEM(expansions[count] = str = malloc(reflen + strlen(check) + offset + 1));
710 memcpy(str, ref, reflen);
711 str[reflen] = '.';
712 strcpy(str + reflen + offset, check);
713
714 count++;
715 if (count >= max_expansions) return count;
716 }
717
718 return count;
719}
720
721static int cmd_show_config_section(FILE *fp, FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
722{
724
725 fr_assert(info->argc > 0);
726
728 info->box[0]->vb_strvalue);
729 if (!item || !cf_item_is_section(item)) {
730 fprintf(fp_err, "No such configuration section.\n");
731 return -1;
732 }
733
735
736 return 0;
737}
738
739
740static int tab_expand_config_section(TALLOC_CTX *talloc_ctx, void *ctx, fr_cmd_info_t *info, int max_expansions, char const **expansions)
741{
742 return tab_expand_config_thing(talloc_ctx, ctx, info, max_expansions, expansions, true);
743}
744
745static int tab_expand_config_item(TALLOC_CTX *talloc_ctx, void *ctx, fr_cmd_info_t *info, int max_expansions, char const **expansions)
746{
747 return tab_expand_config_thing(talloc_ctx, ctx, info, max_expansions, expansions, false);
748}
749
750static int cmd_show_config_item(FILE *fp, FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
751{
752 fr_token_t token;
754 CONF_PAIR *cp;
755
756 fr_assert(info->argc > 0);
757
759 info->box[0]->vb_strvalue);
760 if (!item || !cf_item_is_pair(item)) {
761 fprintf(fp_err, "No such configuration item.\n");
762 return -1;
763 }
764
765 cp = cf_item_to_pair(item);
766 token = cf_pair_value_quote(cp);
767
768 if (token == T_BARE_WORD) {
769 bare:
770 fprintf(fp, "%s\n", cf_pair_value(cp));
771 } else {
772 char quote;
773 char *value;
774
775 switch (token) {
777 quote = '"';
778 break;
779
781 quote = '\'';
782 break;
783
785 quote = '`';
786 break;
787
788 default:
789 goto bare;
790 }
791
792 value = fr_asprint(NULL, cf_pair_value(cp), -1, quote);
793 fprintf(fp, "%c%s%c\n", quote, value, quote);
795 }
796
797 return 0;
798}
799
800static int cmd_show_client(FILE *fp, FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
801{
802 fr_client_t *client;
803
804 if (info->argc >= 2) {
805 int proto;
806
807 if (strcmp(info->argv[1], "tcp") == 0) {
808 proto = IPPROTO_TCP;
809
810 } else if (strcmp(info->argv[1], "udp") == 0) {
811 proto = IPPROTO_UDP;
812
813 } else {
814 fprintf(fp_err, "Unknown proto '%s'.\n", info->argv[1]);
815 return -1;
816 }
817
818 client = client_find(NULL, &info->box[0]->vb_ip, proto);
819 if (client) goto found;
820
821 not_found:
822 fprintf(fp_err, "No such client.\n");
823 return -1;
824 } else {
825 client = client_find(NULL, &info->box[0]->vb_ip, IPPROTO_IP); /* hack */
826 if (!client) goto not_found;
827 }
828
829found:
830 fprintf(fp, "shortname\t%s\n", client->shortname);
831 fprintf(fp, "secret\t\t%s\n", client->secret);
832
833 if (client->proto == IPPROTO_UDP) {
834 fprintf(fp, "proto\t\tudp\n");
835
836 } else if (client->proto == IPPROTO_TCP) {
837 fprintf(fp, "proto\t\ttcp\n");
838 } else {
839 fprintf(fp, "proto\t\t*\n");
840 }
841
842 return 0;
843}
844
845//#define CMD_TEST (1)
846
847#ifdef CMD_TEST
848static int cmd_test(FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
849{
850 int i;
851
852 fprintf(fp, "TEST %d\n", info->argc);
853
854 for (i = 0; i < info->argc; i++) {
855 fprintf(fp, "\t%s\n", info->argv[i]);
856 }
857
858 return 0;
859}
860
861static int cmd_test_tab_expand(UNUSED TALLOC_CTX *talloc_ctx, UNUSED void *ctx, fr_cmd_info_t *info, UNUSED int max_expansions, char const **expansions)
862{
863 char const *text;
864 char *p;
865
866 if (info->argc == 0) return 0;
867
868 text = info->argv[info->argc - 1];
869
870 /*
871 * Expand a list of things
872 */
873 if (!*text) {
874 expansions[0] = strdup("0");
875 expansions[1] = strdup("1");
876 return 2;
877 }
878
879 if ((text[0] < '0') || (text[0] > '9')) {
880 return 0;
881 }
882
883 /*
884 * If the user enters a digit, allow it.
885 */
886 expansions[0] = p = malloc(2);
887 p[0] = text[0];
888 p[1] = '\0';
889
890 return 1;
891}
892#endif
893
895 {
896 .name = "exit",
897 .func = cmd_exit,
898 .help = "Exit from the current context.",
899 .read_only = true
900 },
901
902 {
903 .name = "quit",
904 .func = cmd_exit,
905 .help = "Quit and close the command line immediately.",
906 .read_only = true
907 },
908
909 {
910 .name = "help",
911 .syntax = "[(all|commands)]",
912 .func = cmd_help,
913 .help = "Display list of commands and their help text.",
914 .read_only = true
915 },
916
917
918 {
919 .name = "terminate",
920 .func = cmd_terminate,
921 .help = "Terminate the running server and cause it to exit.",
922 .read_only = false
923 },
924
925
926#ifdef CMD_TEST
927 {
928 .parent = "test",
929 .name = "foo"
930 .syntax = "INTEGER",
931 .func = cmd_test,
932 .tab_expand = cmd_test_tab_expand,
933 .help = "test foo INTEGER",
934 .read_only = true,
935 },
936#endif
937
938 {
939 .name = "uptime",
940 .func = cmd_uptime,
941 .help = "Show uptime since the server started.",
942 .read_only = true
943 },
944
945 {
946 .name = "set",
947 .help = "Change settings in the server.",
948 .read_only = false
949 },
950
951 {
952 .name = "show",
953 .help = "Show settings in the server.",
954 .read_only = true
955 },
956
957 {
958 .parent = "show",
959 .name = "config",
960 .help = "Show configuration settings in the server.",
961 .read_only = true
962 },
963
964 {
965 .parent = "show config",
966 .name = "section",
967 .syntax = "STRING",
968 .help = "Show a named configuration section",
970 .tab_expand = tab_expand_config_section,
971 .read_only = true
972 },
973
974 {
975 .parent = "show config",
976 .name = "item",
977 .syntax = "STRING",
978 .help = "Show a named configuration item",
979 .func = cmd_show_config_item,
980 .tab_expand = tab_expand_config_item,
981 .read_only = true
982 },
983
984 {
985 .parent = "show",
986 .name = "client",
987 .help = "Show information about a client or clients.",
988 .read_only = true
989 },
990
991 {
992 .parent = "show client",
993 .name = "config",
994 .syntax = "IPADDR [(udp|tcp)]",
995 .help = "Show the configuration for a given client.",
996 .func = cmd_show_client,
997 .read_only = true
998 },
999
1000 {
1001 .name = "stats",
1002 .help = "Show statistics in the server.",
1003 .read_only = true
1004 },
1005
1006 {
1007 .parent = "stats",
1008 .name = "memory",
1009 .syntax = "(blocks|full|total)",
1010 .func = cmd_stats_memory,
1011 .help = "Show memory statistics.",
1012 .read_only = true,
1013 },
1014
1015 {
1016 .parent = "set",
1017 .name = "debug",
1018 .help = "Change debug settings.",
1019 .read_only = false
1020 },
1021
1022 {
1023 .parent = "set debug",
1024 .name = "level",
1025 .syntax = "INTEGER",
1026 .func = cmd_set_debug_level,
1027 .help = "Change the debug level.",
1028 .read_only = false,
1029 },
1030
1031 {
1032 .parent = "show",
1033 .name = "debug",
1034 .help = "Show debug settings.",
1035 .read_only = true
1036 },
1037
1038 {
1039 .parent = "show debug",
1040 .name = "level",
1041 .func = cmd_show_debug_level,
1042 .help = "show debug level",
1043 .read_only = true,
1044 },
1045
1046#ifdef HAVE_GPERFTOOLS_PROFILER_H
1047 {
1048 .parent = "set",
1049 .name = "profile",
1050 .help = "Change profiler settings.",
1051 .read_only = false
1052 },
1053
1054 {
1055 .parent = "set profile",
1056 .name = "status",
1057 .syntax = "BOOL [STRING]",
1058 .func = cmd_set_profile_status,
1059 .help = "Change the profiler status on/off, and potentially the filename",
1060 .read_only = false,
1061 },
1062
1063 {
1064 .parent = "show",
1065 .name = "profile",
1066 .help = "Show profile settings.",
1067 .read_only = true
1068 },
1069
1070 {
1071 .parent = "show profile",
1072 .name = "status",
1073 .func = cmd_show_profile_status,
1074 .help = "show profile status, including filename if profiling is on.",
1075 .read_only = true,
1076 },
1077#endif
1078
1080};
1081
1082int fr_radmin_start(main_config_t *config, bool cli, int std_fd[static 3])
1083{
1084 radmin_ctx = talloc_init_const("radmin");
1085 if (!radmin_ctx) return -1;
1086
1087 start_time = fr_time();
1088
1089#ifdef USE_READLINE
1090 memcpy(&rl_readline_name, &config->name, sizeof(rl_readline_name)); /* const issues on OSX */
1091#endif
1092
1095
1096 if (fr_radmin_register(radmin_ctx, NULL, NULL, cmd_table) < 0) {
1097 PERROR("Failed initializing radmin");
1098 return -1;
1099 }
1100
1101 if (!cli) return 0;
1102
1103 my_stdin = fdopen(std_fd[STDIN_FILENO], "r");
1104 if (!my_stdin) {
1105 ERROR("Failed initializing radmin stdin %s", fr_syserror(errno));
1106 return -1;
1107 }
1108#ifdef USE_READLINE
1109 rl_instream = my_stdin;
1110#endif
1111
1112 my_stdout = fdopen(std_fd[STDOUT_FILENO], "w");
1113 if (!my_stdout) {
1114 fclose(my_stdin);
1115 my_stdin = NULL;
1116 ERROR("Failed initializing radmin stdout - %s", fr_syserror(errno));
1117 return -1;
1118 }
1119#ifdef USE_READLINE
1120 rl_outstream = my_stdout;
1121#endif
1122
1123 my_stderr = fdopen(std_fd[2], "w");
1124 if (!my_stderr) {
1125 fclose(my_stdin);
1126 fclose(my_stdout);
1127 my_stdin = NULL;
1128 my_stdout = NULL;
1129 PERROR("Failed initializing radmin stderr");
1130 return -1;
1131 }
1132
1133 /*
1134 * Note that the commands are registered by the main
1135 * thread. That registration is done in a (mostly)
1136 * thread-safe manner. So that asynchronous searches
1137 * won't go into la-la-land. They might find unfinished
1138 * commands, but they don't crash.
1139 */
1140 if (fr_thread_create(&cli_pthread_id, fr_radmin, NULL) < 0) {
1141 PERROR("Failed creating radmin thread");
1142 return -1;
1143 }
1144 cli_started = true;
1145 INFO("radmin interface started");
1146
1147 return 0;
1148}
1149
1151{
1152 if (!radmin_ctx) return;
1153
1154 stop = true;
1155
1156 if (cli_started) {
1157 (void) pthread_join(cli_pthread_id, NULL);
1158 cli_started = false;
1159 }
1160
1161 TALLOC_FREE(radmin_ctx);
1162}
1163
1164/*
1165 * Public registration hooks.
1166 */
1167int fr_radmin_register(UNUSED TALLOC_CTX *talloc_ctx, char const *name, void *ctx, fr_cmd_table_t *table)
1168{
1169 return fr_command_add_multi(radmin_ctx, &radmin_cmd, name, ctx, table);
1170}
1171
1172/** Run a command from an input string.
1173 *
1174 * @param info used to stor
1175 * @param fp standard output
1176 * @param fp_err error output
1177 * @param str the command to run. Note that this command is mangled in-place!
1178 * @param read_only permissions for the administrator trying to run the command.
1179 * @return
1180 * - <0 on error
1181 * - 0 on insufficient arguments to run command
1182 * - 1 for successfully running the command
1183 */
1184int fr_radmin_run(fr_cmd_info_t *info, FILE *fp, FILE *fp_err, char *str, bool read_only)
1185{
1186 int argc, ret;
1187
1188 argc = fr_command_str_to_argv(radmin_cmd, info, str);
1189 if (argc < 0) {
1190 fprintf(fp_err, "%s\n", fr_strerror());
1191 return -1;
1192 }
1193
1194 if (!info->runnable) {
1195 return 0;
1196 }
1197
1198 ret = fr_command_run(fp, fp_err, info, read_only);
1199 fflush(fp);
1200 fflush(fp_err);
1201
1202 /*
1203 * reset "info" to be a top-level context again.
1204 */
1205 (void) fr_command_clear(0, info);
1206
1207 if (ret < 0) return ret;
1208
1209 return 1;
1210}
1211
1212/*
1213 * Get help for a particular line of text.
1214 */
1215void fr_radmin_help(FILE *fp, char const *text)
1216{
1218}
1219
1220void fr_radmin_complete(FILE *fp, const char *text, int start)
1221{
1222 int i, num;
1223 char *my_expansions[CMD_MAX_EXPANSIONS];
1224 char **expansions = &my_expansions[0];
1225 char const **expansions_const;
1226
1227 memcpy(&expansions_const, &expansions, sizeof(expansions)); /* const issues */
1228
1229 num = fr_command_complete(radmin_cmd, text, start,
1230 CMD_MAX_EXPANSIONS, expansions_const);
1231 if (num <= 0) return;
1232
1233 for (i = 0; i < num; i++) {
1234 fprintf(fp, "%s\n", expansions[i]);
1235 free(expansions[i]);
1236 }
1237}
static int const char char buffer[256]
Definition acutest.h:576
strcpy(log_entry->msg, buffer)
int const char int line
Definition acutest.h:702
static int cmd_show_config_section(FILE *fp, FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
Definition radmin.c:721
#define CMD_MAX_ARGV
Definition radmin.c:151
static fr_cmd_t * radmin_cmd
Definition radmin.c:147
static int cmd_exit(FILE *fp, FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
static int cmd_show_config_item(FILE *fp, FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
Definition radmin.c:750
static int cmd_show_debug_level(FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, UNUSED fr_cmd_info_t const *info)
Definition radmin.c:530
static int context
Definition radmin.c:69
static void * fr_radmin(UNUSED void *input_ctx)
Definition radmin.c:231
static int tab_expand_config_section(TALLOC_CTX *talloc_ctx, void *ctx, fr_cmd_info_t *info, int max_expansions, char const **expansions)
Definition radmin.c:740
static fr_time_t start_time
radmin functions, tables, and callbacks
Definition radmin.c:439
static FILE * my_stderr
Definition radmin.c:74
static int cmd_set_debug_level(UNUSED FILE *fp, FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
Definition radmin.c:517
static int cmd_uptime(FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, UNUSED fr_cmd_info_t const *info)
Definition radmin.c:475
static char * readline(char const *prompt)
Definition radmin.c:82
static int cmd_stats_memory(FILE *fp, FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
Definition radmin.c:486
static int cmd_terminate(UNUSED FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, UNUSED fr_cmd_info_t const *info)
Definition radmin.c:469
static bool stop
Definition radmin.c:68
#define CMD_MAX_EXPANSIONS
Definition radmin.c:152
static main_config_t * radmin_main_config
Definition radmin.c:155
void fr_radmin_stop(void)
Definition radmin.c:1150
static int tab_expand_config_thing(TALLOC_CTX *talloc_ctx, UNUSED void *ctx, fr_cmd_info_t *info, int max_expansions, char const **expansions, bool want_section)
Definition radmin.c:601
static int cmd_help(FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
Definition radmin.c:449
int fr_radmin_run(fr_cmd_info_t *info, FILE *fp, FILE *fp_err, char *str, bool read_only)
Run a command from an input string.
Definition radmin.c:1184
int fr_radmin_start(main_config_t *config, bool cli, int std_fd[static 3])
Definition radmin.c:1082
static char * radmin_partial_line
Definition radmin.c:148
#define radmin_free(_x)
Definition radmin.c:136
static fr_cmd_info_t radmin_info
Definition radmin.c:70
static int cmd_show_client(FILE *fp, FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
Definition radmin.c:800
static pthread_t cli_pthread_id
Definition radmin.c:66
int fr_radmin_register(UNUSED TALLOC_CTX *talloc_ctx, char const *name, void *ctx, fr_cmd_table_t *table)
Definition radmin.c:1167
void fr_radmin_complete(FILE *fp, const char *text, int start)
Definition radmin.c:1220
static char readline_buffer[1024]
Definition radmin.c:80
static char * radmin_buffer
Definition radmin.c:149
void fr_radmin_help(FILE *fp, char const *text)
Definition radmin.c:1215
static FILE * my_stdin
Definition radmin.c:72
static FILE * my_stdout
Definition radmin.c:73
static bool cli_started
Definition radmin.c:67
static TALLOC_CTX * radmin_ctx
Definition radmin.c:71
static int tab_expand_config_item(TALLOC_CTX *talloc_ctx, void *ctx, fr_cmd_info_t *info, int max_expansions, char const **expansions)
Definition radmin.c:745
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:186
#define RCSID(id)
Definition build.h:512
#define DIAG_ON(_x)
Definition build.h:487
#define UNUSED
Definition build.h:336
#define DIAG_OFF(_x)
Definition build.h:486
int cf_section_write(FILE *fp, CONF_SECTION *cs, int depth)
Definition cf_file.c:4095
CONF_ITEM * cf_reference_item(CONF_SECTION const *parent_cs, CONF_SECTION const *outer_cs, char const *ptr)
Definition cf_file.c:4211
Common header for all CONF_* types.
Definition cf_priv.h:54
Configuration AVP similar to a fr_pair_t.
Definition cf_priv.h:77
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:106
bool cf_item_is_pair(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_PAIR.
Definition cf_util.c:640
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1352
char const * cf_section_name1(CONF_SECTION const *cs)
Return the first identifier of a CONF_SECTION.
Definition cf_util.c:1338
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:692
fr_token_t cf_pair_value_quote(CONF_PAIR const *pair)
Return the value (rhs) quoting of a pair.
Definition cf_util.c:1790
bool cf_item_is_section(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_SECTION.
Definition cf_util.c:626
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition cf_util.c:672
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1746
char const * cf_pair_attr(CONF_PAIR const *pair)
Return the attr of a CONF_PAIR.
Definition cf_util.c:1730
#define cf_item_next(_parent, _curr)
Definition cf_util.h:94
int fr_command_clear(int new_argc, fr_cmd_info_t *info)
Clear out any value boxes etc.
Definition command.c:2377
void fr_command_list(FILE *fp, int max_depth, fr_cmd_t *head, int options)
Definition command.c:1657
int fr_command_str_to_argv(fr_cmd_t *head, fr_cmd_info_t *info, char const *text)
Split a string in-place, updating argv[].
Definition command.c:2149
int fr_command_run(FILE *fp, FILE *fp_err, fr_cmd_info_t *info, bool read_only)
Run a particular command.
Definition command.c:1481
void fr_command_info_init(TALLOC_CTX *ctx, fr_cmd_info_t *info)
Initialize an fr_cmd_info_t structure.
Definition command.c:2405
int fr_command_print_help(FILE *fp, fr_cmd_t *head, char const *text)
Do readline-style help completions.
Definition command.c:2796
fr_command_register_hook_t fr_command_register_hook
Definition command.c:41
int fr_command_complete(fr_cmd_t *head, char const *text, int start, int max_expansions, char const **expansions)
Do readline-style command completions.
Definition command.c:2653
bool fr_command_strncmp(const char *word, const char *name)
Definition command.c:2900
int fr_command_add_multi(TALLOC_CTX *talloc_ctx, fr_cmd_t **head, char const *name, void *ctx, fr_cmd_table_t const *table)
Add multiple commands to the global command tree.
Definition command.c:993
int argc
current argument count
Definition command.h:39
fr_value_box_t ** box
value_box version of commands.
Definition command.h:43
bool runnable
is the command runnable?
Definition command.h:41
#define FR_COMMAND_OPTION_HELP
Definition command.h:90
char const * name
e.g. "stats"
Definition command.h:53
#define CMD_TABLE_END
Definition command.h:62
char const ** argv
text version of commands
Definition command.h:42
#define FR_COMMAND_OPTION_NONE
Definition command.h:87
int fr_log_talloc_report(TALLOC_CTX const *ctx)
Generate a talloc memory report for a context and print to stderr/stdout.
Definition debug.c:977
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:40
Test enumeration values.
Definition dict_test.h:92
free(array)
talloc_free(hp)
char const * secret
Secret PSK.
Definition client.h:90
int proto
Protocol number.
Definition client.h:147
char const * shortname
Client nickname.
Definition client.h:88
Describes a host allowed to send packets to the server.
Definition client.h:80
#define PERROR(_fmt,...)
Definition log.h:228
#define fr_time()
Definition event.c:60
int fr_debug_lvl
Definition log.c:41
static fr_cmd_table_t cmd_table[]
Definition radmin.c:816
#define add_history(line)
Definition radmin.c:690
static int cmd_test(FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, UNUSED fr_cmd_info_t const *info)
Definition radmin.c:806
static void * item(fr_lst_t const *lst, fr_lst_index_t idx)
Definition lst.c:121
bool talloc_memory_report
Print a memory report on what's left unfreed.
CONF_SECTION * root_cs
Root of the server config.
Definition main_config.h:55
Main server configuration.
Definition main_config.h:51
void main_loop_signal_raise(int flag)
Definition main_loop.c:78
@ RADIUS_SIGNAL_SELF_EXIT
Definition main_loop.h:38
@ RADIUS_SIGNAL_SELF_TERM
Definition main_loop.h:37
@ FR_TYPE_BOOL
A truth value.
ssize_t fr_fprintf(FILE *fp, char const *fmt,...)
Special version of fprintf which implements custom format specifiers.
Definition print.c:906
char * fr_asprint(TALLOC_CTX *ctx, char const *in, ssize_t inlen, char quote)
Escape string that may contain binary data, and write it to a new buffer.
Definition print.c:438
static const conf_parser_t config[]
Definition base.c:163
#define fr_assert(_expr)
Definition rad_assert.h:37
#define INFO(fmt,...)
Definition radict.c:63
static char const * name
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
fr_client_t * client_find(fr_client_list_t const *clients, fr_ipaddr_t const *ipaddr, int proto)
Definition client.c:370
return count
Definition module.c:155
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
static int talloc_const_free(void const *ptr)
Free const'd memory.
Definition talloc.h:254
static TALLOC_CTX * talloc_init_const(char const *name)
Allocate a top level chunk with a constant name.
Definition talloc.h:127
#define talloc_asprintf
Definition talloc.h:151
#define talloc_strdup(_ctx, _str)
Definition talloc.h:149
int fr_thread_create(pthread_t *thread, fr_thread_entry_t func, void *arg)
Create a joinable thread.
Definition thread.c:46
#define fr_time_sub(_a, _b)
Subtract one time from another.
Definition time.h:229
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
enum fr_token fr_token_t
@ T_SINGLE_QUOTED_STRING
Definition token.h:120
@ T_BARE_WORD
Definition token.h:118
@ T_BACK_QUOTED_STRING
Definition token.h:121
@ T_DOUBLE_QUOTED_STRING
Definition token.h:119
#define add(_type, _out, _in)
Definition stats.c:187
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:558
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
Definition strerror.c:737
ssize_t fr_value_box_from_str(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, char const *in, size_t inlen, fr_sbuff_unescape_rules_t const *erules)
Definition value.c:6068
#define fr_box_time_delta(_val)
Definition value.h:366