Permit multiple "-e"
[freeradius.git] / src / main / radmin.c
1 /*
2  * radmin.c     RADIUS Administration tool.
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2008   The FreeRADIUS server project
21  * Copyright 2008   Alan DeKok <aland@deployingradius.com>
22  */
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/radpaths.h>
29
30 #ifdef HAVE_SYS_SOCKET_H
31 #include <sys/socket.h>
32 #endif
33
34 #ifdef HAVE_SYS_UN_H
35 #include <sys/un.h>
36 #endif
37
38 #ifdef HAVE_GETOPT_H
39 #include <getopt.h>
40 #endif
41
42 #ifdef HAVE_LIBREADLINE
43 #if defined(HAVE_READLINE_READLINE_H)
44 #include <readline/readline.h>
45 #elif defined(HAVE_READLINE_H)
46 #include <readline.h>
47 #else /* !defined(HAVE_READLINE_H) */
48 extern char *readline ();
49 #endif /* !defined(HAVE_READLINE_H) */
50 char *cmdline = NULL;
51 #else /* !defined(HAVE_READLINE_READLINE_H) */
52   /* no readline */
53 #endif /* HAVE_LIBREADLINE */
54
55 #ifdef HAVE_READLINE_HISTORY
56 #if defined(HAVE_READLINE_HISTORY_H)
57 #include <readline/history.h>
58 #elif defined(HAVE_HISTORY_H)
59 #include <history.h>
60 #else /* !defined(HAVE_HISTORY_H) */
61 extern void add_history ();
62 extern int write_history ();
63 extern int read_history ();
64 #endif /* defined(HAVE_READLINE_HISTORY_H) */
65   /* no history */
66 #endif /* HAVE_READLINE_HISTORY */
67
68 /*
69  *      For configuration file stuff.
70  */
71 const char *radius_dir = RADDBDIR;
72 const char *progname = "radmin";
73
74 /*
75  *      The rest of this is because the conffile.c, etc. assume
76  *      they're running inside of the server.  And we don't (yet)
77  *      have a "libfreeradius-server", or "libfreeradius-util".
78  */
79 int debug_flag = 0;
80 struct main_config_t mainconfig;
81 char *request_log_file = NULL;
82 char *debug_log_file = NULL;
83 int radius_xlat(UNUSED char *out, UNUSED int outlen, UNUSED const char *fmt,
84                 UNUSED REQUEST *request, UNUSED RADIUS_ESCAPE_STRING func)
85 {
86         return -1;
87 }
88
89 static FILE *outputfp = NULL;
90 static int echo = FALSE;
91
92 static int fr_domain_socket(const char *path)
93 {
94         int sockfd = -1;
95 #ifdef HAVE_SYS_UN_H
96         size_t len;
97         socklen_t socklen;
98         struct sockaddr_un saremote;
99
100         len = strlen(path);
101         if (len >= sizeof(saremote.sun_path)) {
102                 fprintf(stderr, "%s: Path too long in filename\n", progname);
103                 return -1;
104         }
105
106         if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
107                 fprintf(stderr, "%s: Failed creating socket: %s\n",
108                         progname, strerror(errno));
109                 return -1;
110         }
111
112         saremote.sun_family = AF_UNIX;
113         memcpy(saremote.sun_path, path, len + 1); /* SUN_LEN does strlen */
114         
115         socklen = SUN_LEN(&saremote);
116
117         if (connect(sockfd, (struct sockaddr *)&saremote, socklen) < 0) {
118                 fprintf(stderr, "%s: Failed connecting to %s: %s\n",
119                         progname, path, strerror(errno));
120                 close(sockfd);
121                 return -1;
122         }
123
124 #ifdef O_NONBLOCK
125         {
126                 int flags;
127                 
128                 if ((flags = fcntl(sockfd, F_GETFL, NULL)) < 0)  {
129                         fprintf(stderr, "%s: Failure getting socket flags: %s",
130                                 progname, strerror(errno));
131                         close(sockfd);
132                         return -1;
133                 }
134                 
135                 flags |= O_NONBLOCK;
136                 if( fcntl(sockfd, F_SETFL, flags) < 0) {
137                         fprintf(stderr, "%s: Failure setting socket flags: %s",
138                                 progname, strerror(errno));
139                         close(sockfd);
140                         return -1;
141                 }
142         }
143 #endif
144 #endif
145         return sockfd;
146 }
147
148 static int usage(void)
149 {
150         printf("Usage: %s [ args ]\n", progname);
151         printf("  -d raddb_dir    Configuration files are in \"raddbdir/*\".\n");
152         printf("  -e command      Execute 'command' and then exit.\n");
153         printf("  -E              Echo commands as they are being executed.\n");
154         printf("  -f socket_file  Open socket_file directly, without reading radius.conf\n");
155         printf("  -i input_file   Read commands from 'input_file'.\n");
156         printf("  -n name         Read raddb/name.conf instead of raddb/radiusd.conf\n");
157         printf("  -o output_file  Write commands to 'output_file'.\n");
158         printf("  -q              Quiet mode.\n");
159
160         exit(1);
161 }
162
163 static ssize_t run_command(int sockfd, const char *command,
164                            char *buffer, size_t bufsize)
165 {
166         char *p;
167         ssize_t size, len;
168         int flag = 1;
169
170         if (echo) {
171                 fprintf(outputfp, "%s\n", command);
172         }
173
174         /*
175          *      Write the text to the socket.
176          */
177         if (write(sockfd, command, strlen(command)) < 0) return -1;
178         if (write(sockfd, "\r\n", 2) < 0) return -1;
179
180         /*
181          *      Read the response
182          */
183         size = 0;
184         buffer[0] = '\0';
185
186         memset(buffer, 0, bufsize);
187
188         while (flag == 1) {
189                 int rcode;
190                 fd_set readfds;
191
192                 FD_ZERO(&readfds);
193                 FD_SET(sockfd, &readfds);
194
195                 rcode = select(sockfd + 1, &readfds, NULL, NULL, NULL);
196                 if (rcode < 0) {
197                         if (errno == EINTR) continue;
198
199                         fprintf(stderr, "%s: Failed selecting: %s\n",
200                                 progname, strerror(errno));
201                         exit(1);
202                 }
203
204 #ifdef MSG_DONTWAIT
205                 len = recv(sockfd, buffer + size,
206                            bufsize - size - 1, MSG_DONTWAIT);
207 #else
208                 /*
209                  *      Read one byte at a time (ugh)
210                  */
211                 len = recv(sockfd, buffer + size, 1, 0);
212 #endif
213                 if (len < 0) {
214                         /*
215                          *      No data: keep looping
216                          */
217                         if ((errno == EAGAIN) || (errno == EINTR)) {
218                                 continue;
219                         }
220
221                         fprintf(stderr, "%s: Error reading socket: %s\n",
222                                 progname, strerror(errno));
223                         exit(1);
224                 }
225                 if (len == 0) return 0; /* clean exit */
226
227                 size += len;
228                 buffer[size] = '\0';
229
230                 /*
231                  *      There really is a better way of doing this.
232                  */
233                 p = strstr(buffer, "radmin> ");
234                 if (p &&
235                     ((p == buffer) || 
236                      (p[-1] == '\n') ||
237                      (p[-1] == '\r'))) {
238                         *p = '\0';
239
240                         if (p[-1] == '\n') p[-1] = '\0';
241
242                         flag = 0;
243                         break;
244                 }
245         }
246
247         /*
248          *      Blank prompt.  Go get another command.
249          */
250         if (!buffer[0]) return 1;
251
252         buffer[size] = '\0'; /* this is at least right */
253
254         return 2;
255 }
256
257 #define MAX_COMMANDS (4)
258
259 int main(int argc, char **argv)
260 {
261         int argval, quiet = 0;
262         int done_license = 0;
263         int sockfd;
264         uint32_t magic;
265         char *line = NULL;
266         ssize_t len, size;
267         const char *file = NULL;
268         const char *name = "radiusd";
269         char *p, buffer[65536];
270         const char *input_file = NULL;
271         FILE *inputfp = stdin;
272         const char *output_file = NULL;
273         
274         char *commands[MAX_COMMANDS];
275         int num_commands = -1;
276
277         outputfp = stdout;      /* stdout is not a constant value... */
278
279         if ((progname = strrchr(argv[0], FR_DIR_SEP)) == NULL)
280                 progname = argv[0];
281         else
282                 progname++;
283
284         while ((argval = getopt(argc, argv, "d:hi:e:Ef:n:o:q")) != EOF) {
285                 switch(argval) {
286                 case 'd':
287                         if (file) {
288                                 fprintf(stderr, "%s: -d and -f cannot be used together.\n", progname);
289                                 exit(1);
290                         }
291                         radius_dir = optarg;
292                         break;
293
294                 case 'e':
295                         num_commands++; /* starts at -1 */
296                         if (num_commands >= MAX_COMMANDS) {
297                                 fprintf(stderr, "%s: Too many '-e'\n",
298                                         progname);
299                                 exit(1);
300                         }
301                         commands[num_commands] = optarg;
302                         break;
303
304                 case 'E':
305                         echo = TRUE;
306                         break;
307
308                 case 'f':
309                         radius_dir = NULL;
310                         file = optarg;
311                         break;
312
313                 default:
314                 case 'h':
315                         usage();
316                         break;
317
318                 case 'i':
319                         if (strcmp(optarg, "-") != 0) {
320                                 input_file = optarg;
321                         }
322                         quiet = 1;
323                         break;
324
325                 case 'n':
326                         name = optarg;
327                         break;
328
329                 case 'o':
330                         if (strcmp(optarg, "-") != 0) {
331                                 output_file = optarg;
332                         }
333                         quiet = 1;
334                         break;
335
336                 case 'q':
337                         quiet = 1;
338                         break;
339                 }
340         }
341
342         if (radius_dir) {
343                 int rcode;
344                 CONF_SECTION *cs, *subcs;
345
346                 file = NULL;    /* MUST read it from the conffile now */
347
348                 snprintf(buffer, sizeof(buffer), "%s/%s.conf",
349                          radius_dir, name);
350
351                 cs = cf_file_read(buffer);
352                 if (!cs) {
353                         fprintf(stderr, "%s: Errors reading %s\n",
354                                 progname, buffer);
355                         exit(1);
356                 }
357
358                 subcs = NULL;
359                 while ((subcs = cf_subsection_find_next(cs, subcs, "listen")) != NULL) {
360                         const char *value;
361                         CONF_PAIR *cp = cf_pair_find(subcs, "type");
362                         
363                         if (!cp) continue;
364
365                         value = cf_pair_value(cp);
366                         if (!value) continue;
367
368                         if (strcmp(value, "control") != 0) continue;
369
370                         /*
371                          *      Now find the socket name (sigh)
372                          */
373                         rcode = cf_item_parse(subcs, "socket",
374                                               PW_TYPE_STRING_PTR,
375                                               &file, NULL);
376                         if (rcode < 0) {
377                                 fprintf(stderr, "%s: Failed parsing listen section\n", progname);
378                                 exit(1);
379                         }
380
381                         if (!file) {
382                                 fprintf(stderr, "%s: No path given for socket\n",
383                                         progname);
384                                 exit(1);
385                         }
386                         break;
387                 }
388
389                 if (!file) {
390                         fprintf(stderr, "%s: Could not find control socket in %s\n",
391                                 progname, buffer);
392                         exit(1);
393                 }
394         }
395
396         if (input_file) {
397                 inputfp = fopen(input_file, "r");
398                 if (!inputfp) {
399                         fprintf(stderr, "%s: Failed opening %s: %s\n",
400                                 progname, input_file, strerror(errno));
401                         exit(1);
402                 }
403         }
404
405         if (output_file) {
406                 outputfp = fopen(output_file, "w");
407                 if (!outputfp) {
408                         fprintf(stderr, "%s: Failed creating %s: %s\n",
409                                 progname, output_file, strerror(errno));
410                         exit(1);
411                 }
412         }
413
414         /*
415          *      Check if stdin is a TTY only if input is from stdin
416          */
417         if (input_file && !quiet && !isatty(STDIN_FILENO)) quiet = 1;
418
419 #ifdef HAVE_LIBREADLINE
420         if (!quiet) {
421                 using_history();
422                 rl_bind_key('\t', rl_insert);
423         }
424 #endif
425
426  reconnect:
427         /*
428          *      FIXME: Get destination from command line, if possible?
429          */
430         sockfd = fr_domain_socket(file);
431         if (sockfd < 0) {
432                 exit(1);
433         }
434
435         /*
436          *      Read initial magic && version information.
437          */
438         for (size = 0; size < 8; size += len) {
439                 len = read(sockfd, buffer + size, 8 - size);
440                 if (len < 0) {
441                         fprintf(stderr, "%s: Error reading initial data from socket: %s\n",
442                                 progname, strerror(errno));
443                         exit(1);
444                 }
445         }
446
447         memcpy(&magic, buffer, 4);
448         magic = ntohl(magic);
449         if (magic != 0xf7eead15) {
450                 fprintf(stderr, "%s: Socket %s is not FreeRADIUS administration socket\n", progname, file);
451                 exit(1);
452         }
453         
454         memcpy(&magic, buffer + 4, 4);
455         magic = ntohl(magic);
456         if (magic != 1) {
457                 fprintf(stderr, "%s: Socket version mismatch: Need 1, got %d\n",
458                         progname, magic);
459                 exit(1);
460         }       
461
462         /*
463          *      Run one command.
464          */
465         if (num_commands >= 0) {
466                 int i;
467
468                 for (i = 0; i <= num_commands; i++) {
469                         size = run_command(sockfd, commands[i],
470                                            buffer, sizeof(buffer));
471                         if (size < 0) exit(1);
472                         
473                         fputs(buffer, outputfp);
474                         fprintf(outputfp, "\n");
475                         fflush(outputfp);
476                 }
477                 exit(0);
478         }
479
480         if (!done_license && !quiet) {
481                 printf("radmin " RADIUSD_VERSION " - FreeRADIUS Server administration tool.\n");
482                 printf("Copyright (C) 2008 The FreeRADIUS server project and contributors.\n");
483                 printf("There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n");
484                 printf("PARTICULAR PURPOSE.\n");
485                 printf("You may redistribute copies of FreeRADIUS under the terms of the\n");
486                 printf("GNU General Public License v2.\n");
487
488                 done_license = 1;
489         }
490
491         /*
492          *      FIXME: Do login?
493          */
494
495         while (1) {
496 #ifndef HAVE_LIBREADLINE
497                 if (!quiet) {
498                         printf("radmin> ");
499                         fflush(stdout);
500                 }
501 #else
502                 if (!quiet) {
503                         line = readline("radmin> ");
504                         
505                         if (!line) break;
506                         
507                         if (!*line) {
508                                 free(line);
509                                 continue;
510                         }
511                         
512                         add_history(line);
513                 } else          /* quiet, or no readline */
514 #endif
515                 {
516                         line = fgets(buffer, sizeof(buffer), inputfp);
517                         if (!line) break;
518
519                         p = strchr(buffer, '\n');
520                         if (!p) {
521                                 fprintf(stderr, "%s: Input line too long\n",
522                                         progname);
523                                 exit(1);
524                         }
525                         
526                         *p = '\0';
527
528                         /*
529                          *      Strip off leading spaces.
530                          */
531                         for (p = line; *p != '\0'; p++) {
532                                 if ((p[0] == ' ') ||
533                                     (p[0] == '\t')) {
534                                         line = p + 1;
535                                         continue;
536                                 }
537
538                                 if (p[0] == '#') {
539                                         line = NULL;
540                                         break;
541                                 }
542
543                                 break;
544                         }
545
546                         /*
547                          *      Comments: keep going.
548                          */
549                         if (!line) continue;
550
551                         /*
552                          *      Strip off CR / LF
553                          */
554                         for (p = line; *p != '\0'; p++) {
555                                 if ((p[0] == '\r') ||
556                                     (p[0] == '\n')) {
557                                         p[0] = '\0';
558                                         break;
559                                 }
560                         }
561                 }
562
563                 if (strcmp(line, "reconnect") == 0) {
564                         close(sockfd);
565                         line = NULL;
566                         goto reconnect;
567                 }
568
569                 /*
570                  *      Exit, done, etc.
571                  */
572                 if ((strcmp(line, "exit") == 0) ||
573                     (strcmp(line, "quit") == 0)) {
574                         break;
575                 }
576
577                 size = run_command(sockfd, line, buffer, sizeof(buffer));
578                 if (size <= 0) break; /* error, or clean exit */
579
580                 if (size == 1) continue; /* no output. */
581
582                 fputs(buffer, outputfp);
583                 fflush(outputfp);
584                 fprintf(outputfp, "\n");
585         }
586
587         fprintf(outputfp, "\n");
588
589         return 0;
590 }
591