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