/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/*
* svccfg(1) interpreter and command execution engine.
*/
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <libintl.h>
#include <libtecla.h>
#include <md5.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "manifest_find.h"
#include "manifest_hash.h"
#include "svccfg.h"
#define MS_PER_US 1000
engine_state_t *est;
/*
* Replacement lex(1) character retrieval routines.
*/
int
engine_cmd_getc(engine_state_t *E)
{
if (E->sc_cmd_file != NULL)
return (getc(E->sc_cmd_file));
if (E->sc_cmd_flags & SC_CMD_EOF)
return (EOF);
if (E->sc_cmd_bufoff < E->sc_cmd_bufsz)
return (*(E->sc_cmd_buf + E->sc_cmd_bufoff++));
if (!(E->sc_cmd_flags & SC_CMD_IACTIVE)) {
E->sc_cmd_flags |= SC_CMD_EOF;
return (EOF);
} else {
#ifdef NATIVE_BUILD
return (EOF);
#else
extern int parens;
if (parens <= 0) {
E->sc_cmd_flags |= SC_CMD_EOF;
return (EOF);
}
for (;;) {
E->sc_cmd_buf = gl_get_line(E->sc_gl, "> ", NULL, -1);
if (E->sc_cmd_buf != NULL)
break;
switch (gl_return_status(E->sc_gl)) {
case GLR_SIGNAL:
gl_abandon_line(E->sc_gl);
continue;
case GLR_EOF:
E->sc_cmd_flags |= SC_CMD_EOF;
return (EOF);
case GLR_ERROR:
uu_die(gettext("Error reading terminal: %s.\n"),
gl_error_message(E->sc_gl, NULL, 0));
/* NOTREACHED */
default:
#ifndef NDEBUG
(void) fprintf(stderr, "%s:%d: gl_get_line() "
"returned unexpected value %d.\n", __FILE__,
__LINE__, gl_return_status(E->sc_gl));
#endif
abort();
}
}
E->sc_cmd_bufsz = strlen(E->sc_cmd_buf);
E->sc_cmd_bufoff = 1;
return (E->sc_cmd_buf[0]);
#endif /* NATIVE_BUILD */
}
}
int
engine_cmd_ungetc(engine_state_t *E, char c)
{
if (E->sc_cmd_file != NULL)
return (ungetc(c, E->sc_cmd_file));
if (E->sc_cmd_buf != NULL)
*(E->sc_cmd_buf + --E->sc_cmd_bufoff) = c;
return (c);
}
/*ARGSUSED*/
void
engine_cmd_nputs(engine_state_t *E, char *c, size_t n)
{
/* our lexer shouldn't need this state */
exit(11);
}
int
engine_exec(char *cmd)
{
est->sc_cmd_buf = cmd;
est->sc_cmd_bufsz = strlen(cmd) + 1;
est->sc_cmd_bufoff = 0;
(void) yyparse();
return (0);
}
#ifndef NATIVE_BUILD
/* ARGSUSED */
static
CPL_CHECK_FN(check_xml)
{
const char *ext;
if (strlen(pathname) < 4)
return (0);
ext = pathname + strlen(pathname) - 4;
return (strcmp(ext, ".xml") == 0 ? 1 : 0);
}
static const char * const whitespace = " \t";
static
CPL_MATCH_FN(complete_single_xml_file_arg)
{
const char *arg1 = data;
int arg1end_i, ret;
CplFileConf *cfc;
arg1end_i = arg1 + strcspn(arg1, whitespace) - line;
if (arg1end_i < word_end)
return (0);
cfc = new_CplFileConf();
if (cfc == NULL) {
cpl_record_error(cpl, "Out of memory.");
return (1);
}
cfc_set_check_fn(cfc, check_xml, NULL);
ret = cpl_file_completions(cpl, cfc, line, word_end);
(void) del_CplFileConf(cfc);
return (ret);
}
static struct cmd_info {
const char *name;
uint32_t flags;
CplMatchFn *complete_args_f;
} cmds[] = {
{ "validate", CS_GLOBAL, complete_single_xml_file_arg },
{ "import", CS_GLOBAL, complete_single_xml_file_arg },
{ "cleanup", CS_GLOBAL, NULL},
{ "export", CS_GLOBAL, NULL },
{ "archive", CS_GLOBAL, NULL },
{ "apply", CS_GLOBAL, complete_single_xml_file_arg },
{ "extract", CS_GLOBAL, NULL },
{ "repository", CS_GLOBAL, NULL },
{ "inventory", CS_GLOBAL, complete_single_xml_file_arg },
{ "set", CS_GLOBAL, NULL },
{ "end", CS_GLOBAL, NULL },
{ "exit", CS_GLOBAL, NULL },
{ "quit", CS_GLOBAL, NULL },
{ "help", CS_GLOBAL, NULL },
{ "delete", CS_GLOBAL, NULL },
{ "select", CS_GLOBAL, complete_select },
{ "unselect", CS_SVC | CS_INST | CS_SNAP, NULL },
{ "list", CS_SCOPE | CS_SVC | CS_SNAP, NULL },
{ "add", CS_SCOPE | CS_SVC, NULL },
{ "listpg", CS_SVC | CS_INST | CS_SNAP, NULL },
{ "addpg", CS_SVC | CS_INST, NULL },
{ "delpg", CS_SVC | CS_INST, NULL },
{ "delhash", CS_GLOBAL, complete_single_xml_file_arg },
{ "listprop", CS_SVC | CS_INST | CS_SNAP, NULL },
{ "setprop", CS_SVC | CS_INST, NULL },
{ "delprop", CS_SVC | CS_INST, NULL },
{ "editprop", CS_SVC | CS_INST, NULL },
{ "describe", CS_SVC | CS_INST | CS_SNAP, NULL },
{ "listsnap", CS_INST | CS_SNAP, NULL },
{ "selectsnap", CS_INST | CS_SNAP, NULL },
{ "revert", CS_INST | CS_SNAP, NULL },
{ "refresh", CS_INST, NULL },
{ NULL }
};
int
add_cmd_matches(WordCompletion *cpl, const char *line, int word_end,
uint32_t scope)
{
int word_start, err;
size_t len;
const char *bol;
struct cmd_info *cip;
word_start = strspn(line, whitespace);
len = word_end - word_start;
bol = line + word_end - len;
for (cip = cmds; cip->name != NULL; ++cip) {
if ((cip->flags & scope) == 0)
continue;
if (strncmp(cip->name, bol, len) == 0) {
err = cpl_add_completion(cpl, line, word_start,
word_end, cip->name + len, "", " ");
if (err != 0)
return (err);
}
}
return (0);
}
/*
* Suggest completions. We must first determine if the cursor is in command
* position or in argument position. If the former, complete_command() finds
* matching commands. If the latter, we tail-call the command-specific
* argument-completion routine in the cmds table.
*/
/* ARGSUSED */
static
CPL_MATCH_FN(complete)
{
const char *arg0, *arg1;
size_t arg0len;
struct cmd_info *cip;
arg0 = line + strspn(line, whitespace);
arg0len = strcspn(arg0, whitespace);
if ((arg0 + arg0len) - line >= word_end ||
(arg0[arg0len] != ' ' && arg0[arg0len] != '\t'))
return (complete_command(cpl, (void *)arg0, line, word_end));
arg1 = arg0 + arg0len;
arg1 += strspn(arg1, whitespace);
for (cip = cmds; cip->name != NULL; ++cip) {
if (strlen(cip->name) != arg0len)
continue;
if (strncmp(cip->name, arg0, arg0len) != 0)
continue;
if (cip->complete_args_f == NULL)
break;
return (cip->complete_args_f(cpl, (void *)arg1, line,
word_end));
}
return (0);
}
#endif /* NATIVE_BUILD */
int
engine_interp()
{
#ifdef NATIVE_BUILD
uu_die("native build does not support interactive mode.");
#else
char *selfmri;
size_t sfsz;
int r;
extern int parens;
(void) sigset(SIGINT, SIG_IGN);
est->sc_gl = new_GetLine(512, 8000);
if (est->sc_gl == NULL)
uu_die(gettext("Out of memory.\n"));
/* The longest string is "[snapname]fmri[:instname]> ". */
sfsz = 1 + max_scf_name_len + 1 + max_scf_fmri_len + 2 +
max_scf_name_len + 1 + 2 + 1;
selfmri = safe_malloc(sfsz);
r = gl_customize_completion(est->sc_gl, NULL, complete);
assert(r == 0);
for (;;) {
lscf_get_selection_str(selfmri, sfsz - 2);
(void) strcat(selfmri, "> ");
est->sc_cmd_buf = gl_get_line(est->sc_gl, selfmri, NULL, -1);
if (est->sc_cmd_buf == NULL) {
switch (gl_return_status(est->sc_gl)) {
case GLR_SIGNAL:
gl_abandon_line(est->sc_gl);
continue;
case GLR_EOF:
break;
case GLR_ERROR:
uu_die(gettext("Error reading terminal: %s.\n"),
gl_error_message(est->sc_gl, NULL, 0));
/* NOTREACHED */
default:
#ifndef NDEBUG
(void) fprintf(stderr, "%s:%d: gl_get_line() "
"returned unexpected value %d.\n", __FILE__,
__LINE__, gl_return_status(est->sc_gl));
#endif
abort();
}
break;
}
parens = 0;
est->sc_cmd_bufsz = strlen(est->sc_cmd_buf);
est->sc_cmd_bufoff = 0;
est->sc_cmd_flags = SC_CMD_IACTIVE;
(void) yyparse();
}
free(selfmri);
est->sc_gl = del_GetLine(est->sc_gl); /* returns NULL */
#endif /* NATIVE_BUILD */
return (0);
}
int
engine_source(const char *name, boolean_t dont_exit)
{
engine_state_t *old = est;
struct stat st;
int ret;
est = uu_zalloc(sizeof (engine_state_t));
/* first, copy the stuff set up in engine_init */
est->sc_repo_pid = old->sc_repo_pid;
if (old->sc_repo_filename != NULL)
est->sc_repo_filename = safe_strdup(old->sc_repo_filename);
if (old->sc_repo_doordir != NULL)
est->sc_repo_doordir = safe_strdup(old->sc_repo_doordir);
if (old->sc_repo_doorname != NULL)
est->sc_repo_doorname = safe_strdup(old->sc_repo_doorname);
if (old->sc_repo_server != NULL)
est->sc_repo_server = safe_strdup(old->sc_repo_server);
/* set up the new guy */
est->sc_cmd_lineno = 1;
if (dont_exit)
est->sc_cmd_flags |= SC_CMD_DONT_EXIT;
if (strcmp(name, "-") == 0) {
est->sc_cmd_file = stdin;
est->sc_cmd_filename = "<stdin>";
} else {
errno = 0;
est->sc_cmd_filename = name;
est->sc_cmd_file = fopen(name, "r");
if (est->sc_cmd_file == NULL) {
if (errno == 0)
semerr(gettext("No free stdio streams.\n"));
else
semerr(gettext("Could not open %s"), name);
ret = -1;
goto fail;
}
do {
ret = fstat(fileno(est->sc_cmd_file), &st);
} while (ret != 0 && errno == EINTR);
if (ret != 0) {
(void) fclose(est->sc_cmd_file);
est->sc_cmd_file = NULL; /* for semerr() */
semerr(gettext("Could not stat %s"), name);
ret = -1;
goto fail;
}
if (!S_ISREG(st.st_mode)) {
(void) fclose(est->sc_cmd_file);
est->sc_cmd_file = NULL; /* for semerr() */
semerr(gettext("%s is not a regular file.\n"), name);
ret = -1;
goto fail;
}
}
(void) yyparse();
if (est->sc_cmd_file != stdin)
(void) fclose(est->sc_cmd_file);
ret = 0;
fail:
if (est->sc_repo_pid != old->sc_repo_pid)
lscf_cleanup(); /* clean up any new repository */
if (est->sc_repo_filename != NULL)
free((void *)est->sc_repo_filename);
if (est->sc_repo_doordir != NULL)
free((void *)est->sc_repo_doordir);
if (est->sc_repo_doorname != NULL)
free((void *)est->sc_repo_doorname);
if (est->sc_repo_server != NULL)
free((void *)est->sc_repo_server);
free(est);
est = old;
return (ret);
}
/*
* Initialize svccfg state. We recognize four environment variables:
*
* SVCCFG_REPOSITORY Create a private instance of svc.configd(1M) to answer
* requests for the specified repository file.
* SVCCFG_DOOR_PATH Directory for door creation.
*
* SVCCFG_DOOR Rendezvous via an alternative repository door.
*
* SVCCFG_CONFIGD_PATH Resolvable path to alternative svc.configd(1M) binary.
*/
void
engine_init()
{
const char *cp;
est = uu_zalloc(sizeof (engine_state_t));
est->sc_cmd_lineno = 1;
est->sc_repo_pid = -1;
cp = getenv("SVCCFG_REPOSITORY");
est->sc_repo_filename = cp ? safe_strdup(cp) : NULL;
cp = getenv("SVCCFG_DOOR_PATH");
est->sc_repo_doordir = cp ? cp : "/var/run";
cp = getenv("SVCCFG_DOOR");
if (cp != NULL) {
if (est->sc_repo_filename != NULL) {
uu_warn(gettext("SVCCFG_DOOR unused when "
"SVCCFG_REPOSITORY specified\n"));
} else {
est->sc_repo_doorname = safe_strdup(cp);
}
}
cp = getenv("SVCCFG_CONFIGD_PATH");
est->sc_repo_server = cp ? cp : "/lib/svc/bin/svc.configd";
est->sc_miss_type = B_FALSE;
est->sc_in_emi = 0;
cp = getenv("SMF_FMRI");
if ((cp != NULL) && (strcmp(cp, SCF_INSTANCE_EMI) == 0))
est->sc_in_emi = 1;
cp = smf_get_state(SCF_INSTANCE_FS_MINIMAL);
if (cp && (strcmp(cp, SCF_STATE_STRING_ONLINE) == 0))
est->sc_fs_minimal = B_TRUE;
free((void *) cp);
}
static int
import_manifest_file(manifest_info_t *info, boolean_t validate, FILE *pout,
uint_t flags)
{
bundle_t *b;
tmpl_errors_t *errs;
const char *file;
tmpl_validate_status_t vr;
file = info->mi_path;
/* Load the manifest */
b = internal_bundle_new();
if (lxml_get_bundle_file(b, file, SVCCFG_OP_IMPORT) != 0) {
internal_bundle_free(b);
return (-1);
}
/* Validate */
if ((vr = tmpl_validate_bundle(b, &errs)) != TVS_SUCCESS) {
char *prefix;
if ((validate == 0) || (vr == TVS_WARN)) {
prefix = gettext("Warning: ");
} else {
prefix = "";
}
tmpl_errors_print(stderr, errs, prefix);
if (validate && (vr != TVS_WARN)) {
tmpl_errors_destroy(errs);
semerr(gettext("Import of %s failed.\n"),
info->mi_path);
if (pout != NULL) {
(void) fprintf(pout, gettext("WARNING: svccfg "
"import of %s failed.\n"), info->mi_path);
}
return (-1);
}
}
tmpl_errors_destroy(errs);
/* Import */
if (lscf_bundle_import(b, file, flags) != 0) {
internal_bundle_free(b);
semerr(gettext("Import of %s failed.\n"), info->mi_path);
if (pout != NULL) {
(void) fprintf(pout, gettext("WARNING: svccfg import "
"of %s failed.\n"), info->mi_path);
}
return (-1);
}
internal_bundle_free(b);
if (info->mi_prop) {
char *errstr;
if (mhash_store_entry(g_hndl, info->mi_prop, file,
info->mi_hash, APPLY_NONE, &errstr)) {
if (errstr)
semerr(gettext("Could not store hash for %s. "
"%s\n"), info->mi_path, errstr);
else
semerr(gettext("Unknown error from "
"mhash_store_entry() for %s\n"),
info->mi_path);
}
}
return (0);
}
/*
* Return values:
* 1 No manifests need to be imported.
* 0 Success
* -1 Error
* -2 Syntax error
*/
int
engine_import(uu_list_t *args)
{
int argc, i, o;
int dont_exit;
int failed_manifests;
int total_manifests;
char *file;
char **argv = NULL;
string_list_t *slp;
boolean_t validate = B_FALSE;
uint_t flags = SCI_GENERALLAST;
int dirarg = 0;
int isdir;
int rc = -1;
struct stat sb;
char **paths;
manifest_info_t ***manifest_sets = NULL;
manifest_info_t **manifests;
char *progress_file = NULL;
FILE *progress_out = NULL;
int progress_count;
int back_count;
int count;
int fm_flags;
argc = uu_list_numnodes(args);
if (argc < 1)
return (-2);
argv = calloc(argc + 1, sizeof (char *));
if (argv == NULL)
uu_die(gettext("Out of memory.\n"));
for (slp = uu_list_first(args), i = 0;
slp != NULL;
slp = uu_list_next(args, slp), ++i)
argv[i] = slp->str;
argv[i] = NULL;
opterr = 0;
optind = 0; /* Remember, no argv[0]. */
for (;;) {
o = getopt(argc, argv, "np:V");
if (o == -1)
break;
switch (o) {
case 'n':
flags |= SCI_NOREFRESH;
break;
case 'p':
progress_file = optarg;
break;
case 'V':
validate = B_TRUE;
break;
case '?':
free(argv);
return (-2);
default:
bad_error("getopt", o);
}
}
argc -= optind;
if (argc < 1) {
free(argv);
return (-2);
}
/* Open device for progress messages */
if (progress_file != NULL) {
if (strcmp(progress_file, "-") == 0) {
progress_out = stdout;
} else {
progress_out = fopen(progress_file, "w");
if (progress_out == NULL) {
semerr(gettext("Unable to open %s for "
"progress reporting. %s\n"),
progress_file, strerror(errno));
goto out;
}
setbuf(progress_out, NULL);
}
}
paths = argv+optind;
manifest_sets = safe_malloc(argc * sizeof (*manifest_sets));
/* If we're in interactive mode, force strict validation. */
if (est->sc_cmd_flags & SC_CMD_IACTIVE)
validate = B_TRUE;
lscf_prep_hndl();
/* Determine which manifests must be imported. */
total_manifests = 0;
for (i = 0; i < argc; i++) {
file = *(paths + i);
fm_flags = CHECKHASH;
/* Determine if argument is a directory or file. */
if (stat(file, &sb) == -1) {
semerr(gettext("Unable to stat file %s. %s\n"), file,
strerror(errno));
goto out;
}
if (sb.st_mode & S_IFDIR) {
fm_flags |= CHECKEXT;
dirarg = 1;
isdir = 1;
} else if (sb.st_mode & S_IFREG) {
isdir = 0;
} else {
semerr(gettext("%s is not a directory or regular "
"file\n"), file);
goto out;
}
/* Get list of manifests that we should import for this path. */
if ((count = find_manifests(g_hndl, file, &manifests,
fm_flags)) < 0) {
if (isdir) {
semerr(gettext("Could not hash directory %s\n"),
file);
} else {
semerr(gettext("Could not hash file %s\n"),
file);
}
free_manifest_array(manifests);
goto out;
}
total_manifests += count;
manifest_sets[i] = manifests;
}
if (total_manifests == 0) {
/* No manifests to process. */
if (g_verbose) {
warn(gettext("No changes were necessary\n"));
}
rc = 1;
goto out;
}
/*
* If we're processing more than one file, we don't want to exit if
* we encounter an error. We should go ahead and process all of
* the manifests.
*/
dont_exit = est->sc_cmd_flags & SC_CMD_DONT_EXIT;
if (total_manifests > 1)
est->sc_cmd_flags |= SC_CMD_DONT_EXIT;
if (progress_out != NULL)
(void) fprintf(progress_out,
"Loading smf(5) service descriptions: ");
failed_manifests = 0;
progress_count = 0;
for (i = 0; i < argc; i++) {
manifests = manifest_sets[i];
if (manifests == NULL)
continue;
for (; *manifests != NULL; manifests++) {
progress_count++;
if (progress_out != NULL) {
back_count = fprintf(progress_out, "%d/%d",
progress_count, total_manifests);
while (back_count-- > 0) {
(void) fputc('\b', progress_out);
}
}
if (import_manifest_file(*manifests, validate,
progress_out, flags) != 0) {
failed_manifests++;
}
}
}
if (progress_out != NULL)
(void) fputc('\n', progress_out);
if ((total_manifests > 1) && (dont_exit == 0))
est->sc_cmd_flags &= ~SC_CMD_DONT_EXIT;
if (dirarg && total_manifests > 0) {
char *msg;
msg = "Loaded %d smf(5) service descriptions\n";
warn(gettext(msg), progress_count);
if (failed_manifests) {
msg = "%d smf(5) service descriptions failed to load\n";
warn(gettext(msg), failed_manifests);
}
}
if (failed_manifests > 0)
goto out;
if (g_verbose)
warn(gettext("Successful import.\n"));
rc = 0;
out:
if ((progress_out != NULL) && (progress_out != stdout))
(void) fclose(progress_out);
free(argv);
if (manifest_sets != NULL) {
for (i = 0; i < argc; i++) {
free_manifest_array(manifest_sets[i]);
}
free(manifest_sets);
}
return (rc);
}
/*
* Walk each service and get its manifest file.
*
* If the file exists check instance support, and cleanup any
* stale instances.
*
* If the file doesn't exist tear down the service and/or instances
* that are no longer supported by files.
*/
int
engine_cleanup(int flags)
{
boolean_t activity = B_TRUE;
int dont_exit;
int r = -1;
lscf_prep_hndl();
if (flags == 1) {
activity = B_FALSE;
}
dont_exit = est->sc_cmd_flags & SC_CMD_DONT_EXIT;
est->sc_cmd_flags |= SC_CMD_DONT_EXIT;
if (scf_walk_fmri(g_hndl, 0, NULL, SCF_WALK_SERVICE|SCF_WALK_NOINSTANCE,
lscf_service_cleanup, (void *)activity, NULL,
uu_warn) == SCF_SUCCESS)
r = 0;
if (dont_exit == 0)
est->sc_cmd_flags &= ~SC_CMD_DONT_EXIT;
(void) lscf_hash_cleanup();
return (r);
}
static int
apply_profile(manifest_info_t *info, int apply_changes)
{
bundle_t *b = internal_bundle_new();
if (lxml_get_bundle_file(b, info->mi_path, SVCCFG_OP_APPLY) != 0) {
internal_bundle_free(b);
return (-1);
}
if (!apply_changes) { /* we don't want to apply, just test */
internal_bundle_free(b);
return (0);
}
if (lscf_bundle_apply(b, info->mi_path) != 0) {
internal_bundle_free(b);
return (-1);
}
internal_bundle_free(b);
if (info->mi_prop) {
apply_action_t apply;
char *errstr;
apply = (est->sc_in_emi == 1) ? APPLY_LATE : APPLY_NONE;
if (mhash_store_entry(g_hndl, info->mi_prop, info->mi_path,
info->mi_hash, apply, &errstr)) {
semerr(errstr);
}
}
return (0);
}
int
engine_apply(const char *file, int apply_changes)
{
int rc = 0;
int isdir;
int dont_exit;
int profile_count;
int fm_flags;
struct stat sb;
manifest_info_t **profiles = NULL;
manifest_info_t **entry;
manifest_info_t *pfile;
lscf_prep_hndl();
/* Determine which profile(s) must be applied. */
profile_count = 0;
fm_flags = BUNDLE_PROF | CHECKHASH;
/* Determine if argument is a directory or file. */
if (stat(file, &sb) == -1) {
semerr(gettext("Unable to stat file %s. %s\n"), file,
strerror(errno));
rc = -1;
goto out;
}
if (sb.st_mode & S_IFDIR) {
fm_flags |= CHECKEXT;
isdir = 1;
} else if (sb.st_mode & S_IFREG) {
isdir = 0;
} else {
semerr(gettext("%s is not a directory or regular "
"file\n"), file);
rc = -1;
goto out;
}
/* Get list of profiles to be applied. */
if ((profile_count = find_manifests(g_hndl, file, &profiles,
fm_flags)) < 0) {
if (isdir) {
semerr(gettext("Could not hash directory %s\n"), file);
} else {
semerr(gettext("Could not hash file %s\n"), file);
}
rc = -1;
goto out;
}
if (profile_count == 0) {
/* No profiles to process. */
if (g_verbose) {
warn(gettext("No changes were necessary\n"));
}
goto out;
}
/*
* We don't want to exit if we encounter an error. We should go ahead
* and process all of the profiles.
*/
dont_exit = est->sc_cmd_flags & SC_CMD_DONT_EXIT;
est->sc_cmd_flags |= SC_CMD_DONT_EXIT;
for (entry = profiles; *entry != NULL; entry++) {
pfile = *entry;
if (apply_profile(pfile, apply_changes) == 0) {
if (g_verbose) {
warn(gettext("Successfully applied: %s\n"),
pfile->mi_path);
}
} else {
warn(gettext("WARNING: Failed to apply %s\n"),
pfile->mi_path);
rc = -1;
}
}
if (dont_exit == 0)
est->sc_cmd_flags &= ~SC_CMD_DONT_EXIT;
/* exit(1) appropriately if any profile failed to be applied. */
if ((rc == -1) &&
(est->sc_cmd_flags & (SC_CMD_IACTIVE | SC_CMD_DONT_EXIT)) == 0) {
free_manifest_array(profiles);
exit(1);
}
out:
free_manifest_array(profiles);
return (rc);
}
int
engine_restore(const char *file)
{
bundle_t *b;
lscf_prep_hndl();
b = internal_bundle_new();
if (lxml_get_bundle_file(b, file, SVCCFG_OP_RESTORE) != 0) {
internal_bundle_free(b);
return (-1);
}
if (lscf_bundle_import(b, file, SCI_NOSNAP) != 0) {
internal_bundle_free(b);
return (-1);
}
internal_bundle_free(b);
return (0);
}
int
engine_set(uu_list_t *args)
{
uu_list_walk_t *walk;
string_list_t *slp;
if (uu_list_first(args) == NULL) {
/* Display current options. */
if (!g_verbose)
(void) fputs("no", stdout);
(void) puts("verbose");
return (0);
}
walk = uu_list_walk_start(args, UU_DEFAULT);
if (walk == NULL)
uu_die(gettext("Couldn't read arguments"));
/* Use getopt? */
for (slp = uu_list_walk_next(walk);
slp != NULL;
slp = uu_list_walk_next(walk)) {
if (slp->str[0] == '-') {
char *op;
for (op = &slp->str[1]; *op != '\0'; ++op) {
switch (*op) {
case 'v':
g_verbose = 1;
break;
case 'V':
g_verbose = 0;
break;
default:
warn(gettext("Unknown option -%c.\n"),
*op);
}
}
} else {
warn(gettext("No non-flag arguments defined.\n"));
}
}
return (0);
}
void
help(int com)
{
int i;
if (com == 0) {
warn(gettext("General commands: help set repository end\n"
"Manifest commands: inventory validate import export "
"archive\n"
"Profile commands: apply extract\n"
"Entity commands: list select unselect add delete "
"describe\n"
"Snapshot commands: listsnap selectsnap revert\n"
"Instance commands: refresh\n"
"Property group commands: listpg addpg delpg\n"
"Property commands: listprop setprop delprop editprop\n"
"Property value commands: addpropvalue delpropvalue "
"setenv unsetenv\n"
"Notification parameters: "
"listnotify setnotify delnotify\n"));
return;
}
for (i = 0; help_messages[i].message != NULL; ++i) {
if (help_messages[i].token == com) {
warn(gettext("Usage: %s\n"),
gettext(help_messages[i].message));
return;
}
}
warn(gettext("Unknown command.\n"));
}