r/w socket magic && version number
[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/libradius.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
44 static int fr_domain_socket(const char *path)
45 {
46         int sockfd;
47         size_t len;
48         socklen_t socklen;
49         struct sockaddr_un saremote;
50
51         len = strlen(path);
52         if (len >= sizeof(saremote.sun_path)) {
53                 fprintf(stderr, "Path too long in filename\n");
54                 return -1;
55         }
56
57         if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
58                 fprintf(stderr, "Failed creating socket: %s\n",
59                         strerror(errno));
60                 return -1;
61         }
62
63         saremote.sun_family = AF_UNIX;
64         memcpy(saremote.sun_path, path, len); /* not zero terminated */
65         
66         socklen = sizeof(saremote.sun_family) + len;
67
68         if (connect(sockfd, (struct sockaddr *)&saremote, socklen) < 0) {
69                 fprintf(stderr, "Failed connecting to %s: %s\n",
70                         path, strerror(errno));
71                 close(sockfd);
72                 return -1;
73         }
74
75 #ifdef O_NONBLOCK
76         {
77                 int flags;
78                 
79                 if ((flags = fcntl(sockfd, F_GETFL, NULL)) < 0)  {
80                         fprintf(stderr, "Failure getting socket flags: %s",
81                                 strerror(errno));
82                         close(sockfd);
83                         return -1;
84                 }
85                 
86                 flags |= O_NONBLOCK;
87                 if( fcntl(sockfd, F_SETFL, flags) < 0) {
88                         fprintf(stderr, "Failure setting socket flags: %s",
89                                 strerror(errno));
90                         close(sockfd);
91                         return -1;
92                 }
93         }
94 #endif
95
96         return sockfd;
97 }
98
99 int main(int argc, char **argv)
100 {
101         int sockfd, port;
102         uint32_t magic;
103         char *line;
104         ssize_t len, size;
105         const char *file = RUNDIR "/radiusd/radiusd.sock";
106         char *p, buffer[2048];
107
108         if ((argc > 2) ||
109             ((argc == 2) && (strcmp(argv[1], "-h") == 0))) {
110                 printf("Usage: radmin [socket]\n");
111                 exit(0);
112         }
113
114         if (argc == 2) file = argv[1];
115
116 #ifdef HAVE_READLINE_READLINE_H
117         using_history();
118         rl_bind_key('\t', rl_insert);
119 #endif
120
121         /*
122          *      FIXME: Get destination from command line, if possible?
123          */
124         sockfd = fr_domain_socket(file);
125         if (sockfd < 0) {
126                 exit(1);
127         }
128
129         /*
130          *      Read initial magic && version information.
131          */
132         for (size = 0; size < 8; size += len) {
133                 len = read(sockfd, buffer + size, 8 - size);
134                 if (len < 0) {
135                         fprintf(stderr, "Failed reading initial data from socket: %s\n",
136                                 strerror(errno));
137                         exit(1);
138                 }
139         }
140
141         memcpy(&magic, buffer, 4);
142         magic = ntohl(magic);
143         if (magic != 0xf7eead15) {
144                 fprintf(stderr, "Socket is not FreeRADIUS administration socket\n");
145                 exit(1);
146         }
147         
148         memcpy(&magic, buffer + 4, 4);
149         magic = ntohl(magic);
150         if (magic != 1) {
151                 fprintf(stderr, "Socket version mismatch: Need 1, got %d\n",
152                         magic);
153                 exit(1);
154         }       
155
156         /*
157          *      FIXME: Do login?
158          */
159
160         while (1) {
161 #ifndef HAVE_READLINE_READLINE_H
162                 fprintf(stdout, "radmin> ");
163                 fflush(stdout);
164
165                 line = fgets(buffer, sizeof(buffer), stdin);
166                 if (!line) break;
167
168                 p = strchr(buffer, '\n');
169                 if (!p) {
170                         fprintf(stderr, "line too long\n");
171                         exit(1);
172                 }
173
174                 *p = '\0';
175 #else
176                 line = readline("radmin> ");
177
178                 if (!line) break;
179                 
180                 if (!*line) {
181                         free(line);
182                         continue;
183                 }
184
185                 add_history(line);
186 #endif
187
188                 /*
189                  *      Write the text to the socket.
190                  */
191                 if (write(sockfd, line, strlen(line)) < 0) break;
192                 if (write(sockfd, "\r\n", 2) < 0) break;
193
194                 /*
195                  *      Exit, done, etc.
196                  */
197                 if ((strcmp(line, "exit") == 0) ||
198                     (strcmp(line, "quit") == 0)) {
199                         break;
200                 }
201
202                 /*
203                  *      Read the response
204                  */
205                 size = 0;
206                 buffer[0] = '\0';
207
208                 port = 1;
209                 memset(buffer, 0, sizeof(buffer));
210
211                 while (port == 1) {
212                         int rcode;
213                         fd_set readfds;
214
215                         FD_ZERO(&readfds);
216                         FD_SET(sockfd, &readfds);
217
218                         rcode = select(sockfd + 1, &readfds, NULL, NULL, NULL);
219                         if (rcode < 0) {
220                                 if (errno == EINTR) continue;
221
222                                 fprintf(stderr, "Failed selecting: %s\n",
223                                         strerror(errno));
224                                 exit(1);
225                         }
226
227                         len = recv(sockfd, buffer + size,
228                                    sizeof(buffer) - size - 1, MSG_DONTWAIT);
229                         if (len < 0) {
230                                 /*
231                                  *      No data: keep looping
232                                  */
233                                 if ((errno == EAGAIN) || (errno == EINTR)) {
234                                         continue;
235                                 }
236
237                                 fprintf(stderr, "Error reading socket: %s\n",
238                                         strerror(errno));
239                                 exit(1);
240                         }
241                         if (len == 0) break; /* clean close of socket */
242
243                         size += len;
244                         buffer[size] = '\0';
245
246                         /*
247                          *      There really has to be a better way of
248                          *      doing this.
249                          */
250                         p = strstr(buffer, "radmin> ");
251                         if (p &&
252                             ((p == buffer) || 
253                              (p[-1] == '\n') ||
254                              (p[-1] == '\r'))) {
255                                 *p = '\0';
256
257                                 if (p[-1] == '\n') p[-1] = '\0';
258
259                                 port = 0;
260                                 break;
261                         }
262                 }
263
264                 /*
265                  *      Blank prompt.  Go get another line.
266                  */
267                 if (!buffer[0]) continue;
268
269                 buffer[size] = '\0'; /* this is at least right */
270                 puts(buffer);
271                 fflush(stdout);
272         }
273
274         printf("\n");
275
276         return 0;
277 }
278