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