#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/radpaths.h>
-#ifdef HAVE_READLINE_READLINE_H
-#include <readline/readline.h>
-#include <readline/history.h>
-#endif
-
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
+#ifndef SUN_LEN
+#define SUN_LEN(su) (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
+#endif
#endif
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_LIBREADLINE
+
+#if defined(HAVE_READLINE_READLINE_H)
+#include <readline/readline.h>
+#define USE_READLINE (1)
+#elif defined(HAVE_READLINE_H)
+#include <readline.h>
+#define USE_READLINE (1)
+#endif /* !defined(HAVE_READLINE_H) */
+
+#endif /* HAVE_LIBREADLINE */
+
+#ifdef HAVE_READLINE_HISTORY
+#if defined(HAVE_READLINE_HISTORY_H)
+#include <readline/history.h>
+#define USE_READLINE_HISTORY (1)
+#elif defined(HAVE_HISTORY_H)
+#include <history.h>
+#define USE_READLINE_HISTORY (1)
+#endif /* defined(HAVE_READLINE_HISTORY_H) */
+
+#endif /* HAVE_READLINE_HISTORY */
+
/*
* For configuration file stuff.
*/
-const char *radius_dir = RADDBDIR;
+char *radius_dir = RADDBDIR;
const char *progname = "radmin";
/*
return -1;
}
+static FILE *outputfp = NULL;
+static int echo = FALSE;
+
static int fr_domain_socket(const char *path)
{
- int sockfd;
+ int sockfd = -1;
+#ifdef HAVE_SYS_UN_H
size_t len;
socklen_t socklen;
struct sockaddr_un saremote;
}
saremote.sun_family = AF_UNIX;
- memcpy(saremote.sun_path, path, len); /* not zero terminated */
+ memcpy(saremote.sun_path, path, len + 1); /* SUN_LEN does strlen */
- socklen = sizeof(saremote.sun_family) + len;
+ socklen = SUN_LEN(&saremote);
if (connect(sockfd, (struct sockaddr *)&saremote, socklen) < 0) {
+ struct stat buf;
+
+ close(sockfd);
fprintf(stderr, "%s: Failed connecting to %s: %s\n",
progname, path, strerror(errno));
- close(sockfd);
+
+ /*
+ * The file doesn't exist. Tell the user how to
+ * fix it.
+ */
+ if ((stat(path, &buf) < 0) &&
+ (errno == ENOENT)) {
+ fprintf(stderr, " Perhaps you need to run the commands:\n\tcd /etc/raddb\n\tln -s sites-available/control-socket sites-enabled/control-socket\n and then re-start the server?\n");
+ }
+
return -1;
}
}
}
#endif
-
+#endif
return sockfd;
}
static int usage(void)
{
- printf("Usage: %s [ -d raddb_dir ] [ -f socket_file ] [ -n name ] [ -q ]\n", progname);
+ printf("Usage: %s [ args ]\n", progname);
printf(" -d raddb_dir Configuration files are in \"raddbdir/*\".\n");
+ printf(" -e command Execute 'command' and then exit.\n");
+ printf(" -E Echo commands as they are being executed.\n");
printf(" -f socket_file Open socket_file directly, without reading radius.conf\n");
+ printf(" -i input_file Read commands from 'input_file'.\n");
printf(" -n name Read raddb/name.conf instead of raddb/radiusd.conf\n");
+ printf(" -o output_file Write commands to 'output_file'.\n");
printf(" -q Quiet mode.\n");
exit(1);
}
+static ssize_t run_command(int sockfd, const char *command,
+ char *buffer, size_t bufsize)
+{
+ char *p;
+ ssize_t size, len;
+
+ if (echo) {
+ fprintf(outputfp, "%s\n", command);
+ }
+
+ /*
+ * Write the text to the socket.
+ */
+ if (write(sockfd, command, strlen(command)) < 0) return -1;
+ if (write(sockfd, "\r\n", 2) < 0) return -1;
+
+ /*
+ * Read the response
+ */
+ size = 0;
+ buffer[0] = '\0';
+
+ memset(buffer, 0, bufsize);
+
+ while (1) {
+ int rcode;
+ fd_set readfds;
+
+ FD_ZERO(&readfds);
+ FD_SET(sockfd, &readfds);
+
+ rcode = select(sockfd + 1, &readfds, NULL, NULL, NULL);
+ if (rcode < 0) {
+ if (errno == EINTR) continue;
+
+ fprintf(stderr, "%s: Failed selecting: %s\n",
+ progname, strerror(errno));
+ exit(1);
+ }
+
+#ifdef MSG_DONTWAIT
+ len = recv(sockfd, buffer + size,
+ bufsize - size - 1, MSG_DONTWAIT);
+#else
+ /*
+ * Read one byte at a time (ugh)
+ */
+ len = recv(sockfd, buffer + size, 1, 0);
+#endif
+ if (len < 0) {
+ /*
+ * No data: keep looping
+ */
+ if ((errno == EAGAIN) || (errno == EINTR)) {
+ continue;
+ }
+
+ fprintf(stderr, "%s: Error reading socket: %s\n",
+ progname, strerror(errno));
+ exit(1);
+ }
+ if (len == 0) return 0; /* clean exit */
+
+ size += len;
+ buffer[size] = '\0';
+
+ /*
+ * There really is a better way of doing this.
+ */
+ p = strstr(buffer, "radmin> ");
+ if (p &&
+ ((p == buffer) ||
+ (p[-1] == '\n') ||
+ (p[-1] == '\r'))) {
+ *p = '\0';
+
+ if (p[-1] == '\n') p[-1] = '\0';
+ break;
+ }
+ }
+
+ /*
+ * Blank prompt. Go get another command.
+ */
+ if (!buffer[0]) return 1;
+
+ buffer[size] = '\0'; /* this is at least right */
+
+ return 2;
+}
+
+#define MAX_COMMANDS (4)
+
int main(int argc, char **argv)
{
int argval, quiet = 0;
int done_license = 0;
- int sockfd, port;
+ int sockfd;
uint32_t magic;
- char *line;
+ char *line = NULL;
ssize_t len, size;
const char *file = NULL;
const char *name = "radiusd";
- char *p, buffer[2048];
+ char *p, buffer[65536];
+ const char *input_file = NULL;
+ FILE *inputfp = stdin;
+ const char *output_file = NULL;
+
+ char *commands[MAX_COMMANDS];
+ int num_commands = -1;
+
+ outputfp = stdout; /* stdout is not a constant value... */
if ((progname = strrchr(argv[0], FR_DIR_SEP)) == NULL)
progname = argv[0];
else
progname++;
- while ((argval = getopt(argc, argv, "d:hf:n:q")) != EOF) {
+ while ((argval = getopt(argc, argv, "d:hi:e:Ef:n:o:q")) != EOF) {
switch(argval) {
case 'd':
if (file) {
radius_dir = optarg;
break;
+ case 'e':
+ num_commands++; /* starts at -1 */
+ if (num_commands >= MAX_COMMANDS) {
+ fprintf(stderr, "%s: Too many '-e'\n",
+ progname);
+ exit(1);
+ }
+ commands[num_commands] = optarg;
+ break;
+
+ case 'E':
+ echo = TRUE;
+ break;
+
case 'f':
radius_dir = NULL;
file = optarg;
usage();
break;
+ case 'i':
+ if (strcmp(optarg, "-") != 0) {
+ input_file = optarg;
+ }
+ quiet = 1;
+ break;
+
case 'n':
name = optarg;
break;
+ case 'o':
+ if (strcmp(optarg, "-") != 0) {
+ output_file = optarg;
+ }
+ quiet = 1;
+ break;
+
case 'q':
quiet = 1;
break;
}
}
- if (!isatty(STDIN_FILENO)) quiet = 1;
+ if (input_file) {
+ inputfp = fopen(input_file, "r");
+ if (!inputfp) {
+ fprintf(stderr, "%s: Failed opening %s: %s\n",
+ progname, input_file, strerror(errno));
+ exit(1);
+ }
+ }
+
+ if (output_file) {
+ outputfp = fopen(output_file, "w");
+ if (!outputfp) {
+ fprintf(stderr, "%s: Failed creating %s: %s\n",
+ progname, output_file, strerror(errno));
+ exit(1);
+ }
+ }
-#ifdef HAVE_READLINE_READLINE_H
+ /*
+ * Check if stdin is a TTY only if input is from stdin
+ */
+ if (input_file && !quiet && !isatty(STDIN_FILENO)) quiet = 1;
+
+#ifdef USE_READLINE
if (!quiet) {
+#ifdef USE_READLINE_HISTORY
using_history();
+#endif
rl_bind_key('\t', rl_insert);
}
#endif
exit(1);
}
+ /*
+ * Run one command.
+ */
+ if (num_commands >= 0) {
+ int i;
+
+ for (i = 0; i <= num_commands; i++) {
+ size = run_command(sockfd, commands[i],
+ buffer, sizeof(buffer));
+ if (size < 0) exit(1);
+
+ if (buffer[0]) {
+ fputs(buffer, outputfp);
+ fprintf(outputfp, "\n");
+ fflush(outputfp);
+ }
+ }
+ exit(0);
+ }
+
if (!done_license && !quiet) {
printf("radmin " RADIUSD_VERSION " - FreeRADIUS Server administration tool.\n");
printf("Copyright (C) 2008 The FreeRADIUS server project and contributors.\n");
*/
while (1) {
+#ifndef USE_READLINE
if (!quiet) {
-#ifndef HAVE_READLINE_READLINE_H
printf("radmin> ");
fflush(stdout);
+ }
#else
+ if (!quiet) {
line = readline("radmin> ");
if (!line) break;
continue;
}
+#ifdef USE_READLINE_HISTORY
add_history(line);
#endif
- } else { /* quiet, or no readline */
-
- line = fgets(buffer, sizeof(buffer), stdin);
+ } else /* quiet, or no readline */
+#endif
+ {
+ line = fgets(buffer, sizeof(buffer), inputfp);
if (!line) break;
-
+
p = strchr(buffer, '\n');
if (!p) {
fprintf(stderr, "%s: Input line too long\n",
}
*p = '\0';
+
+ /*
+ * Strip off leading spaces.
+ */
+ for (p = line; *p != '\0'; p++) {
+ if ((p[0] == ' ') ||
+ (p[0] == '\t')) {
+ line = p + 1;
+ continue;
+ }
+
+ if (p[0] == '#') {
+ line = NULL;
+ break;
+ }
+
+ break;
+ }
+
+ /*
+ * Comments: keep going.
+ */
+ if (!line) continue;
+
+ /*
+ * Strip off CR / LF
+ */
+ for (p = line; *p != '\0'; p++) {
+ if ((p[0] == '\r') ||
+ (p[0] == '\n')) {
+ p[0] = '\0';
+ break;
+ }
+ }
}
if (strcmp(line, "reconnect") == 0) {
close(sockfd);
+ line = NULL;
goto reconnect;
}
/*
- * Write the text to the socket.
- */
- if (write(sockfd, line, strlen(line)) < 0) break;
- if (write(sockfd, "\r\n", 2) < 0) break;
-
- /*
* Exit, done, etc.
*/
if ((strcmp(line, "exit") == 0) ||
break;
}
- /*
- * Read the response
- */
- size = 0;
- buffer[0] = '\0';
-
- port = 1;
- memset(buffer, 0, sizeof(buffer));
-
- while (port == 1) {
- int rcode;
- fd_set readfds;
-
- FD_ZERO(&readfds);
- FD_SET(sockfd, &readfds);
-
- rcode = select(sockfd + 1, &readfds, NULL, NULL, NULL);
- if (rcode < 0) {
- if (errno == EINTR) continue;
+ size = run_command(sockfd, line, buffer, sizeof(buffer));
+ if (size <= 0) break; /* error, or clean exit */
- fprintf(stderr, "%s: Failed selecting: %s\n",
- progname, strerror(errno));
- exit(1);
- }
-
- len = recv(sockfd, buffer + size,
- sizeof(buffer) - size - 1, MSG_DONTWAIT);
- if (len < 0) {
- /*
- * No data: keep looping
- */
- if ((errno == EAGAIN) || (errno == EINTR)) {
- continue;
- }
-
- fprintf(stderr, "%s: Error reading socket: %s\n",
- progname, strerror(errno));
- exit(1);
- }
- if (len == 0) break; /* clean close of socket */
-
- size += len;
- buffer[size] = '\0';
-
- /*
- * There really has to be a better way of
- * doing this.
- */
- p = strstr(buffer, "radmin> ");
- if (p &&
- ((p == buffer) ||
- (p[-1] == '\n') ||
- (p[-1] == '\r'))) {
- *p = '\0';
-
- if (p[-1] == '\n') p[-1] = '\0';
-
- port = 0;
- break;
- }
- }
-
- /*
- * Blank prompt. Go get another line.
- */
- if (!buffer[0]) continue;
+ if (size == 1) continue; /* no output. */
- buffer[size] = '\0'; /* this is at least right */
- puts(buffer);
- fflush(stdout);
+ fputs(buffer, outputfp);
+ fflush(outputfp);
+ fprintf(outputfp, "\n");
}
- printf("\n");
+ fprintf(outputfp, "\n");
return 0;
}