Corrected typo
[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 #ifdef MSG_DONTWAIT
193                 len = recv(sockfd, buffer + size,
194                            bufsize - size - 1, MSG_DONTWAIT);
195 #else
196                 /*
197                  *      Read one byte at a time (ugh)
198                  */
199                 len = recv(sockfd, buffer + size, 1, 0);
200 #endif
201                 if (len < 0) {
202                         /*
203                          *      No data: keep looping
204                          */
205                         if ((errno == EAGAIN) || (errno == EINTR)) {
206                                 continue;
207                         }
208
209                         fprintf(stderr, "%s: Error reading socket: %s\n",
210                                 progname, strerror(errno));
211                         exit(1);
212                 }
213                 if (len == 0) return 0; /* clean exit */
214
215                 size += len;
216                 buffer[size] = '\0';
217
218                 /*
219                  *      There really is a better way of doing this.
220                  */
221                 p = strstr(buffer, "radmin> ");
222                 if (p &&
223                     ((p == buffer) || 
224                      (p[-1] == '\n') ||
225                      (p[-1] == '\r'))) {
226                         *p = '\0';
227
228                         if (p[-1] == '\n') p[-1] = '\0';
229
230                         flag = 0;
231                         break;
232                 }
233         }
234
235         /*
236          *      Blank prompt.  Go get another command.
237          */
238         if (!buffer[0]) return 1;
239
240         buffer[size] = '\0'; /* this is at least right */
241
242         return 2;
243 }
244
245
246 int main(int argc, char **argv)
247 {
248         int argval, quiet = 0;
249         int done_license = 0;
250         int sockfd;
251         uint32_t magic;
252         char *line = NULL;
253         ssize_t len, size;
254         const char *file = NULL;
255         const char *name = "radiusd";
256         char *p, buffer[65536];
257
258         if ((progname = strrchr(argv[0], FR_DIR_SEP)) == NULL)
259                 progname = argv[0];
260         else
261                 progname++;
262
263         while ((argval = getopt(argc, argv, "d:he:f:n:q")) != EOF) {
264                 switch(argval) {
265                 case 'd':
266                         if (file) {
267                                 fprintf(stderr, "%s: -d and -f cannot be used together.\n", progname);
268                                 exit(1);
269                         }
270                         radius_dir = optarg;
271                         break;
272
273                 case 'e':
274                         line = optarg;
275                         break;
276
277                 case 'f':
278                         radius_dir = NULL;
279                         file = optarg;
280                         break;
281
282                 default:
283                 case 'h':
284                         usage();
285                         break;
286
287                 case 'n':
288                         name = optarg;
289                         break;
290
291                 case 'q':
292                         quiet = 1;
293                         break;
294                 }
295         }
296
297         if (radius_dir) {
298                 int rcode;
299                 CONF_SECTION *cs, *subcs;
300
301                 file = NULL;    /* MUST read it from the conffile now */
302
303                 snprintf(buffer, sizeof(buffer), "%s/%s.conf",
304                          radius_dir, name);
305
306                 cs = cf_file_read(buffer);
307                 if (!cs) {
308                         fprintf(stderr, "%s: Errors reading %s\n",
309                                 progname, buffer);
310                         exit(1);
311                 }
312
313                 subcs = NULL;
314                 while ((subcs = cf_subsection_find_next(cs, subcs, "listen")) != NULL) {
315                         const char *value;
316                         CONF_PAIR *cp = cf_pair_find(subcs, "type");
317                         
318                         if (!cp) continue;
319
320                         value = cf_pair_value(cp);
321                         if (!value) continue;
322
323                         if (strcmp(value, "control") != 0) continue;
324
325                         /*
326                          *      Now find the socket name (sigh)
327                          */
328                         rcode = cf_item_parse(subcs, "socket",
329                                               PW_TYPE_STRING_PTR,
330                                               &file, NULL);
331                         if (rcode < 0) {
332                                 fprintf(stderr, "%s: Failed parsing listen section\n", progname);
333                                 exit(1);
334                         }
335
336                         if (!file) {
337                                 fprintf(stderr, "%s: No path given for socket\n",
338                                         progname);
339                                 exit(1);
340                         }
341                         break;
342                 }
343
344                 if (!file) {
345                         fprintf(stderr, "%s: Could not find control socket in %s\n",
346                                 progname, buffer);
347                         exit(1);
348                 }
349         }
350
351         if (!isatty(STDIN_FILENO)) quiet = 1;
352
353 #ifdef HAVE_LIBREADLINE
354         if (!quiet) {
355                 using_history();
356                 rl_bind_key('\t', rl_insert);
357         }
358 #endif
359
360  reconnect:
361         /*
362          *      FIXME: Get destination from command line, if possible?
363          */
364         sockfd = fr_domain_socket(file);
365         if (sockfd < 0) {
366                 exit(1);
367         }
368
369         /*
370          *      Read initial magic && version information.
371          */
372         for (size = 0; size < 8; size += len) {
373                 len = read(sockfd, buffer + size, 8 - size);
374                 if (len < 0) {
375                         fprintf(stderr, "%s: Error reading initial data from socket: %s\n",
376                                 progname, strerror(errno));
377                         exit(1);
378                 }
379         }
380
381         memcpy(&magic, buffer, 4);
382         magic = ntohl(magic);
383         if (magic != 0xf7eead15) {
384                 fprintf(stderr, "%s: Socket %s is not FreeRADIUS administration socket\n", progname, file);
385                 exit(1);
386         }
387         
388         memcpy(&magic, buffer + 4, 4);
389         magic = ntohl(magic);
390         if (magic != 1) {
391                 fprintf(stderr, "%s: Socket version mismatch: Need 1, got %d\n",
392                         progname, magic);
393                 exit(1);
394         }       
395
396         /*
397          *      Run one command.
398          */
399         if (line) {
400                 size = run_command(sockfd, line, buffer, sizeof(buffer));
401                 if (size < 0) exit(1);
402                 if ((size == 0) || (size == 1)) exit(0);
403
404                 puts(buffer);
405                 fflush(stdout);
406                 exit(0);
407         }
408
409         if (!done_license && !quiet) {
410                 printf("radmin " RADIUSD_VERSION " - FreeRADIUS Server administration tool.\n");
411                 printf("Copyright (C) 2008 The FreeRADIUS server project and contributors.\n");
412                 printf("There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n");
413                 printf("PARTICULAR PURPOSE.\n");
414                 printf("You may redistribute copies of FreeRADIUS under the terms of the\n");
415                 printf("GNU General Public License v2.\n");
416
417                 done_license = 1;
418         }
419
420         /*
421          *      FIXME: Do login?
422          */
423
424         while (1) {
425 #ifndef HAVE_LIBREADLINE
426                 if (!quiet) {
427                         printf("radmin> ");
428                         fflush(stdout);
429                 }
430 #else
431                 if (!quiet) {
432                         line = readline("radmin> ");
433                         
434                         if (!line) break;
435                         
436                         if (!*line) {
437                                 free(line);
438                                 continue;
439                         }
440                         
441                         add_history(line);
442                 } else          /* quiet, or no readline */
443 #endif
444                 {
445                         line = fgets(buffer, sizeof(buffer), stdin);
446                         if (!line) break;
447                         
448                         p = strchr(buffer, '\n');
449                         if (!p) {
450                                 fprintf(stderr, "%s: Input line too long\n",
451                                         progname);
452                                 exit(1);
453                         }
454                         
455                         *p = '\0';
456                 }
457
458                 if (strcmp(line, "reconnect") == 0) {
459                         close(sockfd);
460                         line = NULL;
461                         goto reconnect;
462                 }
463
464                 /*
465                  *      Exit, done, etc.
466                  */
467                 if ((strcmp(line, "exit") == 0) ||
468                     (strcmp(line, "quit") == 0)) {
469                         break;
470                 }
471
472                 size = run_command(sockfd, line, buffer, sizeof(buffer));
473                 if (size <= 0) break; /* error, or clean exit */
474
475                 if (size == 1) continue; /* no output. */
476
477                 puts(buffer);
478                 fflush(stdout);
479         }
480
481         printf("\n");
482
483         return 0;
484 }
485