d4661b89493cd4b2b392fc04fc91b85f41afe4be
[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_READLINE_READLINE_H
31 #include <readline/readline.h>
32 #include <readline/history.h>
33 #endif
34
35 #ifdef HAVE_SYS_SOCKET_H
36 #include <sys/socket.h>
37 #endif
38
39 #ifdef HAVE_SYS_UN_H
40 #include <sys/un.h>
41 #endif
42
43 #ifdef HAVE_GETOPT_H
44 #include <getopt.h>
45 #endif
46
47 /*
48  *      For configuration file stuff.
49  */
50 const char *radius_dir = RADDBDIR;
51 const char *progname = "radmin";
52
53 /*
54  *      The rest of this is because the conffile.c, etc. assume
55  *      they're running inside of the server.  And we don't (yet)
56  *      have a "libfreeradius-server", or "libfreeradius-util".
57  */
58 int debug_flag = 0;
59 struct main_config_t mainconfig;
60 char *request_log_file = NULL;
61 char *debug_log_file = NULL;
62 int radius_xlat(UNUSED char *out, UNUSED int outlen, UNUSED const char *fmt,
63                 UNUSED REQUEST *request, UNUSED RADIUS_ESCAPE_STRING func)
64 {
65         return -1;
66 }
67
68 static int fr_domain_socket(const char *path)
69 {
70         int sockfd;
71         size_t len;
72         socklen_t socklen;
73         struct sockaddr_un saremote;
74
75         len = strlen(path);
76         if (len >= sizeof(saremote.sun_path)) {
77                 fprintf(stderr, "%s: Path too long in filename\n", progname);
78                 return -1;
79         }
80
81         if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
82                 fprintf(stderr, "%s: Failed creating socket: %s\n",
83                         progname, strerror(errno));
84                 return -1;
85         }
86
87         saremote.sun_family = AF_UNIX;
88         memcpy(saremote.sun_path, path, len); /* not zero terminated */
89         
90         socklen = sizeof(saremote.sun_family) + len;
91
92         if (connect(sockfd, (struct sockaddr *)&saremote, socklen) < 0) {
93                 fprintf(stderr, "%s: Failed connecting to %s: %s\n",
94                         progname, path, strerror(errno));
95                 close(sockfd);
96                 return -1;
97         }
98
99 #ifdef O_NONBLOCK
100         {
101                 int flags;
102                 
103                 if ((flags = fcntl(sockfd, F_GETFL, NULL)) < 0)  {
104                         fprintf(stderr, "%s: Failure getting socket flags: %s",
105                                 progname, strerror(errno));
106                         close(sockfd);
107                         return -1;
108                 }
109                 
110                 flags |= O_NONBLOCK;
111                 if( fcntl(sockfd, F_SETFL, flags) < 0) {
112                         fprintf(stderr, "%s: Failure setting socket flags: %s",
113                                 progname, strerror(errno));
114                         close(sockfd);
115                         return -1;
116                 }
117         }
118 #endif
119
120         return sockfd;
121 }
122
123 static int usage(void)
124 {
125         printf("Usage: %s [ -d raddb_dir ] [ -f socket_file ] [ -n name ] [ -q ]\n", progname);
126         printf("  -d raddb_dir    Configuration files are in \"raddbdir/*\".\n");
127         printf("  -f socket_file  Open socket_file directly, without reading radius.conf\n");
128         printf("  -n name         Read raddb/name.conf instead of raddb/radiusd.conf\n");
129         printf("  -q              Quiet mode.\n");
130
131         exit(1);
132 }
133
134 int main(int argc, char **argv)
135 {
136         int argval, quiet = 0;
137         int done_license = 0;
138         int sockfd, port;
139         uint32_t magic;
140         char *line;
141         ssize_t len, size;
142         const char *file = NULL;
143         const char *name = "radiusd";
144         char *p, buffer[2048];
145
146         if ((progname = strrchr(argv[0], FR_DIR_SEP)) == NULL)
147                 progname = argv[0];
148         else
149                 progname++;
150
151         while ((argval = getopt(argc, argv, "d:hf:n:q")) != EOF) {
152                 switch(argval) {
153                 case 'd':
154                         if (file) {
155                                 fprintf(stderr, "%s: -d and -f cannot be used together.\n", progname);
156                                 exit(1);
157                         }
158                         radius_dir = optarg;
159                         break;
160
161                 case 'f':
162                         radius_dir = NULL;
163                         file = optarg;
164                         break;
165
166                 default:
167                 case 'h':
168                         usage();
169                         break;
170
171                 case 'n':
172                         name = optarg;
173                         break;
174
175                 case 'q':
176                         quiet = 1;
177                         break;
178                 }
179         }
180
181         if (radius_dir) {
182                 int rcode;
183                 CONF_SECTION *cs, *subcs;
184
185                 file = NULL;    /* MUST read it from the conffile now */
186
187                 snprintf(buffer, sizeof(buffer), "%s/%s.conf",
188                          radius_dir, name);
189
190                 cs = cf_file_read(buffer);
191                 if (!cs) {
192                         fprintf(stderr, "%s: Errors reading %s\n",
193                                 progname, buffer);
194                         exit(1);
195                 }
196
197                 subcs = NULL;
198                 while ((subcs = cf_subsection_find_next(cs, subcs, "listen")) != NULL) {
199                         const char *value;
200                         CONF_PAIR *cp = cf_pair_find(subcs, "type");
201                         
202                         if (!cp) continue;
203
204                         value = cf_pair_value(cp);
205                         if (!value) continue;
206
207                         if (strcmp(value, "control") != 0) continue;
208
209                         /*
210                          *      Now find the socket name (sigh)
211                          */
212                         rcode = cf_item_parse(subcs, "socket",
213                                               PW_TYPE_STRING_PTR,
214                                               &file, NULL);
215                         if (rcode < 0) {
216                                 fprintf(stderr, "%s: Failed parsing listen section\n", progname);
217                                 exit(1);
218                         }
219
220                         if (!file) {
221                                 fprintf(stderr, "%s: No path given for socket\n",
222                                         progname);
223                                 exit(1);
224                         }
225                         break;
226                 }
227
228                 if (!file) {
229                         fprintf(stderr, "%s: Could not find control socket in %s\n",
230                                 progname, buffer);
231                         exit(1);
232                 }
233         }
234
235         if (!isatty(STDIN_FILENO)) quiet = 1;
236
237 #ifdef HAVE_READLINE_READLINE_H
238         if (!quiet) {
239                 using_history();
240                 rl_bind_key('\t', rl_insert);
241         }
242 #endif
243
244  reconnect:
245         /*
246          *      FIXME: Get destination from command line, if possible?
247          */
248         sockfd = fr_domain_socket(file);
249         if (sockfd < 0) {
250                 exit(1);
251         }
252
253         /*
254          *      Read initial magic && version information.
255          */
256         for (size = 0; size < 8; size += len) {
257                 len = read(sockfd, buffer + size, 8 - size);
258                 if (len < 0) {
259                         fprintf(stderr, "%s: Error reading initial data from socket: %s\n",
260                                 progname, strerror(errno));
261                         exit(1);
262                 }
263         }
264
265         memcpy(&magic, buffer, 4);
266         magic = ntohl(magic);
267         if (magic != 0xf7eead15) {
268                 fprintf(stderr, "%s: Socket %s is not FreeRADIUS administration socket\n", progname, file);
269                 exit(1);
270         }
271         
272         memcpy(&magic, buffer + 4, 4);
273         magic = ntohl(magic);
274         if (magic != 1) {
275                 fprintf(stderr, "%s: Socket version mismatch: Need 1, got %d\n",
276                         progname, magic);
277                 exit(1);
278         }       
279
280         if (!done_license && !quiet) {
281                 printf("radmin " RADIUSD_VERSION " - FreeRADIUS Server administration tool.\n");
282                 printf("Copyright (C) 2008 The FreeRADIUS server project and contributors.\n");
283                 printf("There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n");
284                 printf("PARTICULAR PURPOSE.\n");
285                 printf("You may redistribute copies of FreeRADIUS under the terms of the\n");
286                 printf("GNU General Public License v2.\n");
287
288                 done_license = 1;
289         }
290
291         /*
292          *      FIXME: Do login?
293          */
294
295         while (1) {
296                 if (!quiet) {
297 #ifndef HAVE_READLINE_READLINE_H
298                         printf("radmin> ");
299                         fflush(stdout);
300 #else
301                         line = readline("radmin> ");
302                         
303                         if (!line) break;
304                         
305                         if (!*line) {
306                                 free(line);
307                                 continue;
308                         }
309                         
310                         add_history(line);
311 #endif
312                 } else {        /* quiet, or no readline */
313                         
314                         line = fgets(buffer, sizeof(buffer), stdin);
315                         if (!line) break;
316                         
317                         p = strchr(buffer, '\n');
318                         if (!p) {
319                                 fprintf(stderr, "%s: Input line too long\n",
320                                         progname);
321                                 exit(1);
322                         }
323                         
324                         *p = '\0';
325                 }
326
327                 if (strcmp(line, "reconnect") == 0) {
328                         close(sockfd);
329                         goto reconnect;
330                 }
331
332                 /*
333                  *      Write the text to the socket.
334                  */
335                 if (write(sockfd, line, strlen(line)) < 0) break;
336                 if (write(sockfd, "\r\n", 2) < 0) break;
337
338                 /*
339                  *      Exit, done, etc.
340                  */
341                 if ((strcmp(line, "exit") == 0) ||
342                     (strcmp(line, "quit") == 0)) {
343                         break;
344                 }
345
346                 /*
347                  *      Read the response
348                  */
349                 size = 0;
350                 buffer[0] = '\0';
351
352                 port = 1;
353                 memset(buffer, 0, sizeof(buffer));
354
355                 while (port == 1) {
356                         int rcode;
357                         fd_set readfds;
358
359                         FD_ZERO(&readfds);
360                         FD_SET(sockfd, &readfds);
361
362                         rcode = select(sockfd + 1, &readfds, NULL, NULL, NULL);
363                         if (rcode < 0) {
364                                 if (errno == EINTR) continue;
365
366                                 fprintf(stderr, "%s: Failed selecting: %s\n",
367                                         progname, strerror(errno));
368                                 exit(1);
369                         }
370
371                         len = recv(sockfd, buffer + size,
372                                    sizeof(buffer) - size - 1, MSG_DONTWAIT);
373                         if (len < 0) {
374                                 /*
375                                  *      No data: keep looping
376                                  */
377                                 if ((errno == EAGAIN) || (errno == EINTR)) {
378                                         continue;
379                                 }
380
381                                 fprintf(stderr, "%s: Error reading socket: %s\n",
382                                         progname, strerror(errno));
383                                 exit(1);
384                         }
385                         if (len == 0) break; /* clean close of socket */
386
387                         size += len;
388                         buffer[size] = '\0';
389
390                         /*
391                          *      There really has to be a better way of
392                          *      doing this.
393                          */
394                         p = strstr(buffer, "radmin> ");
395                         if (p &&
396                             ((p == buffer) || 
397                              (p[-1] == '\n') ||
398                              (p[-1] == '\r'))) {
399                                 *p = '\0';
400
401                                 if (p[-1] == '\n') p[-1] = '\0';
402
403                                 port = 0;
404                                 break;
405                         }
406                 }
407
408                 /*
409                  *      Blank prompt.  Go get another line.
410                  */
411                 if (!buffer[0]) continue;
412
413                 buffer[size] = '\0'; /* this is at least right */
414                 puts(buffer);
415                 fflush(stdout);
416         }
417
418         printf("\n");
419
420         return 0;
421 }
422