3 This file is taken from nfsim (http://ozlabs.org/~jk/projects/nfsim/)
5 Copyright (c) 2003,2004 Jeremy Kerr & Rusty Russell
7 This file is part of nfsim.
9 nfsim is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 nfsim is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with nfsim; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include "ctdb-test.h"
29 #include <sys/types.h>
37 #include <readline/readline.h>
38 #include <readline/history.h>
40 #include <dlinklist.h>
42 int tui_echo_commands;
43 int tui_abort_on_fail;
50 struct command *next, *prev;
51 char name[TUI_MAX_CMD_LEN+1];
52 bool (*handler)(int, char **);
53 void (*helpfn) (int, char **);
56 struct pre_post_hook {
57 struct pre_post_hook *next, *prev;
58 void (*pre)(const char *);
59 bool (*post)(const char *);
62 static struct command *commands;
63 static struct pre_post_hook *pre_post_hooks;
65 static bool tui_exit(int argc, char **argv)
71 static bool tui_argtest(int argc, char **argv)
75 for (i = 0; i < argc; i++)
76 log_line(LOG_UI, "argv[%d]: \"%s\"", i, argv[i]);
81 static inline struct command *find_command(const char *name)
84 for (cmd = commands; cmd; cmd = cmd->next)
85 if (!strcmp(name, cmd->name))
91 bool tui_is_command(const char *name)
93 return find_command(name) != NULL;
96 static void do_pre_commands(const char *cmd)
98 struct pre_post_hook *i;
99 for (i = pre_post_hooks; i; i = i->next)
104 static bool do_post_commands(const char *cmd)
106 struct pre_post_hook *i;
109 for (i = pre_post_hooks; i; i = i->next)
110 if (i->post && !i->post(cmd))
115 static bool tui_help(int argc, char **argv)
120 log_line(LOG_UI, "CTDB tester\n"
121 "help is available on the folowing commands:");
122 for (cmd = commands; cmd; cmd = cmd->next) {
124 log_line(LOG_UI, "\t%s", cmd->name);
127 if (!(cmd = find_command(argv[1]))) {
128 log_line(LOG_ALWAYS, "No such command '%s'", argv[1]);
132 log_line(LOG_UI, "No help for the '%s' function",
136 cmd->helpfn(argc-1, argv+1);
143 static void tui_help_help(int argc, char **argv)
145 #include "generated-tui-help:help"
147 <section id="c:help">
148 <title><command>help</command></title>
149 <para>Displays general help, or help for a specified command</para>
151 <command>help</command>
152 <arg choice="opt">command</arg>
154 <para>With no arguments, <command>help</command> will show general system
155 help, and list the available commands. If an argument is specified, then
156 <command>help</command> will show help for that command, if
162 static void tui_exit_help(int argc, char **argv)
164 #include "generated-tui-help:exit"
166 <section id="c:exit">
167 <title><command>exit</command>,
168 <command>quit</command></title>
169 <para>Exit the simulator</para>
171 <command>exit</command>
174 <command>quit</command>
177 <para>The <command>exit</command> and <command>quit</command>
178 commands are synonomous. They both exit the simulator.
184 void script_fail(const char *fmt, ...)
189 log_line(LOG_ALWAYS, "Script failed at line %i: ", tui_linenum);
191 va_start(arglist, fmt);
192 str = talloc_vasprintf(NULL, fmt, arglist);
195 log_line(LOG_ALWAYS, "%s", str);
200 exit(EXIT_SCRIPTFAIL);
203 bool tui_do_command(int argc, char *argv[], bool abort)
208 if ((cmd = find_command(argv[0]))) {
209 do_pre_commands(cmd->name);
210 if (!cmd->handler(argc, argv)) {
211 /* Abort on UNEXPECTED failure. */
212 if (!log_line(LOG_UI, "%s: command failed", argv[0])
214 script_fail("%s failed", argv[0]);
217 if (!do_post_commands(cmd->name))
223 script_fail("%s not found", argv[0]);
225 log_line(LOG_ALWAYS, "%s: command not found", argv[0]);
230 * backslash-escape a binary data block into a newly allocated
233 * @param src a pointer to the data block
234 * @param src_len the length of the data block
235 * @return NULL if out of memory, or a pointer to the allocated escaped
236 * string, which is terminated with a '\0' character
238 static char *escape(const char *src, size_t src_len)
240 static const char hexbuf[]= "0123456789abcdef";
244 /* src_len * 4 is always safe, it's the longest escape
245 sequence for all characters */
246 dest = talloc_array(src, char, src_len * 4 + 1);
249 for (i = 0; i < src_len; i++) {
250 if (src[i] == '\n') {
253 } else if (src[i] == '\r') {
256 } else if (src[i] == '\0') {
259 } else if (src[i] == '\t') {
262 } else if (src[i] == '\\') {
265 } else if (src[i] & 0x80 || (src[i] & 0xe0) == 0) {
268 *p++ = hexbuf[(src[i] >> 4) & 0xf];
269 *p++ = hexbuf[src[i] & 0xf];
278 /* Process `command`: update off to point to tail backquote */
279 static char *backquote(char *line, unsigned int *off)
281 char *end, *cmdstr, *str;
286 /* Skip first backquote, look for next one. */
288 end = strchr(line + *off, '`');
290 script_fail("no matching \"`\" found");
292 len = end - (line + *off);
293 cmdstr = talloc_asprintf(line, "PATH=%s; %.*s",
294 extension_path, (int)len, line + *off);
295 cmdfile = popen(cmdstr, "r");
297 script_fail("failed to popen '%s': %s\n",
298 cmdstr, strerror(errno));
300 /* Jump to backquote. */
303 /* Read command output. */
306 str = talloc_array(line, char, len);
308 while ((i = fread(str + used, 1, len - used, cmdfile)) != 0) {
312 script_fail("command '%s' output too long\n",
315 str = talloc_realloc(line, str, char, len);
318 status = pclose(cmdfile);
319 if (status == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0)
320 script_fail("command '%s' failed\n", cmdstr);
322 return escape(str, used);
325 static char *append_char(char **argv, unsigned int argc, char c)
328 return talloc_asprintf(argv, "%c", c);
329 return talloc_asprintf_append(argv[argc], "%c", c);
332 static char *append_string(char **argv, unsigned int argc, const char *str)
335 return talloc_asprintf(argv, "%s", str);
336 return talloc_asprintf_append(argv[argc], "%s", str);
339 static void process_line(char *line, unsigned int off)
341 unsigned int argc, i;
344 if (tui_echo_commands)
345 printf("%u:%s\n", tui_linenum, line + off);
347 /* Talloc argv off line so commands can use it for auto-cleanup. */
348 argv = talloc_zero_array(line, char *, TUI_MAX_ARGS+1);
350 for (i = off; line[i]; i++) {
351 if (isspace(line[i])) {
352 /* If anything in this arg, move to next. */
355 } else if (line[i] == '`') {
356 char *inside = backquote(line, &i);
357 argv[argc] = append_string(argv, argc, inside);
359 /* If it is a comment, stop before we process `` */
360 if (!argv[0] && line[i] == '#')
363 argv[argc] = append_char(argv, argc, line[i]);
370 tui_do_command(argc, argv, tui_abort_on_fail);
378 static void readline_process_line(char *line)
388 /* Readline isn't talloc-aware, so copy string: functions can
389 * hang temporary variables off this. */
390 talloc_line = talloc_strdup(NULL, line);
391 process_line(talloc_line, 0);
392 talloc_free(talloc_line);
395 static void run_whole_file(int fd)
400 file = grab_fd(fd, &size);
402 err(1, "Grabbing file");
404 for (p = file; p < file + size; p += len+1) {
405 len = strcspn(p, "\n");
407 process_line(file, p - file);
413 tui_register_command("exit", tui_exit, tui_exit_help);
414 tui_register_command("quit", tui_exit, tui_exit_help);
415 tui_register_command("q", tui_exit, tui_exit_help);
416 tui_register_command("test", tui_argtest, NULL);
417 tui_register_command("help", tui_help, tui_help_help);
419 if (fd == STDIN_FILENO) {
421 rl_callback_handler_install(tui_quiet ? "" : "> ",
422 readline_process_line);
424 rl_callback_read_char();
425 rl_callback_handler_remove();
432 int tui_register_pre_post_hook(void (*pre)(const char *),
433 bool (*post)(const char *))
435 struct pre_post_hook *h;
437 h = talloc(NULL, struct pre_post_hook);
440 DLIST_ADD(pre_post_hooks, h);
444 int tui_register_command(const char *command,
445 bool (*handler)(int, char **),
446 void (*helpfn)(int, char **))
450 assert(strlen(command) < TUI_MAX_CMD_LEN);
452 cmd = talloc(NULL, struct command);
453 strncpy(cmd->name, command, TUI_MAX_CMD_LEN);
454 cmd->handler = handler;
455 cmd->helpfn = helpfn;
457 DLIST_ADD(commands, cmd);