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