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