The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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  */
26 RCSID("$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  */
42 DIAG_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 */
62 DIAG_ON(strict-prototypes)
63 
64 #ifdef HAVE_GPERFTOOLS_PROFILER_H
65 #include <gperftools/profiler.h>
66 #endif
67 
68 static pthread_t cli_pthread_id;
69 static bool cli_started = false;
70 static bool stop = false;
71 static int context = 0;
73 static TALLOC_CTX *radmin_ctx = NULL;
74 
75 #ifndef USE_READLINE
76 /*
77  * @todo - use thread-local storage
78  */
79 static char readline_buffer[1024];
80 
81 static 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
141 static void add_history(UNUSED char *line)
142 {
143 }
144 #endif
145 
146 static fr_cmd_t *radmin_cmd = NULL;
147 static char *radmin_partial_line = NULL;
148 static char *radmin_buffer = NULL;
149 
150 #define CMD_MAX_ARGV (32)
151 #define CMD_MAX_EXPANSIONS (128)
152 
153 static 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  */
160 static int radmin_num_expansions;
161 static char *radmin_expansions[CMD_MAX_EXPANSIONS] = {0};
162 
163 static char *
164 radmin_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 
185 static char **
186 radmin_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 
200  offset = (radmin_partial_line - radmin_buffer);
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 
214 static int radmin_help(UNUSED int count, UNUSED int key)
215 {
216  size_t offset;
217  printf("\n");
218 
219  offset = (radmin_partial_line - radmin_buffer);
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 
230 static 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) {
273  radmin_free(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  */
390  add_history(line);
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 
405  radmin_free(line);
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 
423 static 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 
431 static 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 
451 static 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 
457 static 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 
468 static 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");
487  fr_log_talloc_report(NULL);
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 
499 static 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 
512 static 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
519 static 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 
568 static 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 
583 static 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 
703 static int cmd_show_config_section(FILE *fp, FILE *fp_err, UNUSED void *ctx, fr_cmd_info_t const *info)
704 {
705  CONF_ITEM *item;
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 
716  (void) cf_section_write(fp, cf_item_to_section(item), 0);
717 
718  return 0;
719 }
720 
721 
722 static 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 
727 static 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 
732 static 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;
735  CONF_ITEM *item;
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 
782 static 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 
811 found:
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
830 static 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 
843 static 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",
951  .func = cmd_show_config_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 
1101 void fr_radmin_stop(void)
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  */
1118 int 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  */
1135 int 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  */
1166 void fr_radmin_help(FILE *fp, char const *text)
1167 {
1168  fr_command_print_help(fp, radmin_cmd, text);
1169 }
1170 
1171 void 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:574
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 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 void add_history(UNUSED char *line)
Definition: radmin.c:141
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 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 char * readline(char const *prompt)
Definition: radmin.c:81
static void * fr_radmin(UNUSED void *input_ctx)
Definition: radmin.c:230
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:165
#define RCSID(id)
Definition: build.h:444
#define DIAG_ON(_x)
Definition: build.h:419
#define UNUSED
Definition: build.h:313
#define DIAG_OFF(_x)
Definition: build.h:418
int cf_section_write(FILE *fp, CONF_SECTION *cs, int depth)
Definition: cf_file.c:3141
CONF_ITEM * cf_reference_item(CONF_SECTION const *parent_cs, CONF_SECTION const *outer_cs, char const *ptr)
Definition: cf_file.c:3206
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:89
bool cf_item_is_pair(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_PAIR.
Definition: cf_util.c:597
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition: cf_util.c:629
char const * cf_pair_attr(CONF_PAIR const *pair)
Return the attr of a CONF_PAIR.
Definition: cf_util.c:1495
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition: cf_util.c:1126
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition: cf_util.c:1511
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition: cf_util.c:649
fr_token_t cf_pair_value_quote(CONF_PAIR const *pair)
Return the value (rhs) quoting of a pair.
Definition: cf_util.c:1555
bool cf_item_is_section(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_SECTION.
Definition: cf_util.c:583
char const * cf_section_name1(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition: cf_util.c:1112
#define cf_item_next(_ci, _prev)
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:1120
Test enumeration values.
Definition: dict_test.h:92
free(array)
char const * secret
Secret PSK.
Definition: client.h:87
int proto
Protocol number.
Definition: client.h:115
char const * shortname
Client nickname.
Definition: client.h:85
Describes a host allowed to send packets to the server.
Definition: client.h:77
#define PERROR(_fmt,...)
Definition: log.h:228
talloc_free(reap)
int fr_debug_lvl
Definition: log.c:42
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.
Definition: main_config.h:140
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.
Definition: merged_model.c:95
ssize_t fr_fprintf(FILE *fp, char const *fmt,...)
Special version of fprintf which implements custom format specifiers.
Definition: print.c:899
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:430
static const conf_parser_t config[]
Definition: base.c:188
static rc_request_t * current
Definition: radclient-ng.c:97
static char const * proto(int id, int porttype)
Definition: radwho.c:85
#define check(_handle, _len_p)
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:371
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:375
return count
Definition: module.c:175
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
#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:212
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
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
Definition: strerror.c:733
char const * fr_strerror(void)
Get the last library error.
Definition: strerror.c:554
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:5264
#define fr_box_time_delta(_val)
Definition: value.h:336