HAMMER VFS - Hack cursor iterator when locked cursor moved to parent
[dragonfly.git] / usr.sbin / installer / libinstaller / commands.c
blob3ac03a51ff29603e0f5f6d4fcf9bba7dddaab7c8
1 /*
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
6 * are met:
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
14 * distribution.
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.
35 * commands.c
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 $
41 #include <sys/time.h>
42 #include <sys/types.h>
44 #include <libgen.h>
45 #include <signal.h>
46 #include <stdarg.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.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
58 #include "commands.h"
59 #undef NEEDS_COMMANDS_STRUCTURE_DEFINITIONS
61 #include "diskutil.h"
62 #include "functions.h"
63 #include "uiutil.h"
66 * Create a new queue of commands.
68 struct commands *
69 commands_new(void)
71 struct commands *cmds;
73 AURA_MALLOC(cmds, commands);
75 cmds->head = NULL;
76 cmds->tail = NULL;
78 return(cmds);
82 * Add a new, empty command to an existing queue of commands.
84 static struct command *
85 command_new(struct commands *cmds)
87 struct command *cmd;
89 AURA_MALLOC(cmd, command);
91 cmd->cmdline = NULL;
92 cmd->desc = NULL;
93 cmd->log_mode = COMMAND_LOG_VERBOSE;
94 cmd->failure_mode = COMMAND_FAILURE_ABORT;
95 cmd->tag = NULL;
96 cmd->result = COMMAND_RESULT_NEVER_EXECUTED;
97 cmd->output = NULL;
99 cmd->next = NULL;
100 if (cmds->head == NULL)
101 cmds->head = cmd;
102 else
103 cmds->tail->next = cmd;
105 cmd->prev = cmds->tail;
106 cmds->tail = cmd;
108 return(cmd);
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".
116 struct command *
117 command_add(struct commands *cmds, const char *fmt, ...)
119 va_list args;
120 struct command *cmd;
122 cmd = command_new(cmds);
124 va_start(args, fmt);
125 vasprintf(&cmd->cmdline, fmt, args);
126 va_end(args);
128 return(cmd);
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
138 void
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
151 void
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.
161 void
162 command_set_desc(struct command *cmd, const char *fmt, ...)
164 va_list args;
166 if (cmd->desc != NULL)
167 free(cmd->desc);
169 va_start(args, fmt);
170 vasprintf(&cmd->desc, fmt, args);
171 va_end(args);
175 * Set an arbitrary tag on the command.
177 void
178 command_set_tag(struct command *cmd, const char *fmt, ...)
180 va_list args;
182 if (cmd->tag != NULL)
183 free(cmd->tag);
185 va_start(args, fmt);
186 vasprintf(&cmd->tag, fmt, args);
187 va_end(args);
190 struct command *
191 command_get_first(const struct commands *cmds)
193 return(cmds->head);
196 struct command *
197 command_get_next(const struct command *cmd)
199 return(cmd->next);
202 char *
203 command_get_cmdline(const struct command *cmd)
205 return(cmd->cmdline);
208 char *
209 command_get_tag(const struct command *cmd)
211 return(cmd->tag);
215 command_get_result(const struct command *cmd)
217 return(cmd->result);
221 * Allow the user to view the command log.
223 void
224 view_command_log(struct i_fn_args *a)
226 struct dfui_form *f;
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(
234 "error_log",
235 "Error Log",
236 aura_buffer_buf(error_log),
239 "p", "role", "informative",
240 "p", "minimum_width", "72",
241 "p", "monospaced", "true",
243 "a", "ok", "OK", "", "",
244 NULL);
246 if (!dfui_be_present(a->c, f, &r))
247 abort_backend();
249 dfui_form_free(f);
250 dfui_response_free(r);
252 aura_buffer_free(error_log);
256 * Preview a set of commands.
258 void
259 commands_preview(struct dfui_connection *c, const struct commands *cmds)
261 struct command *cmd;
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.)
285 static int
286 pipe_loop(struct i_fn_args *a, struct dfui_progress *pr,
287 struct command *cmd, int *cancelled)
289 FILE *cmdout = NULL;
290 struct timeval tv = { 1, 0 };
291 char cline[256];
292 char *command;
293 pid_t pid;
294 fd_set r;
295 int n;
297 asprintf(&command, "(%s) 2>&1 </dev/null", cmd->cmdline);
298 fflush(stdout);
299 cmdout = aura_popen(command, "r");
300 free(command);
302 if (cmdout == NULL) {
303 i_log(a, "! could not aura_popen() command");
304 return(COMMAND_RESULT_POPEN_ERR);
306 pid = aura_pgetpid(cmdout);
307 #ifdef DEBUG
308 fprintf(stderr, "+ pid = %d\n", pid);
309 #endif
312 * Loop, selecting on the command and a timeout.
314 for (;;) {
315 if (*cancelled)
316 break;
317 FD_ZERO(&r);
318 FD_SET(fileno(cmdout), &r);
319 n = select(fileno(cmdout) + 1, &r, NULL, NULL, &tv);
320 #ifdef DEBUG
321 fprintf(stderr, "+ select() = %d\n", n);
322 #endif
323 if (n < 0) {
324 /* Error */
325 i_log(a, "! select() failed\n");
326 aura_pclose(cmdout);
327 return(COMMAND_RESULT_SELECT_ERR);
328 } else if (n == 0) {
329 /* Timeout */
330 if (!dfui_be_progress_update(a->c, pr, cancelled))
331 abort_backend();
332 #ifdef DEBUG
333 fprintf(stderr, "+ cancelled = %d\n", *cancelled);
334 #endif
335 } else {
336 /* Data came in */
337 fgets(cline, 255, cmdout);
338 while (strlen(cline) > 0 && cline[strlen(cline) - 1] == '\n')
339 cline[strlen(cline) - 1] = '\0';
340 if (feof(cmdout))
341 break;
342 if (!dfui_be_progress_update(a->c, pr, cancelled))
343 abort_backend();
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);
352 if (*cancelled) {
353 #ifdef DEBUG
354 fprintf(stderr, "+ killing %d\n", pid);
355 #endif
356 n = kill(pid, SIGTERM);
357 #ifdef DEBUG
358 fprintf(stderr, "+ kill() = %d\n", n);
359 #endif
362 #ifdef DEBUG
363 fprintf(stderr, "+ pclosing %d\n", fileno(cmdout));
364 #endif
365 n = aura_pclose(cmdout) / 256;
366 #ifdef DEBUG
367 fprintf(stderr, "+ pclose() = %d\n", n);
368 #endif
369 return(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
376 * from the utility.
378 static int
379 command_execute(struct i_fn_args *a, struct dfui_progress *pr,
380 struct command *cmd)
382 FILE *log = NULL;
383 char *filename;
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);
388 else
389 dfui_info_set_short_desc(dfui_progress_get_info(pr), cmd->cmdline);
391 if (!dfui_be_progress_update(a->c, pr, &cancelled))
392 abort_backend();
394 while (!done) {
395 asprintf(&filename, "%sinstall.log", a->tmp);
396 log = fopen(filename, "a");
397 free(filename);
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);
405 if (log != NULL)
406 fclose(log);
408 if (cancelled) {
409 if (!dfui_be_progress_end(a->c))
410 abort_backend();
412 report_done = 0;
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"
417 "was cancelled.",
418 cmd->cmdline)) {
419 case 1:
420 /* View Log */
421 view_command_log(a);
422 break;
423 case 2:
424 /* Retry */
425 cancelled = 0;
426 report_done = 1;
427 break;
428 case 3:
429 /* Cancel */
430 cmd->result = COMMAND_RESULT_CANCELLED;
431 report_done = 1;
432 done = 1;
433 break;
434 case 4:
435 /* Skip */
436 cmd->result = COMMAND_RESULT_SKIPPED;
437 report_done = 1;
438 done = 1;
439 break;
443 if (!dfui_be_progress_begin(a->c, pr))
444 abort_backend();
446 } else if (cmd->failure_mode == COMMAND_FAILURE_IGNORE) {
447 cmd->result = 0;
448 done = 1;
449 } else if (cmd->result != 0 && cmd->failure_mode != COMMAND_FAILURE_WARN) {
450 if (!dfui_be_progress_end(a->c))
451 abort_backend();
453 report_done = 0;
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)) {
460 case 1:
461 /* View Log */
462 view_command_log(a);
463 break;
464 case 2:
465 /* Retry */
466 report_done = 1;
467 break;
468 case 3:
469 /* Cancel */
470 /* XXX need a better way to retain actual result */
471 cmd->result = COMMAND_RESULT_CANCELLED;
472 report_done = 1;
473 done = 1;
474 break;
475 case 4:
476 /* Skip */
477 /* XXX need a better way to retain actual result */
478 cmd->result = COMMAND_RESULT_SKIPPED;
479 report_done = 1;
480 done = 1;
481 break;
485 if (!dfui_be_progress_begin(a->c, pr))
486 abort_backend();
488 } else {
489 done = 1;
493 return(cmd->result);
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;
505 struct command *cmd;
506 int i;
507 int n = 0;
508 int result = 0;
509 int return_val = 1;
511 cmd = cmds->head;
512 while (cmd != NULL) {
513 n++;
514 cmd = cmd->next;
517 pr = dfui_progress_new(dfui_info_new(
518 "Executing Commands",
519 "Executing Commands",
520 ""),
523 if (!dfui_be_progress_begin(a->c, pr))
524 abort_backend();
526 i = 1;
527 for (cmd = cmds->head; cmd != NULL; cmd = cmd->next, i++) {
528 result = command_execute(a, pr, cmd);
529 if (result == COMMAND_RESULT_CANCELLED) {
530 return_val = 0;
531 break;
533 if (result > 0 && result < 256) {
534 return_val = 0;
535 if (cmd->failure_mode == COMMAND_FAILURE_ABORT) {
536 break;
539 dfui_progress_set_amount(pr, (i * 100) / n);
542 if (!dfui_be_progress_end(a->c))
543 abort_backend();
545 dfui_progress_free(pr);
547 return(return_val);
551 * Free the memory allocated for a queue of commands. This invalidates
552 * the pointer passed to it.
554 void
555 commands_free(struct commands *cmds)
557 struct command *cmd, *next;
559 cmd = cmds->head;
560 while (cmd != NULL) {
561 next = cmd->next;
562 if (cmd->cmdline != NULL)
563 free(cmd->cmdline);
564 if (cmd->desc != NULL)
565 free(cmd->desc);
566 if (cmd->tag != NULL)
567 free(cmd->tag);
568 AURA_FREE(cmd, command);
569 cmd = next;
571 AURA_FREE(cmds, commands);