#include <sys/time.h>
#include <sys/types.h>
#include <libgen.h>
#include <signal.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "libaura/mem.h"
#include "libaura/buffer.h"
#include "libaura/popen.h"
#include "libdfui/dfui.h"
#define NEEDS_COMMANDS_STRUCTURE_DEFINITIONS
#include "commands.h"
#undef NEEDS_COMMANDS_STRUCTURE_DEFINITIONS
#include "diskutil.h"
#include "functions.h"
#include "uiutil.h"
struct commands *
commands_new(void)
{
struct commands *cmds;
AURA_MALLOC(cmds, commands);
cmds->head = NULL;
cmds->tail = NULL;
return(cmds);
}
static struct command *
command_new(struct commands *cmds)
{
struct command *cmd;
AURA_MALLOC(cmd, command);
cmd->cmdline = NULL;
cmd->desc = NULL;
cmd->log_mode = COMMAND_LOG_VERBOSE;
cmd->failure_mode = COMMAND_FAILURE_ABORT;
cmd->tag = NULL;
cmd->result = COMMAND_RESULT_NEVER_EXECUTED;
cmd->output = NULL;
cmd->next = NULL;
if (cmds->head == NULL)
cmds->head = cmd;
else
cmds->tail->next = cmd;
cmd->prev = cmds->tail;
cmds->tail = cmd;
return(cmd);
}
struct command *
command_add(struct commands *cmds, const char *fmt, ...)
{
va_list args;
struct command *cmd;
cmd = command_new(cmds);
va_start(args, fmt);
vasprintf(&cmd->cmdline, fmt, args);
va_end(args);
return(cmd);
}
void
command_set_log_mode(struct command *cmd, int log_mode)
{
cmd->log_mode = log_mode;
}
void
command_set_failure_mode(struct command *cmd, int failure_mode)
{
cmd->failure_mode = failure_mode;
}
void
command_set_desc(struct command *cmd, const char *fmt, ...)
{
va_list args;
if (cmd->desc != NULL)
free(cmd->desc);
va_start(args, fmt);
vasprintf(&cmd->desc, fmt, args);
va_end(args);
}
void
command_set_tag(struct command *cmd, const char *fmt, ...)
{
va_list args;
if (cmd->tag != NULL)
free(cmd->tag);
va_start(args, fmt);
vasprintf(&cmd->tag, fmt, args);
va_end(args);
}
struct command *
command_get_first(const struct commands *cmds)
{
return(cmds->head);
}
struct command *
command_get_next(const struct command *cmd)
{
return(cmd->next);
}
char *
command_get_cmdline(const struct command *cmd)
{
return(cmd->cmdline);
}
char *
command_get_tag(const struct command *cmd)
{
return(cmd->tag);
}
int
command_get_result(const struct command *cmd)
{
return(cmd->result);
}
void
view_command_log(struct i_fn_args *a)
{
struct dfui_form *f;
struct dfui_response *r;
struct aura_buffer *error_log;
error_log = aura_buffer_new(1024);
aura_buffer_cat_file(error_log, "%sinstall.log", a->tmp);
f = dfui_form_create(
"error_log",
"Error Log",
aura_buffer_buf(error_log),
"",
"p", "role", "informative",
"p", "minimum_width", "72",
"p", "monospaced", "true",
"a", "ok", "OK", "", "",
NULL);
if (!dfui_be_present(a->c, f, &r))
abort_backend();
dfui_form_free(f);
dfui_response_free(r);
aura_buffer_free(error_log);
}
void
commands_preview(struct dfui_connection *c, const struct commands *cmds)
{
struct command *cmd;
struct aura_buffer *preview;
preview = aura_buffer_new(1024);
for (cmd = cmds->head; cmd != NULL; cmd = cmd->next) {
aura_buffer_cat(preview, cmd->cmdline);
aura_buffer_cat(preview, "\n");
}
inform(c, "%s", aura_buffer_buf(preview));
aura_buffer_free(preview);
}
static int
pipe_loop(struct i_fn_args *a, struct dfui_progress *pr,
struct command *cmd, int *cancelled)
{
FILE *cmdout = NULL;
struct timeval tv = { 1, 0 };
char cline[256];
char *command;
pid_t pid;
fd_set r;
int n;
asprintf(&command, "(%s) 2>&1 </dev/null", cmd->cmdline);
fflush(stdout);
cmdout = aura_popen("%s", "r", command);
free(command);
if (cmdout == NULL) {
i_log(a, "! could not aura_popen() command");
return(COMMAND_RESULT_POPEN_ERR);
}
pid = aura_pgetpid(cmdout);
#ifdef DEBUG
fprintf(stderr, "+ pid = %d\n", pid);
#endif
for (;;) {
if (*cancelled)
break;
FD_ZERO(&r);
FD_SET(fileno(cmdout), &r);
n = select(fileno(cmdout) + 1, &r, NULL, NULL, &tv);
#ifdef DEBUG
fprintf(stderr, "+ select() = %d\n", n);
#endif
if (n < 0) {
i_log(a, "! select() failed\n");
aura_pclose(cmdout);
return(COMMAND_RESULT_SELECT_ERR);
} else if (n == 0) {
if (!dfui_be_progress_update(a->c, pr, cancelled))
abort_backend();
#ifdef DEBUG
fprintf(stderr, "+ cancelled = %d\n", *cancelled);
#endif
} else {
fgets(cline, 255, cmdout);
while (strlen(cline) > 0 && cline[strlen(cline) - 1] == '\n')
cline[strlen(cline) - 1] = '\0';
if (feof(cmdout))
break;
if (!dfui_be_progress_update(a->c, pr, cancelled))
abort_backend();
if (cmd->log_mode == COMMAND_LOG_VERBOSE) {
i_log(a, "| %s", cline);
} else if (cmd->log_mode != COMMAND_LOG_SILENT) {
fprintf(stderr, "| %s\n", cline);
}
}
}
if (*cancelled) {
#ifdef DEBUG
fprintf(stderr, "+ killing %d\n", pid);
#endif
n = kill(pid, SIGTERM);
#ifdef DEBUG
fprintf(stderr, "+ kill() = %d\n", n);
#endif
}
#ifdef DEBUG
fprintf(stderr, "+ pclosing %d\n", fileno(cmdout));
#endif
n = aura_pclose(cmdout) / 256;
#ifdef DEBUG
fprintf(stderr, "+ pclose() = %d\n", n);
#endif
return(n);
}
static int
command_execute(struct i_fn_args *a, struct dfui_progress *pr,
struct command *cmd)
{
FILE *log = NULL;
char *filename;
int cancelled = 0, done = 0, report_done = 0;
if (cmd->desc != NULL)
dfui_info_set_short_desc(dfui_progress_get_info(pr), cmd->desc);
else
dfui_info_set_short_desc(dfui_progress_get_info(pr), cmd->cmdline);
if (!dfui_be_progress_update(a->c, pr, &cancelled))
abort_backend();
while (!done) {
asprintf(&filename, "%sinstall.log", a->tmp);
log = fopen(filename, "a");
free(filename);
if (cmd->log_mode != COMMAND_LOG_SILENT)
i_log(a, ",-<<< Executing `%s'", cmd->cmdline);
cmd->result = pipe_loop(a, pr, cmd, &cancelled);
if (cmd->log_mode != COMMAND_LOG_SILENT)
i_log(a, "`->>> Exit status: %d\n", cmd->result);
if (log != NULL)
fclose(log);
if (cancelled) {
if (!dfui_be_progress_end(a->c))
abort_backend();
report_done = 0;
while (!report_done) {
switch (dfui_be_present_dialog(a->c, "Cancelled",
"View Log|Retry|Cancel|Skip",
"Execution of the command\n\n%s\n\n"
"was cancelled.",
cmd->cmdline)) {
case 1:
view_command_log(a);
break;
case 2:
cancelled = 0;
report_done = 1;
break;
case 3:
cmd->result = COMMAND_RESULT_CANCELLED;
report_done = 1;
done = 1;
break;
case 4:
cmd->result = COMMAND_RESULT_SKIPPED;
report_done = 1;
done = 1;
break;
}
}
if (!dfui_be_progress_begin(a->c, pr))
abort_backend();
} else if (cmd->failure_mode == COMMAND_FAILURE_IGNORE) {
cmd->result = 0;
done = 1;
} else if (cmd->result != 0 && cmd->failure_mode != COMMAND_FAILURE_WARN) {
if (!dfui_be_progress_end(a->c))
abort_backend();
report_done = 0;
while (!report_done) {
switch (dfui_be_present_dialog(a->c, "Command Failed!",
"View Log|Retry|Cancel|Skip",
"Execution of the command\n\n%s\n\n"
"FAILED with a return code of %d.",
cmd->cmdline, cmd->result)) {
case 1:
view_command_log(a);
break;
case 2:
report_done = 1;
break;
case 3:
cmd->result = COMMAND_RESULT_CANCELLED;
report_done = 1;
done = 1;
break;
case 4:
cmd->result = COMMAND_RESULT_SKIPPED;
report_done = 1;
done = 1;
break;
}
}
if (!dfui_be_progress_begin(a->c, pr))
abort_backend();
} else {
done = 1;
}
}
return(cmd->result);
}
int
commands_execute(struct i_fn_args *a, struct commands *cmds)
{
struct dfui_progress *pr;
struct command *cmd;
int i;
int n = 0;
int result = 0;
int return_val = 1;
cmd = cmds->head;
while (cmd != NULL) {
n++;
cmd = cmd->next;
}
pr = dfui_progress_new(dfui_info_new(
"Executing Commands",
"Executing Commands",
""),
0);
if (!dfui_be_progress_begin(a->c, pr))
abort_backend();
i = 1;
for (cmd = cmds->head; cmd != NULL; cmd = cmd->next, i++) {
result = command_execute(a, pr, cmd);
if (result == COMMAND_RESULT_CANCELLED) {
return_val = 0;
break;
}
if (result > 0 && result < 256) {
return_val = 0;
if (cmd->failure_mode == COMMAND_FAILURE_ABORT) {
break;
}
}
dfui_progress_set_amount(pr, (i * 100) / n);
}
if (!dfui_be_progress_end(a->c))
abort_backend();
dfui_progress_free(pr);
return(return_val);
}
void
commands_free(struct commands *cmds)
{
struct command *cmd, *next;
cmd = cmds->head;
while (cmd != NULL) {
next = cmd->next;
if (cmd->cmdline != NULL)
free(cmd->cmdline);
if (cmd->desc != NULL)
free(cmd->desc);
if (cmd->tag != NULL)
free(cmd->tag);
AURA_FREE(cmd, command);
cmd = next;
}
AURA_FREE(cmds, commands);
}