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