mirror of https://github.com/sipwise/klish.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
385 lines
9.4 KiB
385 lines
9.4 KiB
/*
|
|
* --------------------------------------
|
|
* clish.c
|
|
*
|
|
* A console client for libclish
|
|
* --------------------------------------
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif /* HAVE_CONFIG_H */
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#ifdef HAVE_GETOPT_H
|
|
#include <getopt.h>
|
|
#endif
|
|
#include <signal.h>
|
|
#if HAVE_LOCALE_H
|
|
#include <locale.h>
|
|
#endif
|
|
#if HAVE_LANGINFO_CODESET
|
|
#include <langinfo.h>
|
|
#endif
|
|
|
|
#include "lub/list.h"
|
|
#include "lub/system.h"
|
|
#include "clish/shell.h"
|
|
|
|
#define QUOTE(t) #t
|
|
/* #define version(v) printf("%s\n", QUOTE(v)) */
|
|
#define version(v) printf("%s\n", v)
|
|
|
|
static void help(int status, const char *argv0);
|
|
|
|
/*--------------------------------------------------------- */
|
|
int main(int argc, char **argv)
|
|
{
|
|
int running;
|
|
int result = -1;
|
|
clish_shell_t *shell = NULL;
|
|
|
|
/* Command line options */
|
|
const char *socket_path = KONFD_SOCKET_PATH;
|
|
bool_t lockless = BOOL_FALSE;
|
|
bool_t stop_on_error = BOOL_FALSE;
|
|
bool_t interactive = BOOL_TRUE;
|
|
bool_t quiet = BOOL_FALSE;
|
|
bool_t utf8 = BOOL_FALSE;
|
|
bool_t bit8 = BOOL_FALSE;
|
|
bool_t log = BOOL_FALSE;
|
|
bool_t dryrun = BOOL_FALSE;
|
|
bool_t dryrun_config = BOOL_FALSE;
|
|
const char *xml_path = getenv("CLISH_PATH");
|
|
const char *view = getenv("CLISH_VIEW");
|
|
const char *viewid = getenv("CLISH_VIEWID");
|
|
|
|
FILE *outfd = stdout;
|
|
bool_t istimeout = BOOL_FALSE;
|
|
int timeout = 0;
|
|
bool_t cmd = BOOL_FALSE; /* -c option */
|
|
lub_list_t *cmds; /* Commands defined by -c */
|
|
lub_list_node_t *iter;
|
|
const char *histfile = "~/.clish_history";
|
|
char *histfile_expanded = NULL;
|
|
unsigned int histsize = 50;
|
|
clish_sym_t *sym = NULL;
|
|
|
|
/* Signal vars */
|
|
struct sigaction sigpipe_act;
|
|
sigset_t sigpipe_set;
|
|
|
|
static const char *shortopts = "hvs:ledx:w:i:bqu8okt:c:f:z:";
|
|
#ifdef HAVE_GETOPT_H
|
|
static const struct option longopts[] = {
|
|
{"help", 0, NULL, 'h'},
|
|
{"version", 0, NULL, 'v'},
|
|
{"socket", 1, NULL, 's'},
|
|
{"lockless", 0, NULL, 'l'},
|
|
{"stop-on-error", 0, NULL, 'e'},
|
|
{"dry-run", 0, NULL, 'd'},
|
|
{"xml-path", 1, NULL, 'x'},
|
|
{"view", 1, NULL, 'w'},
|
|
{"viewid", 1, NULL, 'i'},
|
|
{"background", 0, NULL, 'b'},
|
|
{"quiet", 0, NULL, 'q'},
|
|
{"utf8", 0, NULL, 'u'},
|
|
{"8bit", 0, NULL, '8'},
|
|
{"log", 0, NULL, 'o'},
|
|
{"check", 0, NULL, 'k'},
|
|
{"timeout", 1, NULL, 't'},
|
|
{"command", 1, NULL, 'c'},
|
|
{"histfile", 1, NULL, 'f'},
|
|
{"histsize", 1, NULL, 'z'},
|
|
{NULL, 0, NULL, 0}
|
|
};
|
|
#endif
|
|
|
|
/* Ignore SIGPIPE */
|
|
sigemptyset(&sigpipe_set);
|
|
sigaddset(&sigpipe_set, SIGPIPE);
|
|
sigpipe_act.sa_flags = 0;
|
|
sigpipe_act.sa_mask = sigpipe_set;
|
|
sigpipe_act.sa_handler = SIG_IGN;
|
|
sigaction(SIGPIPE, &sigpipe_act, NULL);
|
|
|
|
#if HAVE_LOCALE_H
|
|
/* Set current locale */
|
|
setlocale(LC_ALL, "");
|
|
#endif
|
|
|
|
/* Var initialization */
|
|
cmds = lub_list_new(NULL);
|
|
|
|
/* Parse command line options */
|
|
while(1) {
|
|
int opt;
|
|
#ifdef HAVE_GETOPT_H
|
|
opt = getopt_long(argc, argv, shortopts, longopts, NULL);
|
|
#else
|
|
opt = getopt(argc, argv, shortopts);
|
|
#endif
|
|
if (-1 == opt)
|
|
break;
|
|
switch (opt) {
|
|
case 's':
|
|
socket_path = optarg;
|
|
break;
|
|
case 'l':
|
|
lockless = BOOL_TRUE;
|
|
break;
|
|
case 'e':
|
|
stop_on_error = BOOL_TRUE;
|
|
break;
|
|
case 'b':
|
|
interactive = BOOL_FALSE;
|
|
break;
|
|
case 'q':
|
|
quiet = BOOL_TRUE;
|
|
break;
|
|
case 'u':
|
|
utf8 = BOOL_TRUE;
|
|
break;
|
|
case '8':
|
|
bit8 = BOOL_TRUE;
|
|
break;
|
|
case 'o':
|
|
log = BOOL_TRUE;
|
|
break;
|
|
case 'd':
|
|
dryrun = BOOL_TRUE;
|
|
break;
|
|
case 'x':
|
|
xml_path = optarg;
|
|
break;
|
|
case 'w':
|
|
view = optarg;
|
|
break;
|
|
case 'i':
|
|
viewid = optarg;
|
|
break;
|
|
case 'k':
|
|
lockless = BOOL_TRUE;
|
|
dryrun = BOOL_TRUE;
|
|
dryrun_config = BOOL_TRUE;
|
|
break;
|
|
case 't':
|
|
istimeout = BOOL_TRUE;
|
|
timeout = atoi(optarg);
|
|
break;
|
|
case 'c': {
|
|
char *str;
|
|
cmd = BOOL_TRUE;
|
|
quiet = BOOL_TRUE;
|
|
str = strdup(optarg);
|
|
lub_list_add(cmds, str);
|
|
}
|
|
break;
|
|
case 'f':
|
|
if (!strcmp(optarg, "/dev/null"))
|
|
histfile = NULL;
|
|
else
|
|
histfile = optarg;
|
|
break;
|
|
case 'z': {
|
|
int itmp = 0;
|
|
itmp = atoi(optarg);
|
|
if (itmp <= 0) {
|
|
fprintf(stderr, "Error: Illegal histsize option value.\n");
|
|
help(-1, argv[0]);
|
|
goto end;
|
|
}
|
|
histsize = itmp;
|
|
}
|
|
break;
|
|
case 'h':
|
|
help(0, argv[0]);
|
|
exit(0);
|
|
break;
|
|
case 'v':
|
|
version(VERSION);
|
|
exit(0);
|
|
break;
|
|
default:
|
|
help(-1, argv[0]);
|
|
goto end;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Validate command line options */
|
|
if (utf8 && bit8) {
|
|
fprintf(stderr, "Error: The -u and -8 options can't be used together.\n");
|
|
goto end;
|
|
}
|
|
|
|
/* Create shell instance */
|
|
if (quiet)
|
|
outfd = fopen("/dev/null", "w");
|
|
shell = clish_shell_new(NULL, outfd, stop_on_error);
|
|
if (!shell) {
|
|
fprintf(stderr, "Error: Can't run clish.\n");
|
|
goto end;
|
|
}
|
|
/* Load the XML files */
|
|
if (clish_shell_load_scheme(shell, xml_path))
|
|
goto end;
|
|
/* Set communication to the konfd */
|
|
clish_shell__set_socket(shell, socket_path);
|
|
/* Set lockless mode */
|
|
if (lockless)
|
|
clish_shell__set_lockfile(shell, NULL);
|
|
/* Set interactive mode */
|
|
if (!interactive)
|
|
clish_shell__set_interactive(shell, interactive);
|
|
/* Set startup view */
|
|
if (view)
|
|
clish_shell__set_startup_view(shell, view);
|
|
/* Set startup viewid */
|
|
if (viewid)
|
|
clish_shell__set_startup_viewid(shell, viewid);
|
|
/* Set UTF-8 or 8-bit mode */
|
|
if (utf8 || bit8)
|
|
clish_shell__set_utf8(shell, utf8);
|
|
else {
|
|
#if HAVE_LANGINFO_CODESET
|
|
/* Autodetect encoding */
|
|
if (!strcmp(nl_langinfo(CODESET), "UTF-8"))
|
|
clish_shell__set_utf8(shell, BOOL_TRUE);
|
|
#else
|
|
/* The default is 8-bit if locale is not supported */
|
|
clish_shell__set_utf8(shell, BOOL_FALSE);
|
|
#endif
|
|
}
|
|
/* Set logging */
|
|
if (log)
|
|
clish_shell__set_log(shell, log);
|
|
/* Set dry-run */
|
|
if (dryrun)
|
|
clish_shell__set_dryrun(shell, dryrun);
|
|
/* Set idle timeout */
|
|
if (istimeout)
|
|
clish_shell__set_timeout(shell, timeout);
|
|
/* Set history settings */
|
|
clish_shell__stifle_history(shell, histsize);
|
|
if (histfile)
|
|
histfile_expanded = lub_system_tilde_expand(histfile);
|
|
if (histfile_expanded)
|
|
clish_shell__restore_history(shell, histfile_expanded);
|
|
/* Load plugins */
|
|
if (clish_shell_load_plugins(shell) < 0)
|
|
goto end;
|
|
if (clish_shell_link_plugins(shell) < 0)
|
|
goto end;
|
|
/* Dryrun config and log hooks */
|
|
if (dryrun_config) {
|
|
if ((sym = clish_shell_get_hook(shell, CLISH_SYM_TYPE_CONFIG)))
|
|
clish_sym__set_permanent(sym, BOOL_FALSE);
|
|
if ((sym = clish_shell_get_hook(shell, CLISH_SYM_TYPE_LOG)))
|
|
clish_sym__set_permanent(sym, BOOL_FALSE);
|
|
}
|
|
|
|
/* Set source of command stream: files or interactive tty */
|
|
if(optind < argc) {
|
|
int i;
|
|
/* Run the commands from the files */
|
|
for (i = argc - 1; i >= optind; i--)
|
|
clish_shell_push_file(shell, argv[i], stop_on_error);
|
|
} else {
|
|
/* The interactive shell */
|
|
clish_shell_push_fd(shell, fdopen(dup(fileno(stdin)), "r"),
|
|
stop_on_error);
|
|
}
|
|
|
|
/* Execute startup */
|
|
running = clish_shell_startup(shell);
|
|
if (running) {
|
|
fprintf(stderr, "Error: Can't startup clish.\n");
|
|
goto end;
|
|
}
|
|
|
|
if (cmd) {
|
|
/* Iterate cmds */
|
|
for(iter = lub_list__get_head(cmds);
|
|
iter; iter = lub_list_node__get_next(iter)) {
|
|
char *str = (char *)lub_list_node__get_data(iter);
|
|
clish_shell_forceline(shell, str, NULL);
|
|
}
|
|
} else {
|
|
/* Main loop */
|
|
result = clish_shell_loop(shell);
|
|
}
|
|
|
|
end:
|
|
/* Cleanup */
|
|
if (shell) {
|
|
if (histfile_expanded) {
|
|
clish_shell__save_history(shell, histfile_expanded);
|
|
free(histfile_expanded);
|
|
}
|
|
clish_shell_delete(shell);
|
|
}
|
|
if (quiet)
|
|
fclose(outfd);
|
|
|
|
/* Delete each cmds element */
|
|
while ((iter = lub_list__get_head(cmds))) {
|
|
lub_list_del(cmds, iter);
|
|
free(lub_list_node__get_data(iter));
|
|
lub_list_node_free(iter);
|
|
}
|
|
lub_list_free(cmds);
|
|
|
|
return result;
|
|
}
|
|
|
|
/*--------------------------------------------------------- */
|
|
/* Print help message */
|
|
static void help(int status, const char *argv0)
|
|
{
|
|
const char *name = NULL;
|
|
|
|
if (!argv0)
|
|
return;
|
|
|
|
/* Find the basename */
|
|
name = strrchr(argv0, '/');
|
|
if (name)
|
|
name++;
|
|
else
|
|
name = argv0;
|
|
|
|
if (status != 0) {
|
|
fprintf(stderr, "Try `%s -h' for more information.\n",
|
|
name);
|
|
} else {
|
|
printf("Usage: %s [options] [script_file] [script_file] ...\n", name);
|
|
printf("CLI utility. Command line shell."
|
|
"The part of the klish project.\n");
|
|
printf("Options:\n");
|
|
printf("\t-v, --version\tPrint version.\n");
|
|
printf("\t-h, --help\tPrint this help.\n");
|
|
printf("\t-s <path>, --socket=<path>\tSpecify listen socket "
|
|
"\n\t\tof the konfd daemon.\n");
|
|
printf("\t-l, --lockless\tDon't use locking mechanism.\n");
|
|
printf("\t-e, --stop-on-error\tStop script execution on error.\n");
|
|
printf("\t-b, --background\tStart shell using non-interactive mode.\n");
|
|
printf("\t-q, --quiet\tDisable echo while executing commands\n\t\tfrom the file stream.\n");
|
|
printf("\t-d, --dry-run\tDon't actually execute ACTION scripts.\n");
|
|
printf("\t-x <path>, --xml-path=<path>\tPath to XML scheme files.\n");
|
|
printf("\t-w <view_name>, --view=<view_name>\tSet the startup view.\n");
|
|
printf("\t-i <vars>, --viewid=<vars>\tSet the startup viewid variables.\n");
|
|
printf("\t-u, --utf8\tForce UTF-8 encoding.\n");
|
|
printf("\t-8, --8bit\tForce 8-bit encoding.\n");
|
|
printf("\t-o, --log\tEnable command logging to syslog's local0.\n");
|
|
printf("\t-k, --check\tCheck input files for syntax errors only.\n");
|
|
printf("\t-t <timeout>, --timeout=<timeout>\tIdle timeout in seconds.\n");
|
|
printf("\t-c <command>, --command=<command>\tExecute specified command(s).\n\t\tMultiple options are possible.\n");
|
|
printf("\t-f <path>, --histfile=<path>\tFile to save command history.\n");
|
|
printf("\t-z <num>, --histsize=<num>\tCommand history size in lines.\n");
|
|
}
|
|
}
|