Files
ocserv/src/occtl.c
Nikos Mavrogiannopoulos 30bcf35576 Revert "license upgraded to GPLv3"
This reverts commit 213f9a63ee.

Conflicts:
	configure.ac
2014-09-24 11:34:15 +02:00

565 lines
12 KiB
C

/*
* Copyright (C) 2014 Red Hat
*
* Author: Nikos Mavrogiannopoulos
*
* This file is part of ocserv.
*
* ocserv is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* ocserv is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <signal.h>
#include <c-ctype.h>
#include <occtl.h>
#include <c-strcase.h>
static int handle_reset_cmd(CONN_TYPE * conn, const char *arg);
static int handle_help_cmd(CONN_TYPE * conn, const char *arg);
static int handle_exit_cmd(CONN_TYPE * conn, const char *arg);
typedef struct {
char *name;
unsigned name_size;
char *arg;
cmd_func func;
char *doc;
int always_show;
int need_preconn;
} commands_st;
#define ENTRY(name, arg, func, doc, show, npc) \
{name, sizeof(name)-1, arg, func, doc, show, npc}
static const commands_st commands[] = {
ENTRY("disconnect user", "[NAME]", handle_disconnect_user_cmd,
"Disconnect the specified user", 1, 1),
ENTRY("disconnect id", "[ID]", handle_disconnect_id_cmd,
"Disconnect the specified ID", 1, 1),
ENTRY("reload", NULL, handle_reload_cmd,
"Reloads the server configuration", 1, 1),
ENTRY("show status", NULL, handle_status_cmd,
"Prints the status of the server", 1, 1),
ENTRY("show users", NULL, handle_list_users_cmd,
"Prints the connected users", 1, 1),
ENTRY("show user", "[NAME]", handle_show_user_cmd,
"Prints information on the specified user", 1, 1),
ENTRY("show id", "[ID]", handle_show_id_cmd,
"Prints information on the specified ID", 1, 1),
ENTRY("stop", "now", handle_stop_cmd,
"Terminates the server", 1, 1),
ENTRY("reset", NULL, handle_reset_cmd, "Resets the screen and terminal",
0, 0),
ENTRY("help", "or ?", handle_help_cmd, "Prints this help", 0, 0),
ENTRY("exit", NULL, handle_exit_cmd, "Exits this application", 0, 0),
/* hidden options */
ENTRY("?", NULL, handle_help_cmd, "Prints this help", -1, 0),
ENTRY("quit", NULL, handle_exit_cmd, "Exits this application", -1, 0),
{NULL, 0, NULL, NULL}
};
static void print_commands(unsigned interactive)
{
unsigned int i;
printf("Available Commands\n");
for (i = 0;; i++) {
if (commands[i].name == NULL)
break;
if (commands[i].always_show == -1)
continue;
if (commands[i].always_show == 0 && interactive == 0)
continue;
if (commands[i].arg)
printf(" %12s %s\t%16s\n", commands[i].name,
commands[i].arg, commands[i].doc);
else
printf(" %16s\t%16s\n", commands[i].name,
commands[i].doc);
}
}
#ifndef HAVE_ORIG_READLINE
# define whitespace(x) c_isspace(x)
#endif
unsigned need_help(const char *arg)
{
while (whitespace(*arg))
arg++;
if (arg[0] == 0 || (arg[0] == '?' && arg[1] == 0))
return 1;
return 0;
}
unsigned check_cmd_help(const char *line)
{
unsigned int i;
unsigned len = strlen(line);
unsigned status = 0, tlen;
while (len > 0 && (line[len - 1] == '?' || whitespace(line[len - 1])))
len--;
for (i = 0;; i++) {
if (commands[i].name == NULL)
break;
tlen = len;
if (tlen > commands[i].name_size) {
tlen = commands[i].name_size;
}
if (c_strncasecmp(commands[i].name, line, tlen) == 0) {
status = 1;
if (commands[i].arg)
printf(" %12s %s\t%16s\n", commands[i].name,
commands[i].arg, commands[i].doc);
else
printf(" %16s\t%16s\n", commands[i].name,
commands[i].doc);
}
}
return status;
}
static
void usage(void)
{
printf("occtl: [OPTIONS...] {COMMAND}\n\n");
printf(" -s --socket-file Specify the server's occtl socket file\n");
printf(" -h --help Show this help\n");
printf(" -v --version Show the program's version\n");
printf("\n");
print_commands(0);
printf("\n");
}
static
void version(void)
{
fprintf(stderr,
"OpenConnect server control (occtl) version %s\n", VERSION);
fprintf(stderr, "Copyright (C) 2014 Red Hat and others.\n");
fprintf(stderr,
"ocserv comes with ABSOLUTELY NO WARRANTY. This is free software,\n");
fprintf(stderr,
"and you are welcome to redistribute it under the conditions of the\n");
fprintf(stderr,
"GNU General Public License version 2.\n");
fprintf(stderr, "\nFor help type ? or 'help'\n");
fprintf(stderr,
"==================================================================\n");
}
/* Read a string, and return a pointer to it. Returns NULL on EOF. */
static char *rl_gets(char *line_read)
{
/* If the buffer has already been allocated, return the memory
to the free pool. */
if (line_read) {
free(line_read); /* this is allocated using readline() not talloc */
}
/* Get a line from the user. */
line_read = readline("> ");
/* If the line has any text in it, save it on the history. */
if (line_read && *line_read)
add_history(line_read);
return (line_read);
}
void
bytes2human(unsigned long bytes, char* output, unsigned output_size, const char* suffix)
{
double data;
if (suffix == NULL)
suffix = "";
if (bytes > 1000 && bytes < 1000 * 1000) {
data = ((double) bytes) / 1000;
snprintf(output, output_size, "%.1f KB%s", data, suffix);
return;
} else if (bytes >= 1000 * 1000 && bytes < 1000 * 1000 * 1000) {
data = ((double) bytes) / (1000 * 1000);
snprintf(output, output_size, "%.1f MB%s", data, suffix);
return;
} else if (bytes >= 1000 * 1000 * 1000) {
data = ((double) bytes) / (1000 * 1000 * 1000);
snprintf(output, output_size, "%.1f GB%s", data, suffix);
return;
} else {
snprintf(output, output_size, "%lu bytes%s", bytes, suffix);
return;
}
}
static int handle_help_cmd(CONN_TYPE * conn, const char *arg)
{
print_commands(1);
return 0;
}
static int handle_reset_cmd(CONN_TYPE * conn, const char *arg)
{
rl_reset_terminal(NULL);
#ifdef HAVE_ORIG_READLINE
rl_reset_screen_size();
#endif
return 0;
}
static int handle_exit_cmd(CONN_TYPE * conn, const char *arg)
{
exit(0);
}
/* checks whether an input command of type " list users" maches
* the given cmd (e.g., "list users"). If yes it executes func() and returns true.
*/
unsigned check_cmd(const char *cmd, const char *input,
CONN_TYPE * conn, int need_preconn, cmd_func func, int *status)
{
char *t, *p;
unsigned len, tlen;
unsigned i, j, ret = 0;
char prev;
while (whitespace(*input))
input++;
len = strlen(input);
t = talloc_size(conn, len + 1);
if (t == NULL)
return 0;
prev = 0;
p = t;
for (i = j = 0; i < len; i++) {
if (!whitespace(prev) || !whitespace(input[i])) {
*p = input[i];
prev = input[i];
p++;
}
}
*p = 0;
tlen = p - t;
len = strlen(cmd);
if (len == 0)
goto cleanup;
if (tlen >= len && c_strncasecmp(cmd, t, len) == 0 && cmd[len] == 0) { /* match */
p = t + len;
while (whitespace(*p))
p++;
if (need_preconn != 0) {
if (conn_prehandle(conn) < 0) {
*status = 1;
} else {
*status = func(conn, p);
}
} else {
*status = func(conn, p);
}
ret = 1;
if (need_preconn != 0)
conn_posthandle(conn);
}
cleanup:
talloc_free(t);
return ret;
}
char *stripwhite(char *string)
{
register char *s, *t;
for (s = string; whitespace(*s); s++) ;
if (*s == 0)
return (s);
t = s + strlen(s) - 1;
while (t > s && whitespace(*t))
t--;
*++t = '\0';
return s;
}
int handle_cmd(CONN_TYPE * conn, char *line)
{
char *cline;
unsigned int i;
int status = 0;
cline = stripwhite(line);
if (strlen(cline) == 0)
return 1;
for (i = 0;; i++) {
if (commands[i].name == NULL)
goto error;
if (check_cmd
(commands[i].name, cline, conn,
commands[i].need_preconn,
commands[i].func,
&status) != 0)
break;
}
return status;
error:
if (check_cmd_help(line) == 0) {
fprintf(stderr, "unknown command: %s\n", line);
fprintf(stderr,
"use help or '?' to get a list of the available commands\n");
}
return 1;
}
/* returns an allocated string using malloc(), not talloc,
* to be compatible with readline() return.
*/
static char *merge_args(int argc, char **argv)
{
unsigned size = 0;
char *data, *p;
unsigned i, len;
for (i = 1; i < argc; i++) {
size += strlen(argv[i]) + 1;
}
size++;
data = malloc(size);
if (data == NULL) {
fprintf(stderr, "memory error\n");
exit(1);
}
p = data;
for (i = 1; i < argc; i++) {
len = strlen(argv[i]);
memcpy(p, argv[i], len);
p += len;
*p = ' ';
p++;
}
*p = 0;
return data;
}
static unsigned int cmd_start = 0;
static char *command_generator(const char *text, int state)
{
static int list_index, len;
static int entries_idx;
unsigned name_size;
char *name, *arg;
char *ret;
/* If this is a new word to complete, initialize now. This includes
saving the length of TEXT for efficiency, and initializing the index
variable to 0. */
if (!state) {
list_index = 0;
entries_idx = 0;
len = strlen(text);
}
/* Return the next name which partially matches from the command list. */
while ((name = commands[list_index].name)) {
name_size = commands[list_index].name_size;
arg = commands[list_index].arg;
list_index++;
if (cmd_start > name_size) {
/* check for user or ID options */
if (rl_line_buffer != NULL &&
c_strncasecmp(rl_line_buffer, name, name_size) == 0
&&
/* make sure only one argument is appended */
rl_line_buffer[name_size] != 0 &&
strchr(&rl_line_buffer[name_size + 1],
' ') == NULL) {
if (arg != NULL) {
ret = NULL;
if (strcmp(arg, "[NAME]") == 0)
ret =
search_for_user(entries_idx,
text, len);
else if (strcmp(arg, "[ID]") == 0)
ret =
search_for_id(entries_idx,
text, len);
if (ret != NULL) {
entries_idx++;
}
list_index--; /* restart at the same cmd */
return ret;
}
}
continue;
}
if (cmd_start > 0 && name[cmd_start - 1] != ' ')
continue;
if (rl_line_buffer != NULL
&& c_strncasecmp(rl_line_buffer, name, cmd_start) != 0)
continue;
name += cmd_start;
if (c_strncasecmp(name, text, len) == 0) {
return (strdup(name));
}
}
return NULL;
}
static char **occtl_completion(const char *text, int start, int end)
{
cmd_start = start;
return rl_completion_matches(text, command_generator);
}
void handle_sigint(int signo)
{
#ifdef HAVE_ORIG_READLINE
rl_reset_line_state();
rl_replace_line("", 0);
rl_crlf();
#endif
rl_redisplay();
return;
}
void initialize_readline(void)
{
rl_readline_name = "occtl";
rl_attempted_completion_function = occtl_completion;
rl_completion_entry_function = command_generator;
rl_completion_query_items = 20;
#ifdef HAVE_ORIG_READLINE
rl_clear_signals();
#endif
signal(SIGINT, handle_sigint);
}
static int single_cmd(int argc, char **argv, void *pool, const char *file)
{
CONN_TYPE *conn;
char *line;
int ret;
conn = conn_init(pool, file);
line = merge_args(argc, argv);
ret = handle_cmd(conn, line);
free(line);
return ret;
}
int main(int argc, char **argv)
{
char *line = NULL;
CONN_TYPE *conn;
const char *file = NULL;
void *gl_pool;
gl_pool = talloc_init("occtl");
if (gl_pool == NULL) {
fprintf(stderr, "talloc init error\n");
exit(1);
}
signal(SIGPIPE, SIG_IGN);
if (argc > 1) {
if (argv[1][0] == '-') {
if (argv[1][1] == 'v'
|| (argv[1][1] == '-' && argv[1][2] == 'v')) {
version();
} else if (argc > 2 && (argv[1][1] == 's'
|| (argv[1][1] == '-' && argv[1][2] == 's'))) {
file = talloc_strdup(gl_pool, argv[2]);
if (argc == 3) {
goto interactive;
} else {
argv += 2;
argc -= 2;
exit(single_cmd(argc, argv, gl_pool, file));
}
} else {
usage();
}
exit(0);
}
/* handle all arguments as a command */
exit(single_cmd(argc, argv, gl_pool, file));
}
interactive:
conn = conn_init(gl_pool, file);
initialize_readline();
version();
for (;;) {
line = rl_gets(line);
if (line == NULL)
return 0;
handle_cmd(conn, line);
}
conn_close(conn);
return 0;
}