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
);