2 * Copyright (c)2004 The DragonFly Project. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in
13 * the documentation and/or other materials provided with the
16 * Neither the name of the DragonFly Project nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31 * OF THE POSSIBILITY OF SUCH DAMAGE.
36 * Execute a queued series of commands, updating a DFUI progress bar
37 * as each is executed.
38 * $Id: commands.c,v 1.27 2005/03/12 04:32:14 cpressey Exp $
42 #include <sys/types.h>
51 #include "libaura/mem.h"
52 #include "libaura/buffer.h"
53 #include "libaura/popen.h"
55 #include "libdfui/dfui.h"
57 #define NEEDS_COMMANDS_STRUCTURE_DEFINITIONS
59 #undef NEEDS_COMMANDS_STRUCTURE_DEFINITIONS
62 #include "functions.h"
66 * Create a new queue of commands.
71 struct commands *cmds;
73 AURA_MALLOC(cmds, commands);
82 * Add a new, empty command to an existing queue of commands.
84 static struct command *
85 command_new(struct commands *cmds)
89 AURA_MALLOC(cmd, command);
93 cmd->log_mode = COMMAND_LOG_VERBOSE;
94 cmd->failure_mode = COMMAND_FAILURE_ABORT;
96 cmd->result = COMMAND_RESULT_NEVER_EXECUTED;
100 if (cmds->head == NULL)
103 cmds->tail->next = cmd;
105 cmd->prev = cmds->tail;
112 * Add a new shell command to an existing queue of commands.
113 * The command can be specified as a format string followed by
114 * any number of arguments, in the style of "sprintf".
117 command_add(struct commands *cmds, const char *fmt, ...)
122 cmd = command_new(cmds);
125 vasprintf(&cmd->cmdline, fmt, args);
132 * Set the log mode of the given command.
133 * Valid log modes are:
134 * COMMAND_LOG_SILENT - do not log anything at all
135 * COMMAND_LOG_QUIET - only log command name and exit code, not output
136 * COMMAND_LOG_VERBOSE - log everything
139 command_set_log_mode(struct command *cmd, int log_mode)
141 cmd->log_mode = log_mode;
145 * Set the failure mode of the given command.
146 * Valid failure modes are:
147 * COMMAND_FAILURE_IGNORE - ignore failures and carry on
148 * COMMAND_FAILURE_WARN - issue a non-critical warning
149 * COMMAND_FAILURE_ABORT - halt the command chain and ask the user
152 command_set_failure_mode(struct command *cmd, int failure_mode)
154 cmd->failure_mode = failure_mode;
158 * Set the description of the given command. If present, it will
159 * be displayed in the progress bar instead of the command line.
162 command_set_desc(struct command *cmd, const char *fmt, ...)
166 if (cmd->desc != NULL)
170 vasprintf(&cmd->desc, fmt, args);
175 * Set an arbitrary tag on the command.
178 command_set_tag(struct command *cmd, const char *fmt, ...)
182 if (cmd->tag != NULL)
186 vasprintf(&cmd->tag, fmt, args);
191 command_get_first(const struct commands *cmds)
197 command_get_next(const struct command *cmd)
203 command_get_cmdline(const struct command *cmd)
205 return(cmd->cmdline);
209 command_get_tag(const struct command *cmd)
215 command_get_result(const struct command *cmd)
221 * Allow the user to view the command log.
224 view_command_log(struct i_fn_args *a)
227 struct dfui_response *r;
228 struct aura_buffer *error_log;
230 error_log = aura_buffer_new(1024);
231 aura_buffer_cat_file(error_log, "%sinstall.log", a->tmp);
233 f = dfui_form_create(
236 aura_buffer_buf(error_log),
239 "p", "role", "informative",
240 "p", "minimum_width", "72",
241 "p", "monospaced", "true",
243 "a", "ok", "OK", "", "",
246 if (!dfui_be_present(a->c, f, &r))
250 dfui_response_free(r);
252 aura_buffer_free(error_log);
256 * Preview a set of commands.
259 commands_preview(struct dfui_connection *c, const struct commands *cmds)
262 struct aura_buffer *preview;
264 preview = aura_buffer_new(1024);
266 for (cmd = cmds->head; cmd != NULL; cmd = cmd->next) {
267 aura_buffer_cat(preview, cmd->cmdline);
268 aura_buffer_cat(preview, "\n");
271 inform(c, aura_buffer_buf(preview));
273 aura_buffer_free(preview);
277 * The command chain executing engine proper follows.
281 * Read from the pipe that was opened to the executing commands
282 * and update the progress bar as data comes and (and/or as the
283 * read from the pipe times out.)
286 pipe_loop(struct i_fn_args *a, struct dfui_progress *pr,
287 struct command *cmd, int *cancelled)
290 struct timeval tv = { 1, 0 };
297 asprintf(&command, "(%s) 2>&1 </dev/null", cmd->cmdline);
299 cmdout = aura_popen(command, "r");
302 if (cmdout == NULL) {
303 i_log(a, "! could not aura_popen() command");
304 return(COMMAND_RESULT_POPEN_ERR);
306 pid = aura_pgetpid(cmdout);
308 fprintf(stderr, "+ pid = %d\n", pid);
312 * Loop, selecting on the command and a timeout.
318 FD_SET(fileno(cmdout), &r);
319 n = select(fileno(cmdout) + 1, &r, NULL, NULL, &tv);
321 fprintf(stderr, "+ select() = %d\n", n);
325 i_log(a, "! select() failed\n");
327 return(COMMAND_RESULT_SELECT_ERR);
330 if (!dfui_be_progress_update(a->c, pr, cancelled))
333 fprintf(stderr, "+ cancelled = %d\n", *cancelled);
337 fgets(cline, 255, cmdout);
338 while (strlen(cline) > 0 && cline[strlen(cline) - 1] == '\n')
339 cline[strlen(cline) - 1] = '\0';
342 if (!dfui_be_progress_update(a->c, pr, cancelled))
344 if (cmd->log_mode == COMMAND_LOG_VERBOSE) {
345 i_log(a, "| %s", cline);
346 } else if (cmd->log_mode != COMMAND_LOG_SILENT) {
347 fprintf(stderr, "| %s\n", cline);
354 fprintf(stderr, "+ killing %d\n", pid);
356 n = kill(pid, SIGTERM);
358 fprintf(stderr, "+ kill() = %d\n", n);
363 fprintf(stderr, "+ pclosing %d\n", fileno(cmdout));
365 n = aura_pclose(cmdout) / 256;
367 fprintf(stderr, "+ pclose() = %d\n", n);
373 * Execute a single command.
374 * Return value is a COMMAND_RESULT_* constant, or
375 * a value from 0 to 255 to indicate the exit code
379 command_execute(struct i_fn_args *a, struct dfui_progress *pr,
384 int cancelled = 0, done = 0, report_done = 0;
386 if (cmd->desc != NULL)
387 dfui_info_set_short_desc(dfui_progress_get_info(pr), cmd->desc);
389 dfui_info_set_short_desc(dfui_progress_get_info(pr), cmd->cmdline);
391 if (!dfui_be_progress_update(a->c, pr, &cancelled))
395 asprintf(&filename, "%sinstall.log", a->tmp);
396 log = fopen(filename, "a");
399 if (cmd->log_mode != COMMAND_LOG_SILENT)
400 i_log(a, ",-<<< Executing `%s'", cmd->cmdline);
401 cmd->result = pipe_loop(a, pr, cmd, &cancelled);
402 if (cmd->log_mode != COMMAND_LOG_SILENT)
403 i_log(a, "`->>> Exit status: %d\n", cmd->result);
409 if (!dfui_be_progress_end(a->c))
413 while (!report_done) {
414 switch (dfui_be_present_dialog(a->c, "Cancelled",
415 "View Log|Retry|Cancel|Skip",
416 "Execution of the command\n\n%s\n\n"
430 cmd->result = COMMAND_RESULT_CANCELLED;
436 cmd->result = COMMAND_RESULT_SKIPPED;
443 if (!dfui_be_progress_begin(a->c, pr))
446 } else if (cmd->failure_mode == COMMAND_FAILURE_IGNORE) {
449 } else if (cmd->result != 0 && cmd->failure_mode != COMMAND_FAILURE_WARN) {
450 if (!dfui_be_progress_end(a->c))
454 while (!report_done) {
455 switch (dfui_be_present_dialog(a->c, "Command Failed!",
456 "View Log|Retry|Cancel|Skip",
457 "Execution of the command\n\n%s\n\n"
458 "FAILED with a return code of %d.",
459 cmd->cmdline, cmd->result)) {
470 /* XXX need a better way to retain actual result */
471 cmd->result = COMMAND_RESULT_CANCELLED;
477 /* XXX need a better way to retain actual result */
478 cmd->result = COMMAND_RESULT_SKIPPED;
485 if (!dfui_be_progress_begin(a->c, pr))
497 * Execute a series of external utility programs.
498 * Returns 1 if everything executed OK, 0 if one of the
499 * critical commands failed or if the user cancelled.
502 commands_execute(struct i_fn_args *a, struct commands *cmds)
504 struct dfui_progress *pr;
512 while (cmd != NULL) {
517 pr = dfui_progress_new(dfui_info_new(
518 "Executing Commands",
519 "Executing Commands",
523 if (!dfui_be_progress_begin(a->c, pr))
527 for (cmd = cmds->head; cmd != NULL; cmd = cmd->next, i++) {
528 result = command_execute(a, pr, cmd);
529 if (result == COMMAND_RESULT_CANCELLED) {
533 if (result > 0 && result < 256) {
535 if (cmd->failure_mode == COMMAND_FAILURE_ABORT) {
539 dfui_progress_set_amount(pr, (i * 100) / n);
542 if (!dfui_be_progress_end(a->c))
545 dfui_progress_free(pr);
551 * Free the memory allocated for a queue of commands. This invalidates
552 * the pointer passed to it.
555 commands_free(struct commands *cmds)
557 struct command *cmd, *next;
560 while (cmd != NULL) {
562 if (cmd->cmdline != NULL)
564 if (cmd->desc != NULL)
566 if (cmd->tag != NULL)
568 AURA_FREE(cmd, command);
571 AURA_FREE(cmds, commands);
575 * Command generators.
579 * Generate a command to create a generic device node
580 * only if it does not already exist.
583 command_add_ensure_dev(struct i_fn_args *a, struct commands *cmds,
584 const char *dev_name)
589 dev_basename = basename(dev_name);
592 * We don't need to do this on systems with a devfs
593 * (which is, at the time of this writing, FreeBSD 5.x and later.)
595 cmd = command_add(cmds, "cd %sdev && %s%s %s || %s%s MAKEDEV %s",
597 a->os_root, cmd_name(a, "TEST_DEV"),
599 a->os_root, cmd_name(a, "SH"),