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