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: b147255506c8d0e5217091d51bdfb675fa43907c $
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: b147255506c8d0e5217091d51bdfb675fa43907c $")
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) continue;
347
348 /*
349 * It's a partial command. Add it to the context
350 * and continue.
351 *
352 * Note that we have to update `current_str`, because
353 * argv[context] currently points there...
354 */
355 if (!info->runnable) {
356 size_t len;
357
358 fr_assert(argc > 0);
359 len = strlen(line);
360
361 /*
362 * Not enough room for more commands, refuse to do it.
363 */
364 if ((context_offset[context] + len + 80) >= size) {
365 fprintf(my_stderr, "Too many commands!\n");
366 goto next;
367 }
368
369 /*
370 * Move the pointer down the buffer and
371 * keep reading more.
372 */
373
374 if (context > 0) {
375 talloc_const_free(prompt);
376 }
377
378 /*
379 * Remember how many arguments we
380 * added in this context, and go back up
381 * that number of arguments when entering
382 * 'exit'.
383 *
384 * Otherwise, entering a partial command
385 * "foo bar baz" would require you to
386 * type "exit" 3 times in order to get
387 * back to the root.
388 */
389 context_exit[argc] = context;
390 context_offset[argc] = context_offset[context] + len + 1;
391 radmin_buffer[context_offset[context] + len] = ' ';
392 context = argc;
393 prompt = talloc_asprintf(ctx, "... %s> ", info->argv[context - 1]);
394 goto next;
395 }
396
397 /*
398 * Else it's a info->runnable command. Add it to the
399 * history.
400 */
402
403 if (fr_command_run(my_stdout, my_stderr, info, false) < 0) {
404 fprintf(my_stderr, "Failed running command.\n");
405 }
406
407 next:
408 /*
409 * Reset this to the current context.
410 */
411 if (fr_command_clear(context, info) < 0) {
412 fr_perror("Failing clearing buffers");
413 break;
414 }
415
417
418 if (stop) break;
419 }
420
421 fclose(my_stdin);
422 fclose(my_stdout);
423 fclose(my_stderr);
424
425 talloc_free(ctx);
426 radmin_buffer = NULL;
427 radmin_partial_line = NULL;
428
429 return NULL;
430}
431
432
433/** radmin functions, tables, and callbacks
434 *
435 */
437
438static int cmd_exit(UNUSED FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, UNUSED fr_cmd_info_t const *info)
439{
441 stop = true;
442
443 return 0;
444}
445
446static int cmd_help(FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
447{
448 int max = 1;
449 int options = FR_COMMAND_OPTION_HELP;
450
451 if (info->argc > 0) {
452 if (strcmp(info->argv[0], "all") == 0) {
453 max = CMD_MAX_ARGV;
454 }
455 else if (strcmp(info->argv[0], "commands") == 0) {
456 max = CMD_MAX_ARGV;
457 options = FR_COMMAND_OPTION_NONE;
458 }
459 }
460
461 fr_command_list(fp, max, radmin_cmd, options);
462
463 return 0;
464}
465
466static int cmd_terminate(UNUSED FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, UNUSED fr_cmd_info_t const *info)
467{
469 return 0;
470}
471
472static int cmd_uptime(FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, UNUSED fr_cmd_info_t const *info)
473{
474 fr_time_delta_t uptime;
475
476 uptime = fr_time_sub(fr_time(), start_time);
477
478 fr_fprintf(fp, "Uptime: %pVs seconds\n", fr_box_time_delta(uptime));
479
480 return 0;
481}
482
483static int cmd_stats_memory(FILE *fp, FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
484{
486 fprintf(fp, "Statistics are only available when the server is started with '-M'.\n");
487 return -1;
488 }
489
490 if (strcmp(info->argv[0], "total") == 0) {
491 fprintf(fp, "%zd\n", talloc_total_size(NULL));
492 return 0;
493 }
494
495 if (strcmp(info->argv[0], "blocks") == 0) {
496 fprintf(fp, "%zd\n", talloc_total_blocks(NULL));
497 return 0;
498 }
499
500 if (strcmp(info->argv[0], "full") == 0) {
501 fprintf(fp, "see stdout of the server for the full report.\n");
503 return 0;
504 }
505
506 /*
507 * Should never reach here. The command parser will
508 * ensure that.
509 */
510 fprintf(fp_err, "Must use 'stats memory (blocks|full|total)'\n");
511 return -1;
512}
513
514static int cmd_set_debug_level(UNUSED FILE *fp, FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
515{
516 int level = atoi(info->argv[0]);
517
518 if ((level < 0) || level > 5) {
519 fprintf(fp_err, "Invalid debug level '%s'\n", info->argv[0]);
520 return -1;
521 }
522
523 fr_debug_lvl = level;
524 return 0;
525}
526
527static int cmd_show_debug_level(FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, UNUSED fr_cmd_info_t const *info)
528{
529 fprintf(fp, "%d\n", fr_debug_lvl);
530 return 0;
531}
532
533#ifdef HAVE_GPERFTOOLS_PROFILER_H
534static int cmd_set_profile_status(UNUSED FILE *fp, FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
535{
536 fr_value_box_t box;
537 struct ProfilerState state;
538
539 if (fr_value_box_from_str(NULL, &box, FR_TYPE_BOOL, NULL,
540 info->argv[0], strlen(info->argv[0]),
541 NULL) <= 0) {
542 fprintf(fp_err, "Failed setting profile status '%s' - %s\n", info->argv[0], fr_strerror());
543 return -1;
544 }
545
546 ProfilerGetCurrentState(&state);
547
548 if (box.vb_bool) {
549 char *filename;
550
551 if (state.enabled) {
552 fprintf(fp_err, "Profiling is already on, to file %s\n", state.profile_name);
553 return -1;
554 }
555
556 if (info->argc >= 2) {
557 filename = UNCONST(char *, info->argv[1]);
558 } else {
559 filename = getenv("FR_PROFILE_FILENAME");
560 }
561
562 if (filename) {
563 ProfilerStart(filename);
564 } else {
565 pid_t pid = getpid();
566 MEM(filename = talloc_asprintf(NULL, "/tmp/freeradius-profile.%u.prof", pid));
567 ProfilerStart(filename);
568 talloc_free(filename);
569 }
570
571 } else if (state.enabled) {
572 ProfilerFlush();
573 ProfilerStop();
574 }
575 /*
576 * Else profiling is already off, allow the admin to turn
577 * it off again without producing an error
578 */
579
580 return 0;
581}
582
583static int cmd_show_profile_status(FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, UNUSED fr_cmd_info_t const *info)
584{
585 struct ProfilerState state;
586 ProfilerGetCurrentState(&state);
587
588 if (!state.enabled) {
589 fprintf(fp, "off\n");
590 return 0;
591 }
592
593 fprintf(fp, "on %s\n", state.profile_name);
594 return 0;
595}
596#endif
597
598static int tab_expand_config_thing(TALLOC_CTX *talloc_ctx, UNUSED void *ctx, fr_cmd_info_t *info, int max_expansions, char const **expansions,
599 bool want_section)
600{
601 int count;
602 size_t reflen, offset;
603 char *ref;
604 char const *text;
605 CONF_ITEM *ci;
606 CONF_SECTION *cs;
607
608 if (info->argc <= 0) return 0;
609
610 ref = talloc_strdup(talloc_ctx, info->argv[info->argc - 1]);
611 text = strrchr(ref, '.');
612 if (!text) {
614 reflen = 0;
615 offset = 0;
616 text = ref;
617
618 /*
619 * If it's a good ref, use that for expansions.
620 */
622 if (ci && cf_item_is_section(ci)) {
623 cs = cf_item_to_section(ci);
624 text = "";
625 reflen = strlen(ref);
626 offset = 1;
627 }
628
629 } else {
630 reflen = (text - ref);
631 offset = 1;
632 ref[reflen] = '\0';
633 text++;
634
636 if (!ci) {
637 none:
638 talloc_free(ref);
639 return 0;
640 }
641
642 /*
643 * The ref is to a pair. Don't allow further
644 * expansions.
645 */
646 if (cf_item_is_pair(ci)) goto none;
647 cs = cf_item_to_section(ci);
648 }
649
650 count = 0;
651
652 /*
653 * Walk the reference, allowing for additional expansions.
654 */
655 for (ci = cf_item_next(cs, NULL);
656 ci != NULL;
657 ci = cf_item_next(cs, ci)) {
658 char const *name1, *check;
659 char *str;
660 char buffer[256];
661
662 /*
663 * @todo - if we want a config pair, AND we have
664 * partial input, THEN check if the section name
665 * matches the partial input. If so, allow it as
666 * an expansion.
667 */
668 if (cf_item_is_section(ci)) {
669 char const *name2;
670
673
674 if (name2) {
675 snprintf(buffer, sizeof(buffer), "%s[%s]", name1, name2);
676 check = buffer;
677 } else {
678 check = name1;
679 }
680
681 if (!want_section) {
682 if (*text && fr_command_strncmp(text, check)) {
683 // @todo - expand the pairs in this section
684 goto add;
685 }
686
687 continue;
688 }
689
690 } else if (!cf_item_is_pair(ci)) {
691 continue;
692
693 } else {
694 if (want_section) continue;
695
696 name1 = cf_pair_attr(cf_item_to_pair(ci));
697 check = name1;
698 }
699
700 /*
701 * Check for a matching name.
702 */
703 if (!fr_command_strncmp(text, check)) continue;
704
705 add:
706 MEM(expansions[count] = str = malloc(reflen + strlen(check) + offset + 1));
707 memcpy(str, ref, reflen);
708 str[reflen] = '.';
709 strcpy(str + reflen + offset, check);
710
711 count++;
712 if (count >= max_expansions) return count;
713 }
714
715 return count;
716}
717
718static int cmd_show_config_section(FILE *fp, FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
719{
721
722 fr_assert(info->argc > 0);
723
725 info->box[0]->vb_strvalue);
726 if (!item || !cf_item_is_section(item)) {
727 fprintf(fp_err, "No such configuration section.\n");
728 return -1;
729 }
730
732
733 return 0;
734}
735
736
737static int tab_expand_config_section(TALLOC_CTX *talloc_ctx, void *ctx, fr_cmd_info_t *info, int max_expansions, char const **expansions)
738{
739 return tab_expand_config_thing(talloc_ctx, ctx, info, max_expansions, expansions, true);
740}
741
742static int tab_expand_config_item(TALLOC_CTX *talloc_ctx, void *ctx, fr_cmd_info_t *info, int max_expansions, char const **expansions)
743{
744 return tab_expand_config_thing(talloc_ctx, ctx, info, max_expansions, expansions, false);
745}
746
747static int cmd_show_config_item(FILE *fp, FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
748{
749 fr_token_t token;
751 CONF_PAIR *cp;
752
753 fr_assert(info->argc > 0);
754
756 info->box[0]->vb_strvalue);
757 if (!item || !cf_item_is_pair(item)) {
758 fprintf(fp_err, "No such configuration item.\n");
759 return -1;
760 }
761
762 cp = cf_item_to_pair(item);
763 token = cf_pair_value_quote(cp);
764
765 if (token == T_BARE_WORD) {
766 bare:
767 fprintf(fp, "%s\n", cf_pair_value(cp));
768 } else {
769 char quote;
770 char *value;
771
772 switch (token) {
774 quote = '"';
775 break;
776
778 quote = '\'';
779 break;
780
782 quote = '`';
783 break;
784
785 default:
786 goto bare;
787 }
788
789 value = fr_asprint(NULL, cf_pair_value(cp), -1, quote);
790 fprintf(fp, "%c%s%c\n", quote, value, quote);
792 }
793
794 return 0;
795}
796
797static int cmd_show_client(FILE *fp, FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
798{
799 fr_client_t *client;
800
801 if (info->argc >= 2) {
802 int proto;
803
804 if (strcmp(info->argv[1], "tcp") == 0) {
805 proto = IPPROTO_TCP;
806
807 } else if (strcmp(info->argv[1], "udp") == 0) {
808 proto = IPPROTO_UDP;
809
810 } else {
811 fprintf(fp_err, "Unknown proto '%s'.\n", info->argv[1]);
812 return -1;
813 }
814
815 client = client_find(NULL, &info->box[0]->vb_ip, proto);
816 if (client) goto found;
817
818 not_found:
819 fprintf(fp_err, "No such client.\n");
820 return -1;
821 } else {
822 client = client_find(NULL, &info->box[0]->vb_ip, IPPROTO_IP); /* hack */
823 if (!client) goto not_found;
824 }
825
826found:
827 fprintf(fp, "shortname\t%s\n", client->shortname);
828 fprintf(fp, "secret\t\t%s\n", client->secret);
829
830 if (client->proto == IPPROTO_UDP) {
831 fprintf(fp, "proto\t\tudp\n");
832
833 } else if (client->proto == IPPROTO_TCP) {
834 fprintf(fp, "proto\t\ttcp\n");
835 } else {
836 fprintf(fp, "proto\t\t*\n");
837 }
838
839 return 0;
840}
841
842//#define CMD_TEST (1)
843
844#ifdef CMD_TEST
845static int cmd_test(FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
846{
847 int i;
848
849 fprintf(fp, "TEST %d\n", info->argc);
850
851 for (i = 0; i < info->argc; i++) {
852 fprintf(fp, "\t%s\n", info->argv[i]);
853 }
854
855 return 0;
856}
857
858static 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)
859{
860 char const *text;
861 char *p;
862
863 if (info->argc == 0) return 0;
864
865 text = info->argv[info->argc - 1];
866
867 /*
868 * Expand a list of things
869 */
870 if (!*text) {
871 expansions[0] = strdup("0");
872 expansions[1] = strdup("1");
873 return 2;
874 }
875
876 if ((text[0] < '0') || (text[0] > '9')) {
877 return 0;
878 }
879
880 /*
881 * If the user enters a digit, allow it.
882 */
883 expansions[0] = p = malloc(2);
884 p[0] = text[0];
885 p[1] = '\0';
886
887 return 1;
888}
889#endif
890
892 {
893 .name = "exit",
894 .func = cmd_exit,
895 .help = "Exit from the current context.",
896 .read_only = true
897 },
898
899 {
900 .name = "quit",
901 .func = cmd_exit,
902 .help = "Quit and close the command line immediately.",
903 .read_only = true
904 },
905
906 {
907 .name = "help",
908 .syntax = "[(all|commands)]",
909 .func = cmd_help,
910 .help = "Display list of commands and their help text.",
911 .read_only = true
912 },
913
914
915 {
916 .name = "terminate",
917 .func = cmd_terminate,
918 .help = "Terminate the running server and cause it to exit.",
919 .read_only = false
920 },
921
922
923#ifdef CMD_TEST
924 {
925 .parent = "test",
926 .name = "foo"
927 .syntax = "INTEGER",
928 .func = cmd_test,
929 .tab_expand = cmd_test_tab_expand,
930 .help = "test foo INTEGER",
931 .read_only = true,
932 },
933#endif
934
935 {
936 .name = "uptime",
937 .func = cmd_uptime,
938 .help = "Show uptime since the server started.",
939 .read_only = true
940 },
941
942 {
943 .name = "set",
944 .help = "Change settings in the server.",
945 .read_only = false
946 },
947
948 {
949 .name = "show",
950 .help = "Show settings in the server.",
951 .read_only = true
952 },
953
954 {
955 .parent = "show",
956 .name = "config",
957 .help = "Show configuration settings in the server.",
958 .read_only = true
959 },
960
961 {
962 .parent = "show config",
963 .name = "section",
964 .syntax = "STRING",
965 .help = "Show a named configuration section",
967 .tab_expand = tab_expand_config_section,
968 .read_only = true
969 },
970
971 {
972 .parent = "show config",
973 .name = "item",
974 .syntax = "STRING",
975 .help = "Show a named configuration item",
976 .func = cmd_show_config_item,
977 .tab_expand = tab_expand_config_item,
978 .read_only = true
979 },
980
981 {
982 .parent = "show",
983 .name = "client",
984 .help = "Show information about a client or clients.",
985 .read_only = true
986 },
987
988 {
989 .parent = "show client",
990 .name = "config",
991 .syntax = "IPADDR [(udp|tcp)]",
992 .help = "Show the configuration for a given client.",
993 .func = cmd_show_client,
994 .read_only = true
995 },
996
997 {
998 .name = "stats",
999 .help = "Show statistics in the server.",
1000 .read_only = true
1001 },
1002
1003 {
1004 .parent = "stats",
1005 .name = "memory",
1006 .syntax = "(blocks|full|total)",
1007 .func = cmd_stats_memory,
1008 .help = "Show memory statistics.",
1009 .read_only = true,
1010 },
1011
1012 {
1013 .parent = "set",
1014 .name = "debug",
1015 .help = "Change debug settings.",
1016 .read_only = false
1017 },
1018
1019 {
1020 .parent = "set debug",
1021 .name = "level",
1022 .syntax = "INTEGER",
1023 .func = cmd_set_debug_level,
1024 .help = "Change the debug level.",
1025 .read_only = false,
1026 },
1027
1028 {
1029 .parent = "show",
1030 .name = "debug",
1031 .help = "Show debug settings.",
1032 .read_only = true
1033 },
1034
1035 {
1036 .parent = "show debug",
1037 .name = "level",
1038 .func = cmd_show_debug_level,
1039 .help = "show debug level",
1040 .read_only = true,
1041 },
1042
1043#ifdef HAVE_GPERFTOOLS_PROFILER_H
1044 {
1045 .parent = "set",
1046 .name = "profile",
1047 .help = "Change profiler settings.",
1048 .read_only = false
1049 },
1050
1051 {
1052 .parent = "set profile",
1053 .name = "status",
1054 .syntax = "BOOL [STRING]",
1055 .func = cmd_set_profile_status,
1056 .help = "Change the profiler status on/off, and potentially the filename",
1057 .read_only = false,
1058 },
1059
1060 {
1061 .parent = "show",
1062 .name = "profile",
1063 .help = "Show profile settings.",
1064 .read_only = true
1065 },
1066
1067 {
1068 .parent = "show profile",
1069 .name = "status",
1070 .func = cmd_show_profile_status,
1071 .help = "show profile status, including filename if profiling is on.",
1072 .read_only = true,
1073 },
1074#endif
1075
1077};
1078
1079int fr_radmin_start(main_config_t *config, bool cli, int std_fd[static 3])
1080{
1081 radmin_ctx = talloc_init_const("radmin");
1082 if (!radmin_ctx) return -1;
1083
1084 start_time = fr_time();
1085
1086#ifdef USE_READLINE
1087 memcpy(&rl_readline_name, &config->name, sizeof(rl_readline_name)); /* const issues on OSX */
1088#endif
1089
1092
1093 if (fr_radmin_register(radmin_ctx, NULL, NULL, cmd_table) < 0) {
1094 PERROR("Failed initializing radmin");
1095 return -1;
1096 }
1097
1098 if (!cli) return 0;
1099
1100 my_stdin = fdopen(std_fd[STDIN_FILENO], "r");
1101 if (!my_stdin) {
1102 ERROR("Failed initializing radmin stdin %s", fr_syserror(errno));
1103 return -1;
1104 }
1105#ifdef USE_READLINE
1106 rl_instream = my_stdin;
1107#endif
1108
1109 my_stdout = fdopen(std_fd[STDOUT_FILENO], "w");
1110 if (!my_stdout) {
1111 fclose(my_stdin);
1112 my_stdin = NULL;
1113 ERROR("Failed initializing radmin stdout - %s", fr_syserror(errno));
1114 return -1;
1115 }
1116#ifdef USE_READLINE
1117 rl_outstream = my_stdout;
1118#endif
1119
1120 my_stderr = fdopen(std_fd[2], "w");
1121 if (!my_stderr) {
1122 fclose(my_stdin);
1123 fclose(my_stdout);
1124 my_stdin = NULL;
1125 my_stdout = NULL;
1126 PERROR("Failed initializing radmin stderr");
1127 return -1;
1128 }
1129
1130 /*
1131 * Note that the commands are registered by the main
1132 * thread. That registration is done in a (mostly)
1133 * thread-safe manner. So that asynchronous searches
1134 * won't go into la-la-land. They might find unfinished
1135 * commands, but they don't crash.
1136 */
1137 if (fr_thread_create(&cli_pthread_id, fr_radmin, NULL) < 0) {
1138 PERROR("Failed creating radmin thread");
1139 return -1;
1140 }
1141 cli_started = true;
1142 INFO("radmin interface started");
1143
1144 return 0;
1145}
1146
1148{
1149 if (!radmin_ctx) return;
1150
1151 stop = true;
1152
1153 if (cli_started) {
1154 (void) pthread_join(cli_pthread_id, NULL);
1155 cli_started = false;
1156 }
1157
1158 TALLOC_FREE(radmin_ctx);
1159}
1160
1161/*
1162 * Public registration hooks.
1163 */
1164int fr_radmin_register(UNUSED TALLOC_CTX *talloc_ctx, char const *name, void *ctx, fr_cmd_table_t *table)
1165{
1166 return fr_command_add_multi(radmin_ctx, &radmin_cmd, name, ctx, table);
1167}
1168
1169/** Run a command from an input string.
1170 *
1171 * @param info used to stor
1172 * @param fp standard output
1173 * @param fp_err error output
1174 * @param str the command to run. Note that this command is mangled in-place!
1175 * @param read_only permissions for the administrator trying to run the command.
1176 * @return
1177 * - <0 on error
1178 * - 0 on insufficient arguments to run command
1179 * - 1 for successfully running the command
1180 */
1181int fr_radmin_run(fr_cmd_info_t *info, FILE *fp, FILE *fp_err, char *str, bool read_only)
1182{
1183 int argc, ret;
1184
1185 argc = fr_command_str_to_argv(radmin_cmd, info, str);
1186 if (argc < 0) {
1187 fprintf(fp_err, "%s\n", fr_strerror());
1188 return -1;
1189 }
1190
1191 if (!info->runnable) {
1192 return 0;
1193 }
1194
1195 ret = fr_command_run(fp, fp_err, info, read_only);
1196 fflush(fp);
1197 fflush(fp_err);
1198
1199 /*
1200 * reset "info" to be a top-level context again.
1201 */
1202 (void) fr_command_clear(0, info);
1203
1204 if (ret < 0) return ret;
1205
1206 return 1;
1207}
1208
1209/*
1210 * Get help for a particular line of text.
1211 */
1212void fr_radmin_help(FILE *fp, char const *text)
1213{
1215}
1216
1217void fr_radmin_complete(FILE *fp, const char *text, int start)
1218{
1219 int i, num;
1220 char *my_expansions[CMD_MAX_EXPANSIONS];
1221 char **expansions = &my_expansions[0];
1222 char const **expansions_const;
1223
1224 memcpy(&expansions_const, &expansions, sizeof(expansions)); /* const issues */
1225
1226 num = fr_command_complete(radmin_cmd, text, start,
1227 CMD_MAX_EXPANSIONS, expansions_const);
1228 if (num <= 0) return;
1229
1230 for (i = 0; i < num; i++) {
1231 fprintf(fp, "%s\n", expansions[i]);
1232 free(expansions[i]);
1233 }
1234}
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:718
#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:747
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:527
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:737
static fr_time_t start_time
radmin functions, tables, and callbacks
Definition radmin.c:436
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:514
static int cmd_uptime(FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, UNUSED fr_cmd_info_t const *info)
Definition radmin.c:472
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:483
static int cmd_terminate(UNUSED FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, UNUSED fr_cmd_info_t const *info)
Definition radmin.c:466
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:1147
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:598
static int cmd_help(FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
Definition radmin.c:446
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:1181
int fr_radmin_start(main_config_t *config, bool cli, int std_fd[static 3])
Definition radmin.c:1079
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:797
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:1164
void fr_radmin_complete(FILE *fp, const char *text, int start)
Definition radmin.c:1217
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:1212
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:742
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:186
#define RCSID(id)
Definition build.h:506
#define DIAG_ON(_x)
Definition build.h:481
#define UNUSED
Definition build.h:336
#define DIAG_OFF(_x)
Definition build.h:480
int cf_section_write(FILE *fp, CONF_SECTION *cs, int depth)
Definition cf_file.c:3778
CONF_ITEM * cf_reference_item(CONF_SECTION const *parent_cs, CONF_SECTION const *outer_cs, char const *ptr)
Definition cf_file.c:3843
Common header for all CONF_* types.
Definition cf_priv.h:49
Configuration AVP similar to a fr_pair_t.
Definition cf_priv.h:72
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:631
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1184
char const * cf_section_name1(CONF_SECTION const *cs)
Return the first identifier of a CONF_SECTION.
Definition cf_util.c:1170
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:683
fr_token_t cf_pair_value_quote(CONF_PAIR const *pair)
Return the value (rhs) quoting of a pair.
Definition cf_util.c:1618
bool cf_item_is_section(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_SECTION.
Definition cf_util.c:617
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition cf_util.c:663
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1574
char const * cf_pair_attr(CONF_PAIR const *pair)
Return the attr of a CONF_PAIR.
Definition cf_util.c:1558
#define cf_item_next(_parent, _curr)
Definition cf_util.h:89
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:962
#define MEM(x)
Definition debug.h:46
#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:40
static fr_cmd_table_t cmd_table[]
Definition radmin.c:782
#define add_history(line)
Definition radmin.c:659
static int cmd_test(FILE *fp, UNUSED FILE *fp_err, UNUSED void *ctx, UNUSED fr_cmd_info_t const *info)
Definition radmin.c:772
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:373
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:253
static TALLOC_CTX * talloc_init_const(char const *name)
Allocate a top level chunk with a constant name.
Definition talloc.h:120
#define talloc_asprintf
Definition talloc.h:144
#define talloc_strdup(_ctx, _str)
Definition talloc.h:142
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:553
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
Definition strerror.c:732
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:6053
#define fr_box_time_delta(_val)
Definition value.h:366