#include "common.h" #include "commands.h" #include "frontend.h" #include "database.h" #include "question.h" #include "template.h" #include "plugin.h" #include "strutl.h" #include #include #define CHECKARGC(pred) \ ({\ char *out; \ if (!(argc pred)) {\ if (asprintf(&out, "%u Incorrect number of arguments", \ CMDSTATUS_SYNTAXERROR) == -1) \ return strdup("1"); \ else \ return out; \ } \ }) char * command_input(struct confmodule *mod, char *arg) { int visible = 1; struct question *q = 0; char *qtag; char *priority; char *argv[3]; char *out; int argc; argc = strcmdsplit(arg, argv, DIM(argv)); CHECKARGC(== 2); priority = argv[0]; qtag = argv[1]; q = mod->questions->methods.get(mod->questions, qtag); if (q == NULL) { if (asprintf(&out, "%u \"%s\" doesn't exist", CMDSTATUS_BADQUESTION, qtag) == -1) return strdup("1"); else return out; } /* check priority */ visible = (mod->frontend->interactive && mod->questions->methods.is_visible(mod->questions, qtag, priority)); if (visible) visible = mod->frontend->methods.add(mod->frontend, q); else mod->frontend->methods.add_noninteractive(mod->frontend, q); free(q->priority); q->priority = strdup(priority); if (visible) { mod->backed_up = 0; asprintf(&out, "%u question will be asked", CMDSTATUS_SUCCESS); } else asprintf(&out, "%u question skipped", CMDSTATUS_INPUTINVISIBLE); question_deref(q); return out; } char * command_clear(struct confmodule *mod, char *arg) { char *argv[3]; int argc; char *out; argc = strcmdsplit(arg, argv, DIM(argv)); CHECKARGC(== 0); mod->frontend->methods.clear(mod->frontend); asprintf(&out, "%u", CMDSTATUS_SUCCESS); return out; } char * command_version(struct confmodule *mod, char *arg) { int ver; char *argv[3]; int argc; char *out; argc = strcmdsplit(arg, argv, DIM(argv)); CHECKARGC(== 1); ver = atoi(argv[0]); if (ver < DEBCONF_VERSION) asprintf(&out, "%u Version too low (%d)", CMDSTATUS_BADVERSION, ver); else if (ver > DEBCONF_VERSION) asprintf(&out, "%u Version too high (%d)", CMDSTATUS_BADVERSION, ver); else asprintf(&out, "%u %.1f", CMDSTATUS_SUCCESS, DEBCONF_VERSION); return out; } char * command_capb(struct confmodule *mod, char *arg) { int i; char *argv[32]; int argc; char *out, *outend; size_t outalloc; struct plugin *plugin; void *plugin_state; argc = strcmdsplit(arg, argv, DIM(argv)); /* FIXME: frontend.h should provide a method to prevent direct * access to capability member */ mod->frontend->capability = 0; for (i = 0; i < argc; i++) { if (strcmp(argv[i], "backup") == 0) mod->frontend->capability |= DCF_CAPB_BACKUP; else if (strcmp(argv[i], "progresscancel") == 0) mod->frontend->capability |= DCF_CAPB_PROGRESSCANCEL; else if (strcmp(argv[i], "align") == 0) mod->frontend->capability |= DCF_CAPB_ALIGN; else if (strcmp(argv[i], "escape") == 0) mod->frontend->capability |= DCF_CAPB_ESCAPE; } if (asprintf(&out, "%u multiselect backup progresscancel align escape", CMDSTATUS_SUCCESS) == -1) DIE("Out of memory"); plugin_state = NULL; outend = strchr(out, '\0'); outalloc = outend - out + 1; while ((plugin = plugin_iterate(mod->frontend, &plugin_state)) != NULL) { size_t namelen; char *newout; namelen = strlen(plugin->name); outalloc += 8 + namelen; newout = realloc(out, outalloc); if (!newout) DIE("Out of memory"); outend = newout + (outend - out); out = newout; outend = mempcpy(outend, " plugin-", 8); outend = mempcpy(outend, plugin->name, namelen); *outend = '\0'; } return out; } char * command_title(struct confmodule *mod, char *arg) { char *out; mod->frontend->methods.set_title(mod->frontend, arg); asprintf(&out, "%u", CMDSTATUS_SUCCESS); return out; } char * command_beginblock(struct confmodule *mod, char *arg) { char *out; asprintf(&out, "%u", CMDSTATUS_SUCCESS); return out; } char * command_endblock(struct confmodule *mod, char *arg) { char *out; asprintf(&out, "%u", CMDSTATUS_SUCCESS); return out; } char * command_go(struct confmodule *mod, char *arg) { char *argv[3]; int argc; char *out; const char *running_frontend = NULL; const char *requested_frontend = NULL; int ret; argc = strcmdsplit(arg, argv, DIM(argv) - 1); CHECKARGC(== 0); requested_frontend = getenv("DEBIAN_FRONTEND"); if (requested_frontend) { if (mod != NULL && mod->frontend != NULL) running_frontend = mod->frontend->name; if (requested_frontend && running_frontend && strcmp(running_frontend, requested_frontend) != 0) { struct frontend *new_frontend; /* Shut down the current frontend, but do not delete unless opening the new one actually succeeds */ mod->frontend->methods.shutdown(mod->frontend); new_frontend = frontend_new(mod->config, mod->templates, mod->questions); if (new_frontend) { struct frontend *old_frontend = mod->frontend; mod->frontend = new_frontend; new_frontend->questions = old_frontend->questions; new_frontend->questions_noninteractive = old_frontend->questions_noninteractive; frontend_delete(old_frontend); } else { /* If the new frontend failed, reinitialise the old one */ mod->frontend->methods.initialize(mod->frontend, mod->config); } } } mod->frontend->methods.go_noninteractive(mod->frontend); ret = mod->frontend->methods.go(mod->frontend); if (ret == CMDSTATUS_GOBACK || mod->backed_up != 0) { mod->backed_up = 1; asprintf(&out, "%u backup", CMDSTATUS_GOBACK); mod->update_seen_questions(mod, STACK_SEEN_REMOVE); } else if (ret == DC_NOTOK) /* TODO return value namespace */ { mod->backed_up = 0; asprintf(&out, "%u internal error", CMDSTATUS_INTERNALERROR); mod->update_seen_questions(mod, STACK_SEEN_REMOVE); } else { mod->backed_up = 0; asprintf(&out, "%u ok", CMDSTATUS_SUCCESS); mod->update_seen_questions(mod, STACK_SEEN_ADD); } mod->frontend->methods.clear(mod->frontend); return out; } static char *escapestr_frontend(const char *in) { static size_t buflen = 0; static char *buf = NULL; size_t inlen; const char *p; if (in == 0) return 0; inlen = strlen(in) + 1; /* Each newline will consume an additional byte due to escaping. */ for (p = in; *p; ++p) if (*p == '\\' || *p == '\n') ++inlen; if (buflen < inlen) { buflen = inlen; buf = realloc(buf, buflen * sizeof *buf); if (!buf) DIE("Out of memory"); } strescape(in, buf, buflen, STRESCAPE_CAPB); return buf; } char * command_get(struct confmodule *mod, char *arg) { struct question *q; char *argv[3]; int argc; char *out; argc = strcmdsplit(arg, argv, DIM(argv)); CHECKARGC(== 1); q = mod->questions->methods.get(mod->questions, argv[0]); if (q == NULL) asprintf(&out, "%u %s doesn't exist", CMDSTATUS_BADQUESTION, argv[0]); else { const char *value = question_getvalue(q, "C"); if (mod->frontend->capability & DCF_CAPB_ESCAPE) { char *esc = escapestr_frontend(value); asprintf(&out, "%u %s", CMDSTATUS_ESCAPEDDATA, esc ? esc : ""); } else asprintf(&out, "%u %s", CMDSTATUS_SUCCESS, value ? value : ""); } question_deref(q); return out; } char * command_set(struct confmodule *mod, char *arg) { struct question *q; char *argv[2] = { "", "" }; int argc; char *out; argc = strcmdsplit(arg, argv, DIM(argv)); CHECKARGC(>= 1); q = mod->questions->methods.get(mod->questions, argv[0]); if (q == NULL) asprintf(&out, "%u %s doesn't exist", CMDSTATUS_BADQUESTION, argv[0]); else { char *prev = STRDUP(question_getvalue(q, "")); question_setvalue(q, argv[1]); if (frontend_qdb_set(mod->questions, q, prev)) asprintf(&out, "%u value set", CMDSTATUS_SUCCESS); else asprintf(&out, "%u cannot set value", CMDSTATUS_INTERNALERROR); free(prev); } question_deref(q); return out; } char * command_reset(struct confmodule *mod, char *arg) { struct question *q; char *argv[2]; int argc; char *out; argc = strcmdsplit(arg, argv, DIM(argv)); CHECKARGC(== 1); q = mod->questions->methods.get(mod->questions, argv[0]); if (q == NULL) asprintf(&out, "%u %s doesn't exist", CMDSTATUS_BADQUESTION, argv[0]); else { DELETE(q->value); question_clear_flag(q, DC_QFLAG_SEEN); if (mod->questions->methods.set(mod->questions, q) != 0) asprintf(&out, "%u", CMDSTATUS_SUCCESS); else asprintf(&out, "%u cannot reset value", CMDSTATUS_INTERNALERROR); } question_deref(q); return out; } char * command_subst(struct confmodule *mod, char *arg) { struct question *q; struct questionvariable; char *variable; char *argv[3] = { "", "", "" }; int argc; char *out; argc = strcmdsplit(arg, argv, DIM(argv)); CHECKARGC(>= 2); variable = argv[1]; q = mod->questions->methods.get(mod->questions, argv[0]); if (q == NULL) asprintf(&out, "%u %s doesn't exist", CMDSTATUS_BADQUESTION, argv[0]); else { question_variable_add(q, variable, argv[2]); if (mod->questions->methods.set(mod->questions, q) != 0) asprintf(&out, "%u", CMDSTATUS_SUCCESS); else asprintf(&out, "%u substitution failed", CMDSTATUS_INTERNALERROR); } question_deref(q); return out; } char * command_register(struct confmodule *mod, char *arg) { struct question *q; struct template *t; char *argv[4]; int argc; char *out; argc = strcmdsplit(arg, argv, DIM(argv)); CHECKARGC(== 2); t = mod->templates->methods.get(mod->templates, argv[0]); if (t == NULL) { asprintf(&out, "%u No such template, \"%s\"", CMDSTATUS_BADQUESTION, argv[0]); return out; } q = mod->questions->methods.get(mod->questions, argv[1]); if (q == NULL) q = question_new(argv[1]); if (q == NULL) { asprintf(&out, "%u Internal error making question", CMDSTATUS_INTERNALERROR); return out; } question_owner_add(q, mod->owner); q->template = t; /* steal reference from mod->templates->methods.get above */ mod->questions->methods.set(mod->questions, q); question_deref(q); asprintf(&out, "%u", CMDSTATUS_SUCCESS); return out; } char * command_unregister(struct confmodule *mod, char *arg) { struct question *q; char *argv[3]; int argc; char *out; argc = strcmdsplit(arg, argv, DIM(argv)); CHECKARGC(== 1); q = mod->questions->methods.get(mod->questions, argv[0]); if (q == NULL) { asprintf(&out, "%u %s doesn't exist", CMDSTATUS_BADQUESTION, argv[0]); return out; } question_owner_delete(q, mod->owner); question_deref(q); asprintf(&out, "%u", CMDSTATUS_SUCCESS); return out; } char * command_purge(struct confmodule *mod, char *arg) { char *out; mod->questions->methods.disownall(mod->questions, mod->owner); asprintf(&out, "%u", CMDSTATUS_SUCCESS); return out; } char * command_metaget(struct confmodule *mod, char *arg) { struct question *q; char *value; char *argv[4]; int argc; char *out; argc = strcmdsplit(arg, argv, DIM(argv)); CHECKARGC(== 2); q = mod->questions->methods.get(mod->questions, argv[0]); if (q == NULL) { asprintf(&out, "%u %s doesn't exist", CMDSTATUS_BADQUESTION, argv[0]); return out; } value = question_get_raw_field(q, "", argv[1]); if (value == NULL) asprintf(&out, "%u %s does not exist", CMDSTATUS_BADQUESTION, argv[1]); else { if (mod->frontend->capability & DCF_CAPB_ESCAPE) { char *esc = escapestr_frontend(value); asprintf(&out, "%u %s", CMDSTATUS_ESCAPEDDATA, esc); } else asprintf(&out, "%u %s", CMDSTATUS_SUCCESS, value); } free(value); question_deref(q); return out; } char * command_fget(struct confmodule *mod, char *arg) { struct question *q; char *field; char *argv[4]; int argc; char *out; argc = strcmdsplit(arg, argv, DIM(argv)); CHECKARGC(== 2); q = mod->questions->methods.get(mod->questions, argv[0]); if (q == NULL) { asprintf(&out, "%u %s doesn't exist", CMDSTATUS_BADQUESTION, argv[0]); return out; } field = argv[1]; /* isdefault is for backward compability only */ if (strcmp(field, "isdefault") == 0) asprintf(&out, "%u %s", CMDSTATUS_SUCCESS, (question_get_flag(q, DC_QFLAG_SEEN) ? "false" : "true")); else asprintf(&out, "%u %s", CMDSTATUS_SUCCESS, (question_get_flag(q, field) ? "true" : "false")); question_deref(q); return out; } char * command_fset(struct confmodule *mod, char *arg) { struct question *q; char *field; char *argv[5]; int argc; char *out; argc = strcmdsplit(arg, argv, DIM(argv)); CHECKARGC(== 3); q = mod->questions->methods.get(mod->questions, argv[0]); if (q == NULL) { asprintf(&out, "%u %s doesn't exist", CMDSTATUS_BADQUESTION, argv[0]); return out; } field = argv[1]; if (strcmp(field, "isdefault") == 0) { if (strcmp(argv[2], "false") == 0) question_set_flag(q, DC_QFLAG_SEEN); else question_clear_flag(q, DC_QFLAG_SEEN); } else { if (strcmp(argv[2], "true") == 0) question_set_flag(q, field); else question_clear_flag(q, field); } asprintf(&out, "%u %s", CMDSTATUS_SUCCESS, argv[2]); question_deref(q); return out; } char * command_exist(struct confmodule *mod, char *arg) { struct question *q; char *argv[3]; int argc; char *out; argc = strcmdsplit(arg, argv, DIM(argv)); CHECKARGC(== 1); q = mod->questions->methods.get(mod->questions, argv[0]); if (q) { question_deref(q); asprintf(&out, "%u true", CMDSTATUS_SUCCESS); } else { asprintf(&out, "%u false", CMDSTATUS_SUCCESS); } return out; } char * command_stop(struct confmodule *mod, char *arg) { char *argv[3]; int argc; argc = strcmdsplit(arg, argv, DIM(argv)); CHECKARGC(== 0); if (mod->pid != 0) { waitpid(mod->pid, NULL, 0); } return strdup(""); } char * command_x_loadtemplatefile(struct confmodule *mod, char *arg) { struct template *t = NULL; struct question *q = NULL; char *argv[3] = { "", "", "" }; int argc; char *out; argc = strcmdsplit(arg, argv, DIM(argv)); CHECKARGC(>= 1); CHECKARGC(<= 2); t = template_load(argv[0]); while (t) { mod->templates->methods.set(mod->templates, t); q = mod->questions->methods.get(mod->questions, t->tag); if (q == NULL) { q = question_new(t->tag); q->template = t; template_ref(t); } else if (q->template != t) { template_deref(q->template); q->template = t; template_ref(t); } if (*argv[1]) question_owner_add(q, argv[1]); mod->questions->methods.set(mod->questions, q); question_deref(q); t = t->next; } asprintf(&out, "%u OK", CMDSTATUS_SUCCESS); return out; } char * command_progress(struct confmodule *mod, char *arg) { int min, max; struct question *q = NULL; char *argv[6]; int argc; char *out; int ret; argc = strcmdsplit(arg, argv, DIM(argv)); CHECKARGC(>= 1); if (strcasecmp(argv[0], "start") == 0) { CHECKARGC(== 4); min = atoi(argv[1]); max = atoi(argv[2]); if (min > max) { asprintf(&out, "%u min (%d) > max (%d)", CMDSTATUS_SYNTAXERROR, min, max); return out; } q = mod->questions->methods.get(mod->questions, argv[3]); if (q == NULL) { asprintf(&out, "%u %s does not exist", CMDSTATUS_BADQUESTION, argv[3]); return out; } mod->frontend->methods.progress_start(mod->frontend, min, max, q); question_deref(q); } else if (strcasecmp(argv[0], "set") == 0) { CHECKARGC(== 2); ret = mod->frontend->methods.progress_set(mod->frontend, atoi(argv[1])); if (ret == DC_GOBACK) { asprintf(&out, "%u progress bar cancelled", CMDSTATUS_PROGRESSCANCELLED); return out; } } else if (strcasecmp(argv[0], "step") == 0) { CHECKARGC(== 2); ret = mod->frontend->methods.progress_step(mod->frontend, atoi(argv[1])); if (ret == DC_GOBACK) { asprintf(&out, "%u progress bar cancelled", CMDSTATUS_PROGRESSCANCELLED); return out; } } else if (strcasecmp(argv[0], "info") == 0) { CHECKARGC(== 2); q = mod->questions->methods.get(mod->questions, argv[1]); if (q == NULL) { asprintf(&out, "%u %s does not exist", CMDSTATUS_BADQUESTION, argv[1]); return out; } ret = mod->frontend->methods.progress_info(mod->frontend, q); question_deref(q); if (ret == DC_GOBACK) { asprintf(&out, "%u progress bar cancelled", CMDSTATUS_PROGRESSCANCELLED); return out; } } else if (strcasecmp(argv[0], "stop") == 0) { mod->frontend->methods.progress_stop(mod->frontend); } else { asprintf(&out, "%u unknown subcommand %s", CMDSTATUS_SYNTAXERROR, argv[0]); return out; } asprintf(&out, "%u OK", CMDSTATUS_SUCCESS); return out; } /* * takes a template name and sets the debconf title to the description * of the template. This will allow us to localise titles. */ char * command_settitle(struct confmodule *mod, char *arg) { struct question *q = NULL; char *value; char *out; q = mod->questions->methods.get(mod->questions, arg); if (q == NULL) { asprintf(&out, "%u %s does not exist", CMDSTATUS_BADQUESTION, arg); return out; } value = question_get_raw_field(q, "", "description"); question_deref(q); if (value == NULL) { asprintf(&out, "%u %s description field does not exist", CMDSTATUS_BADQUESTION, arg); return out; } mod->frontend->methods.set_title(mod->frontend, value); free(value); asprintf(&out, "%u OK", CMDSTATUS_SUCCESS); return out; } char *command_x_save(struct confmodule *mod, char *arg) { char *argv[2]; int argc, ret = DC_OK; char *out; argc = strcmdsplit(arg, argv, DIM(argv)); CHECKARGC(== 0); if (mod) ret = mod->save(mod); if (ret == DC_OK) asprintf(&out, "%u OK", CMDSTATUS_SUCCESS); else asprintf(&out, "%u not OK", CMDSTATUS_INTERNALERROR); return out; } /* * Displays the given template as an out-of-band informative message. Unlike * inputting a note, this doesn't require an acknowledgement from the user, * and depending on the frontend it may not even be displayed at all. Like * PROGRESS INFO, frontends should display the info persistently until some * other info comes along. */ char * command_info(struct confmodule *mod, char *arg) { struct question *q = NULL; char *out; q = mod->questions->methods.get(mod->questions, arg); if (q == NULL) { asprintf(&out, "%u %s does not exist", CMDSTATUS_BADQUESTION, arg); return out; } mod->frontend->methods.info(mod->frontend, q); question_deref(q); asprintf(&out, "%u OK", CMDSTATUS_SUCCESS); return out; } /* * Accept template data from the client * (for use with the passthrough frontend) */ char * command_data(struct confmodule *mod, char *arg) { char *argv[3]; const char *tag = NULL, *item = NULL, *value = NULL; int argc; struct template *t = NULL; struct question *q = NULL; char *out; argc = strcmdsplit(arg, argv, DIM(argv)); CHECKARGC(== 3); tag = argv[0]; item = argv[1]; value = unescapestr(argv[2]); t = mod->templates->methods.get(mod->templates, tag); if (t == NULL) { t = template_new(tag); mod->templates->methods.set(mod->templates, t); q = mod->questions->methods.get(mod->questions, t->tag); if (q == NULL) { q = question_new(t->tag); q->template = t; template_ref(t); } else if (q->template != t) { template_deref(q->template); q->template = t; template_ref(t); } template_lset(t, NULL, item, value); mod->questions->methods.set(mod->questions, q); question_deref(q); } else template_lset(t, NULL, item, value); template_deref(t); asprintf(&out, "%u OK", CMDSTATUS_SUCCESS); return out; } /* vim: expandtab sw=4 */