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