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