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);
199 exit(EXIT_SCRIPTFAIL);
202 bool tui_do_command(int argc, char *argv[], bool abort)
207 if ((cmd = find_command(argv[0]))) {
208 do_pre_commands(cmd->name);
209 if (!cmd->handler(argc, argv)) {
210 /* Abort on UNEXPECTED failure. */
211 if (!log_line(LOG_UI, "%s: command failed", argv[0])
213 script_fail("%s failed", argv[0]);
216 if (!do_post_commands(cmd->name))
222 script_fail("%s not found", argv[0]);
224 log_line(LOG_ALWAYS, "%s: command not found", argv[0]);
229 * backslash-escape a binary data block into a newly allocated
232 * @param src a pointer to the data block
233 * @param src_len the length of the data block
234 * @return NULL if out of memory, or a pointer to the allocated escaped
235 * string, which is terminated with a '\0' character
237 static char *escape(const char *src, size_t src_len)
239 static const char hexbuf[]= "0123456789abcdef";
243 /* src_len * 4 is always safe, it's the longest escape
244 sequence for all characters */
245 dest = talloc_array(src, char, src_len * 4 + 1);
248 for (i = 0; i < src_len; i++) {
249 if (src[i] == '\n') {
252 } else if (src[i] == '\r') {
255 } else if (src[i] == '\0') {
258 } else if (src[i] == '\t') {
261 } else if (src[i] == '\\') {
264 } else if (src[i] & 0x80 || (src[i] & 0xe0) == 0) {
267 *p++ = hexbuf[(src[i] >> 4) & 0xf];
268 *p++ = hexbuf[src[i] & 0xf];
277 /* Process `command`: update off to point to tail backquote */
278 static char *backquote(char *line, unsigned int *off)
280 char *end, *cmdstr, *str;
285 /* Skip first backquote, look for next one. */
287 end = strchr(line + *off, '`');
289 script_fail("no matching \"`\" found");
291 len = end - (line + *off);
292 cmdstr = talloc_asprintf(line, "PATH=%s; %.*s",
293 extension_path, (int)len, line + *off);
294 cmdfile = popen(cmdstr, "r");
296 script_fail("failed to popen '%s': %s\n",
297 cmdstr, strerror(errno));
299 /* Jump to backquote. */
302 /* Read command output. */
305 str = talloc_array(line, char, len);
307 while ((i = fread(str + used, 1, len - used, cmdfile)) != 0) {
311 script_fail("command '%s' output too long\n",
314 str = talloc_realloc(line, str, char, len);
317 status = pclose(cmdfile);
318 if (status == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0)
319 script_fail("command '%s' failed\n", cmdstr);
321 return escape(str, used);
324 static char *append_char(char **argv, unsigned int argc, char c)
327 return talloc_asprintf(argv, "%c", c);
328 return talloc_asprintf_append(argv[argc], "%c", c);
331 static char *append_string(char **argv, unsigned int argc, const char *str)
334 return talloc_asprintf(argv, "%s", str);
335 return talloc_asprintf_append(argv[argc], "%s", str);
338 static void process_line(char *line, unsigned int off)
340 unsigned int argc, i;
343 if (tui_echo_commands)
344 printf("%u:%s\n", tui_linenum, line + off);
346 /* Talloc argv off line so commands can use it for auto-cleanup. */
347 argv = talloc_zero_array(line, char *, TUI_MAX_ARGS+1);
349 for (i = off; line[i]; i++) {
350 if (isspace(line[i])) {
351 /* If anything in this arg, move to next. */
354 } else if (line[i] == '`') {
355 char *inside = backquote(line, &i);
356 argv[argc] = append_string(argv, argc, inside);
358 /* If it is a comment, stop before we process `` */
359 if (!argv[0] && line[i] == '#')
362 argv[argc] = append_char(argv, argc, line[i]);
369 tui_do_command(argc, argv, tui_abort_on_fail);
377 static void readline_process_line(char *line)
387 /* Readline isn't talloc-aware, so copy string: functions can
388 * hang temporary variables off this. */
389 talloc_line = talloc_strdup(NULL, line);
390 process_line(talloc_line, 0);
391 talloc_free(talloc_line);
394 static void run_whole_file(int fd)
399 file = grab_fd(fd, &size);
401 err(1, "Grabbing file");
403 for (p = file; p < file + size; p += len+1) {
404 len = strcspn(p, "\n");
406 process_line(file, p - file);
412 tui_register_command("exit", tui_exit, tui_exit_help);
413 tui_register_command("quit", tui_exit, tui_exit_help);
414 tui_register_command("q", tui_exit, tui_exit_help);
415 tui_register_command("test", tui_argtest, NULL);
416 tui_register_command("help", tui_help, tui_help_help);
418 if (fd == STDIN_FILENO) {
420 rl_callback_handler_install(tui_quiet ? "" : "> ",
421 readline_process_line);
423 rl_callback_read_char();
424 rl_callback_handler_remove();
431 int tui_register_pre_post_hook(void (*pre)(const char *),
432 bool (*post)(const char *))
434 struct pre_post_hook *h;
436 h = talloc(NULL, struct pre_post_hook);
439 DLIST_ADD(pre_post_hooks, h);
443 int tui_register_command(const char *command,
444 bool (*handler)(int, char **),
445 void (*helpfn)(int, char **))
449 assert(strlen(command) < TUI_MAX_CMD_LEN);
451 cmd = talloc(NULL, struct command);
452 strncpy(cmd->name, command, TUI_MAX_CMD_LEN);
453 cmd->handler = handler;
454 cmd->helpfn = helpfn;
456 DLIST_ADD(commands, cmd);