/*
* 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.
* Copyright 2015 Joyent, Inc.
* Copyright 2012 Milan Jurik. All rights reserved.
*/
#include <alloca.h>
#include <assert.h>
#include <ctype.h>
#include <door.h>
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <inttypes.h>
#include <libintl.h>
#include <libnvpair.h>
#include <libscf.h>
#include <libscf_priv.h>
#include <libtecla.h>
#include <libuutil.h>
#include <limits.h>
#include <locale.h>
#include <stdarg.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>
#include <wait.h>
#include <poll.h>
#include <libxml/tree.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include "svccfg.h"
#include "notify_params.h"
#include "manifest_hash.h"
#include "manifest_find.h"
/* The colon namespaces in each entity (each followed by a newline). */
#define COLON_NAMESPACES ":properties\n"
#define TEMP_FILE_PATTERN "/tmp/svccfg-XXXXXX"
/* These are characters which the lexer requires to be in double-quotes. */
#define CHARS_TO_QUOTE " \t\n\\>=\"()"
#define HASH_SIZE 16
#define HASH_PG_TYPE "framework"
#define HASH_PG_FLAGS 0
#define HASH_PROP "md5sum"
/*
* Indentation used in the output of the describe subcommand.
*/
#define TMPL_VALUE_INDENT " "
#define TMPL_INDENT " "
#define TMPL_INDENT_2X " "
#define TMPL_CHOICE_INDENT " "
/*
* Directory locations for manifests
*/
#define VARSVC_DIR "/var/svc/manifest"
#define LIBSVC_DIR "/lib/svc/manifest"
#define VARSVC_PR "var_svc_manifest"
#define LIBSVC_PR "lib_svc_manifest"
#define MFSTFILEPR "manifestfile"
#define SUPPORTPROP "support"
#define MFSTHISTFILE "/lib/svc/share/mfsthistory"
#define MFSTFILE_MAX 16
/*
* These are the classes of elements which may appear as children of service
* or instance elements in XML manifests.
*/
struct entity_elts {
xmlNodePtr create_default_instance;
xmlNodePtr single_instance;
xmlNodePtr restarter;
xmlNodePtr dependencies;
xmlNodePtr dependents;
xmlNodePtr method_context;
xmlNodePtr exec_methods;
xmlNodePtr notify_params;
xmlNodePtr property_groups;
xmlNodePtr instances;
xmlNodePtr stability;
xmlNodePtr template;
};
/*
* Likewise for property_group elements.
*/
struct pg_elts {
xmlNodePtr stability;
xmlNodePtr propvals;
xmlNodePtr properties;
};
/*
* Likewise for template elements.
*/
struct template_elts {
xmlNodePtr common_name;
xmlNodePtr description;
xmlNodePtr documentation;
};
/*
* Likewise for type (for notification parameters) elements.
*/
struct params_elts {
xmlNodePtr paramval;
xmlNodePtr parameter;
};
/*
* This structure is for snaplevel lists. They are convenient because libscf
* only allows traversing snaplevels in one direction.
*/
struct snaplevel {
uu_list_node_t list_node;
scf_snaplevel_t *sl;
};
/*
* This is used for communication between lscf_service_export and
* export_callback.
*/
struct export_args {
const char *filename;
int flags;
};
/*
* The service_manifest structure is used by the upgrade process
* to create a list of service to manifest linkages from the manifests
* in a set of given directories.
*/
typedef struct service_manifest {
const char *servicename;
uu_list_t *mfstlist;
size_t mfstlist_sz;
uu_avl_node_t svcmfst_node;
} service_manifest_t;
/*
* Structure to track the manifest file property group
* and the manifest file associated with that property
* group. Also, a flag to keep the access once it has
* been checked.
*/
struct mpg_mfile {
char *mpg;
char *mfile;
int access;
};
const char * const scf_pg_general = SCF_PG_GENERAL;
const char * const scf_group_framework = SCF_GROUP_FRAMEWORK;
const char * const scf_property_enabled = SCF_PROPERTY_ENABLED;
const char * const scf_property_external = "external";
const char * const snap_initial = "initial";
const char * const snap_lastimport = "last-import";
const char * const snap_previous = "previous";
const char * const snap_running = "running";
scf_handle_t *g_hndl = NULL; /* only valid after lscf_prep_hndl() */
ssize_t max_scf_fmri_len;
ssize_t max_scf_name_len;
ssize_t max_scf_pg_type_len;
ssize_t max_scf_value_len;
static size_t max_scf_len;
static scf_scope_t *cur_scope;
static scf_service_t *cur_svc = NULL;
static scf_instance_t *cur_inst = NULL;
static scf_snapshot_t *cur_snap = NULL;
static scf_snaplevel_t *cur_level = NULL;
static uu_list_pool_t *snaplevel_pool;
/* cur_levels is the snaplevels of cur_snap, from least specific to most. */
static uu_list_t *cur_levels;
static struct snaplevel *cur_elt; /* cur_elt->sl == cur_level */
static FILE *tempfile = NULL;
static char tempfilename[sizeof (TEMP_FILE_PATTERN)] = "";
static const char *emsg_entity_not_selected;
static const char *emsg_permission_denied;
static const char *emsg_create_xml;
static const char *emsg_cant_modify_snapshots;
static const char *emsg_invalid_for_snapshot;
static const char *emsg_read_only;
static const char *emsg_deleted;
static const char *emsg_invalid_pg_name;
static const char *emsg_invalid_prop_name;
static const char *emsg_no_such_pg;
static const char *emsg_fmri_invalid_pg_name;
static const char *emsg_fmri_invalid_pg_name_type;
static const char *emsg_pg_added;
static const char *emsg_pg_changed;
static const char *emsg_pg_deleted;
static const char *emsg_pg_mod_perm;
static const char *emsg_pg_add_perm;
static const char *emsg_pg_del_perm;
static const char *emsg_snap_perm;
static const char *emsg_dpt_dangling;
static const char *emsg_dpt_no_dep;
static int li_only = 0;
static int no_refresh = 0;
/* how long in ns we should wait between checks for a pg */
static uint64_t pg_timeout = 100 * (NANOSEC / MILLISEC);
/* import globals, to minimize allocations */
static scf_scope_t *imp_scope = NULL;
static scf_service_t *imp_svc = NULL, *imp_tsvc = NULL;
static scf_instance_t *imp_inst = NULL, *imp_tinst = NULL;
static scf_snapshot_t *imp_snap = NULL, *imp_lisnap = NULL, *imp_tlisnap = NULL;
static scf_snapshot_t *imp_rsnap = NULL;
static scf_snaplevel_t *imp_snpl = NULL, *imp_rsnpl = NULL;
static scf_propertygroup_t *imp_pg = NULL, *imp_pg2 = NULL;
static scf_property_t *imp_prop = NULL;
static scf_iter_t *imp_iter = NULL;
static scf_iter_t *imp_rpg_iter = NULL;
static scf_iter_t *imp_up_iter = NULL;
static scf_transaction_t *imp_tx = NULL; /* always reset this */
static char *imp_str = NULL;
static size_t imp_str_sz;
static char *imp_tsname = NULL;
static char *imp_fe1 = NULL; /* for fmri_equal() */
static char *imp_fe2 = NULL;
static uu_list_t *imp_deleted_dpts = NULL; /* pgroup_t's to refresh */
/* upgrade_dependents() globals */
static scf_instance_t *ud_inst = NULL;
static scf_snaplevel_t *ud_snpl = NULL;
static scf_propertygroup_t *ud_pg = NULL;
static scf_propertygroup_t *ud_cur_depts_pg = NULL;
static scf_propertygroup_t *ud_run_dpts_pg = NULL;
static int ud_run_dpts_pg_set = 0;
static scf_property_t *ud_prop = NULL;
static scf_property_t *ud_dpt_prop = NULL;
static scf_value_t *ud_val = NULL;
static scf_iter_t *ud_iter = NULL, *ud_iter2 = NULL;
static scf_transaction_t *ud_tx = NULL;
static char *ud_ctarg = NULL;
static char *ud_oldtarg = NULL;
static char *ud_name = NULL;
/* export globals */
static scf_instance_t *exp_inst;
static scf_propertygroup_t *exp_pg;
static scf_property_t *exp_prop;
static scf_value_t *exp_val;
static scf_iter_t *exp_inst_iter, *exp_pg_iter, *exp_prop_iter, *exp_val_iter;
static char *exp_str;
static size_t exp_str_sz;
/* cleanup globals */
static uu_avl_pool_t *service_manifest_pool = NULL;
static uu_avl_t *service_manifest_tree = NULL;
static void scfdie_lineno(int lineno) __NORETURN;
static char *start_method_names[] = {
"start",
"inetd_start",
NULL
};
static struct uri_scheme {
const char *scheme;
const char *protocol;
} uri_scheme[] = {
{ "mailto", "smtp" },
{ "snmp", "snmp" },
{ "syslog", "syslog" },
{ NULL, NULL }
};
#define URI_SCHEME_NUM ((sizeof (uri_scheme) / \
sizeof (struct uri_scheme)) - 1)
static int
check_uri_scheme(const char *scheme)
{
int i;
for (i = 0; uri_scheme[i].scheme != NULL; ++i) {
if (strcmp(scheme, uri_scheme[i].scheme) == 0)
return (i);
}
return (-1);
}
static int
check_uri_protocol(const char *p)
{
int i;
for (i = 0; uri_scheme[i].protocol != NULL; ++i) {
if (strcmp(p, uri_scheme[i].protocol) == 0)
return (i);
}
return (-1);
}
/*
* For unexpected libscf errors.
*/
#ifdef NDEBUG
static void scfdie(void) __NORETURN;
static void
scfdie(void)
{
scf_error_t err = scf_error();
if (err == SCF_ERROR_CONNECTION_BROKEN)
uu_die(gettext("Repository connection broken. Exiting.\n"));
uu_die(gettext("Unexpected fatal libscf error: %s. Exiting.\n"),
scf_strerror(err));
}
#else
#define scfdie() scfdie_lineno(__LINE__)
static void
scfdie_lineno(int lineno)
{
scf_error_t err = scf_error();
if (err == SCF_ERROR_CONNECTION_BROKEN)
uu_die(gettext("Repository connection broken. Exiting.\n"));
uu_die(gettext("Unexpected libscf error on line %d of " __FILE__
": %s.\n"), lineno, scf_strerror(err));
}
#endif
static void
scfwarn(void)
{
warn(gettext("Unexpected libscf error: %s.\n"),
scf_strerror(scf_error()));
}
/*
* Clear a field of a structure.
*/
static int
clear_int(void *a, void *b)
{
/* LINTED */
*(int *)((char *)a + (size_t)b) = 0;
return (UU_WALK_NEXT);
}
static int
scferror2errno(scf_error_t err)
{
switch (err) {
case SCF_ERROR_BACKEND_ACCESS:
return (EACCES);
case SCF_ERROR_BACKEND_READONLY:
return (EROFS);
case SCF_ERROR_CONNECTION_BROKEN:
return (ECONNABORTED);
case SCF_ERROR_CONSTRAINT_VIOLATED:
case SCF_ERROR_INVALID_ARGUMENT:
return (EINVAL);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_EXISTS:
return (EEXIST);
case SCF_ERROR_NO_MEMORY:
return (ENOMEM);
case SCF_ERROR_NO_RESOURCES:
return (ENOSPC);
case SCF_ERROR_NOT_FOUND:
return (ENOENT);
case SCF_ERROR_PERMISSION_DENIED:
return (EPERM);
default:
#ifndef NDEBUG
(void) fprintf(stderr, "%s:%d: Unknown libscf error %d.\n",
__FILE__, __LINE__, err);
#else
(void) fprintf(stderr, "Unknown libscf error %d.\n", err);
#endif
abort();
/* NOTREACHED */
}
}
static int
entity_get_pg(void *ent, int issvc, const char *name,
scf_propertygroup_t *pg)
{
if (issvc)
return (scf_service_get_pg(ent, name, pg));
else
return (scf_instance_get_pg(ent, name, pg));
}
static void
entity_destroy(void *ent, int issvc)
{
if (issvc)
scf_service_destroy(ent);
else
scf_instance_destroy(ent);
}
static int
get_pg(const char *pg_name, scf_propertygroup_t *pg)
{
int ret;
if (cur_level != NULL)
ret = scf_snaplevel_get_pg(cur_level, pg_name, pg);
else if (cur_inst != NULL)
ret = scf_instance_get_pg(cur_inst, pg_name, pg);
else
ret = scf_service_get_pg(cur_svc, pg_name, pg);
return (ret);
}
/*
* Find a snaplevel in a snapshot. If get_svc is true, find the service
* snaplevel. Otherwise find the instance snaplevel.
*
* Returns
* 0 - success
* ECONNABORTED - repository connection broken
* ECANCELED - instance containing snap was deleted
* ENOENT - snap has no snaplevels
* - requested snaplevel not found
*/
static int
get_snaplevel(scf_snapshot_t *snap, int get_svc, scf_snaplevel_t *snpl)
{
if (scf_snapshot_get_base_snaplevel(snap, snpl) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_DELETED:
case SCF_ERROR_NOT_FOUND:
return (scferror2errno(scf_error()));
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_snapshot_get_base_snaplevel",
scf_error());
}
}
for (;;) {
ssize_t ssz;
ssz = scf_snaplevel_get_instance_name(snpl, NULL, 0);
if (ssz >= 0) {
if (!get_svc)
return (0);
} else {
switch (scf_error()) {
case SCF_ERROR_CONSTRAINT_VIOLATED:
if (get_svc)
return (0);
break;
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
return (scferror2errno(scf_error()));
case SCF_ERROR_NOT_SET:
case SCF_ERROR_NOT_BOUND:
default:
bad_error("scf_snaplevel_get_instance_name",
scf_error());
}
}
if (scf_snaplevel_get_next_snaplevel(snpl, snpl) != 0) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_DELETED:
return (scferror2errno(scf_error()));
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
case SCF_ERROR_INVALID_ARGUMENT:
default:
bad_error("scf_snaplevel_get_next_snaplevel",
scf_error());
}
}
}
}
/*
* If issvc is 0, take ent to be a pointer to an scf_instance_t. If it has
* a running snapshot, and that snapshot has an instance snaplevel, set pg to
* the property group named name in it. If it doesn't have a running
* snapshot, set pg to the instance's current property group named name.
*
* If issvc is nonzero, take ent to be a pointer to an scf_service_t, and walk
* its instances. If one has a running snapshot with a service snaplevel, set
* pg to the property group named name in it. If no such snaplevel could be
* found, set pg to the service's current property group named name.
*
* iter, inst, snap, and snpl are required scratch objects.
*
* Returns
* 0 - success
* ECONNABORTED - repository connection broken
* ECANCELED - ent was deleted
* ENOENT - no such property group
* EINVAL - name is an invalid property group name
* EBADF - found running snapshot is missing a snaplevel
*/
static int
entity_get_running_pg(void *ent, int issvc, const char *name,
scf_propertygroup_t *pg, scf_iter_t *iter, scf_instance_t *inst,
scf_snapshot_t *snap, scf_snaplevel_t *snpl)
{
int r;
if (issvc) {
/* Search for an instance with a running snapshot. */
if (scf_iter_service_instances(iter, ent) != 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
return (scferror2errno(scf_error()));
case SCF_ERROR_NOT_SET:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
default:
bad_error("scf_iter_service_instances",
scf_error());
}
}
for (;;) {
r = scf_iter_next_instance(iter, inst);
if (r == 0) {
if (scf_service_get_pg(ent, name, pg) == 0)
return (0);
switch (scf_error()) {
case SCF_ERROR_DELETED:
case SCF_ERROR_NOT_FOUND:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_CONNECTION_BROKEN:
return (scferror2errno(scf_error()));
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_service_get_pg",
scf_error());
}
}
if (r != 1) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
return (scferror2errno(scf_error()));
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
default:
bad_error("scf_iter_next_instance",
scf_error());
}
}
if (scf_instance_get_snapshot(inst, snap_running,
snap) == 0)
break;
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
case SCF_ERROR_DELETED:
continue;
case SCF_ERROR_CONNECTION_BROKEN:
return (ECONNABORTED);
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
case SCF_ERROR_NOT_BOUND:
default:
bad_error("scf_instance_get_snapshot",
scf_error());
}
}
} else {
if (scf_instance_get_snapshot(ent, snap_running, snap) != 0) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
return (scferror2errno(scf_error()));
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_instance_get_snapshot",
scf_error());
}
if (scf_instance_get_pg(ent, name, pg) == 0)
return (0);
switch (scf_error()) {
case SCF_ERROR_DELETED:
case SCF_ERROR_NOT_FOUND:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_CONNECTION_BROKEN:
return (scferror2errno(scf_error()));
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_instance_get_pg", scf_error());
}
}
}
r = get_snaplevel(snap, issvc, snpl);
switch (r) {
case 0:
break;
case ECONNABORTED:
case ECANCELED:
return (r);
case ENOENT:
return (EBADF);
default:
bad_error("get_snaplevel", r);
}
if (scf_snaplevel_get_pg(snpl, name, pg) == 0)
return (0);
switch (scf_error()) {
case SCF_ERROR_DELETED:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_NOT_FOUND:
return (scferror2errno(scf_error()));
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_snaplevel_get_pg", scf_error());
/* NOTREACHED */
}
}
/*
* To be registered with atexit().
*/
static void
remove_tempfile(void)
{
int ret;
if (tempfile != NULL) {
if (fclose(tempfile) == EOF)
(void) warn(gettext("Could not close temporary file"));
tempfile = NULL;
}
if (tempfilename[0] != '\0') {
do {
ret = remove(tempfilename);
} while (ret == -1 && errno == EINTR);
if (ret == -1)
warn(gettext("Could not remove temporary file"));
tempfilename[0] = '\0';
}
}
/*
* Launch private svc.configd(1M) for manipulating alternate repositories.
*/
static void
start_private_repository(engine_state_t *est)
{
int fd, stat;
struct door_info info;
pid_t pid;
/*
* 1. Create a temporary file for the door.
*/
if (est->sc_repo_doorname != NULL)
free((void *)est->sc_repo_doorname);
est->sc_repo_doorname = tempnam(est->sc_repo_doordir, "scfdr");
if (est->sc_repo_doorname == NULL)
uu_die(gettext("Could not acquire temporary filename"));
fd = open(est->sc_repo_doorname, O_CREAT | O_EXCL | O_RDWR, 0600);
if (fd < 0)
uu_die(gettext("Could not create temporary file for "
"repository server"));
(void) close(fd);
/*
* 2. Launch a configd with that door, using the specified
* repository.
*/
if ((est->sc_repo_pid = fork()) == 0) {
(void) execlp(est->sc_repo_server, est->sc_repo_server, "-p",
"-d", est->sc_repo_doorname, "-r", est->sc_repo_filename,
NULL);
uu_die(gettext("Could not execute %s"), est->sc_repo_server);
} else if (est->sc_repo_pid == -1)
uu_die(gettext("Attempt to fork failed"));
do {
pid = waitpid(est->sc_repo_pid, &stat, 0);
} while (pid == -1 && errno == EINTR);
if (pid == -1)
uu_die(gettext("Could not waitpid() for repository server"));
if (!WIFEXITED(stat)) {
uu_die(gettext("Repository server failed (status %d).\n"),
stat);
} else if (WEXITSTATUS(stat) != 0) {
uu_die(gettext("Repository server failed (exit %d).\n"),
WEXITSTATUS(stat));
}
/*
* See if it was successful by checking if the door is a door.
*/
fd = open(est->sc_repo_doorname, O_RDWR);
if (fd < 0)
uu_die(gettext("Could not open door \"%s\""),
est->sc_repo_doorname);
if (door_info(fd, &info) < 0)
uu_die(gettext("Unexpected door_info() error"));
if (close(fd) == -1)
warn(gettext("Could not close repository door"),
strerror(errno));
est->sc_repo_pid = info.di_target;
}
void
lscf_cleanup(void)
{
/*
* In the case where we've launched a private svc.configd(1M)
* instance, we must terminate our child and remove the temporary
* rendezvous point.
*/
if (est->sc_repo_pid > 0) {
(void) kill(est->sc_repo_pid, SIGTERM);
(void) waitpid(est->sc_repo_pid, NULL, 0);
(void) unlink(est->sc_repo_doorname);
est->sc_repo_pid = 0;
}
}
void
unselect_cursnap(void)
{
void *cookie;
cur_level = NULL;
cookie = NULL;
while ((cur_elt = uu_list_teardown(cur_levels, &cookie)) != NULL) {
scf_snaplevel_destroy(cur_elt->sl);
free(cur_elt);
}
scf_snapshot_destroy(cur_snap);
cur_snap = NULL;
}
void
lscf_prep_hndl(void)
{
if (g_hndl != NULL)
return;
g_hndl = scf_handle_create(SCF_VERSION);
if (g_hndl == NULL)
scfdie();
if (est->sc_repo_filename != NULL)
start_private_repository(est);
if (est->sc_repo_doorname != NULL) {
scf_value_t *repo_value;
int ret;
repo_value = scf_value_create(g_hndl);
if (repo_value == NULL)
scfdie();
ret = scf_value_set_astring(repo_value, est->sc_repo_doorname);
assert(ret == SCF_SUCCESS);
if (scf_handle_decorate(g_hndl, "door_path", repo_value) !=
SCF_SUCCESS)
scfdie();
scf_value_destroy(repo_value);
}
if (scf_handle_bind(g_hndl) != 0)
uu_die(gettext("Could not connect to repository server: %s.\n"),
scf_strerror(scf_error()));
cur_scope = scf_scope_create(g_hndl);
if (cur_scope == NULL)
scfdie();
if (scf_handle_get_local_scope(g_hndl, cur_scope) != 0)
scfdie();
}
static void
repository_teardown(void)
{
if (g_hndl != NULL) {
if (cur_snap != NULL)
unselect_cursnap();
scf_instance_destroy(cur_inst);
scf_service_destroy(cur_svc);
scf_scope_destroy(cur_scope);
scf_handle_destroy(g_hndl);
cur_inst = NULL;
cur_svc = NULL;
cur_scope = NULL;
g_hndl = NULL;
lscf_cleanup();
}
}
void
lscf_set_repository(const char *repfile, int force)
{
repository_teardown();
if (est->sc_repo_filename != NULL) {
free((void *)est->sc_repo_filename);
est->sc_repo_filename = NULL;
}
if ((force == 0) && (access(repfile, R_OK) != 0)) {
/*
* Repository file does not exist
* or has no read permission.
*/
warn(gettext("Cannot access \"%s\": %s\n"),
repfile, strerror(errno));
} else {
est->sc_repo_filename = safe_strdup(repfile);
}
lscf_prep_hndl();
}
void
lscf_init()
{
if ((max_scf_fmri_len = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH)) < 0 ||
(max_scf_name_len = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH)) < 0 ||
(max_scf_pg_type_len = scf_limit(SCF_LIMIT_MAX_PG_TYPE_LENGTH)) <
0 ||
(max_scf_value_len = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH)) < 0)
scfdie();
max_scf_len = max_scf_fmri_len;
if (max_scf_name_len > max_scf_len)
max_scf_len = max_scf_name_len;
if (max_scf_pg_type_len > max_scf_len)
max_scf_len = max_scf_pg_type_len;
/*
* When a value of type opaque is represented as a string, the
* string contains 2 characters for every byte of data. That is
* because the string contains the hex representation of the opaque
* value.
*/
if (2 * max_scf_value_len > max_scf_len)
max_scf_len = 2 * max_scf_value_len;
if (atexit(remove_tempfile) != 0)
uu_die(gettext("Could not register atexit() function"));
emsg_entity_not_selected = gettext("An entity is not selected.\n");
emsg_permission_denied = gettext("Permission denied.\n");
emsg_create_xml = gettext("Could not create XML node.\n");
emsg_cant_modify_snapshots = gettext("Cannot modify snapshots.\n");
emsg_invalid_for_snapshot =
gettext("Invalid operation on a snapshot.\n");
emsg_read_only = gettext("Backend read-only.\n");
emsg_deleted = gettext("Current selection has been deleted.\n");
emsg_invalid_pg_name =
gettext("Invalid property group name \"%s\".\n");
emsg_invalid_prop_name = gettext("Invalid property name \"%s\".\n");
emsg_no_such_pg = gettext("No such property group \"%s\".\n");
emsg_fmri_invalid_pg_name = gettext("Service %s has property group "
"with invalid name \"%s\".\n");
emsg_fmri_invalid_pg_name_type = gettext("Service %s has property "
"group with invalid name \"%s\" or type \"%s\".\n");
emsg_pg_added = gettext("%s changed unexpectedly "
"(property group \"%s\" added).\n");
emsg_pg_changed = gettext("%s changed unexpectedly "
"(property group \"%s\" changed).\n");
emsg_pg_deleted = gettext("%s changed unexpectedly "
"(property group \"%s\" or an ancestor was deleted).\n");
emsg_pg_mod_perm = gettext("Could not modify property group \"%s\" "
"in %s (permission denied).\n");
emsg_pg_add_perm = gettext("Could not create property group \"%s\" "
"in %s (permission denied).\n");
emsg_pg_del_perm = gettext("Could not delete property group \"%s\" "
"in %s (permission denied).\n");
emsg_snap_perm = gettext("Could not take \"%s\" snapshot of %s "
"(permission denied).\n");
emsg_dpt_dangling = gettext("Conflict upgrading %s (not importing "
"new dependent \"%s\" because it already exists). Warning: The "
"current dependent's target (%s) does not exist.\n");
emsg_dpt_no_dep = gettext("Conflict upgrading %s (not importing new "
"dependent \"%s\" because it already exists). Warning: The "
"current dependent's target (%s) does not have a dependency named "
"\"%s\" as expected.\n");
string_pool = uu_list_pool_create("strings", sizeof (string_list_t),
offsetof(string_list_t, node), NULL, 0);
snaplevel_pool = uu_list_pool_create("snaplevels",
sizeof (struct snaplevel), offsetof(struct snaplevel, list_node),
NULL, 0);
}
static const char *
prop_to_typestr(const scf_property_t *prop)
{
scf_type_t ty;
if (scf_property_type(prop, &ty) != SCF_SUCCESS)
scfdie();
return (scf_type_to_string(ty));
}
static scf_type_t
string_to_type(const char *type)
{
size_t len = strlen(type);
char *buf;
if (len == 0 || type[len - 1] != ':')
return (SCF_TYPE_INVALID);
buf = (char *)alloca(len + 1);
(void) strlcpy(buf, type, len + 1);
buf[len - 1] = 0;
return (scf_string_to_type(buf));
}
static scf_value_t *
string_to_value(const char *str, scf_type_t ty, boolean_t require_quotes)
{
scf_value_t *v;
char *dup, *nstr;
size_t len;
v = scf_value_create(g_hndl);
if (v == NULL)
scfdie();
len = strlen(str);
if (require_quotes &&
(len < 2 || str[0] != '\"' || str[len - 1] != '\"')) {
semerr(gettext("Multiple string values or string values "
"with spaces must be quoted with '\"'.\n"));
scf_value_destroy(v);
return (NULL);
}
nstr = dup = safe_strdup(str);
if (dup[0] == '\"') {
/*
* Strip out the first and the last quote.
*/
dup[len - 1] = '\0';
nstr = dup + 1;
}
if (scf_value_set_from_string(v, ty, (const char *)nstr) != 0) {
assert(scf_error() == SCF_ERROR_INVALID_ARGUMENT);
semerr(gettext("Invalid \"%s\" value \"%s\".\n"),
scf_type_to_string(ty), nstr);
scf_value_destroy(v);
v = NULL;
}
free(dup);
return (v);
}
/*
* Print str to strm, quoting double-quotes and backslashes with backslashes.
* Optionally append a comment prefix ('#') to newlines ('\n').
*/
static int
quote_and_print(const char *str, FILE *strm, int commentnl)
{
const char *cp;
for (cp = str; *cp != '\0'; ++cp) {
if (*cp == '"' || *cp == '\\')
(void) putc('\\', strm);
(void) putc(*cp, strm);
if (commentnl && *cp == '\n') {
(void) putc('#', strm);
}
}
return (ferror(strm));
}
/*
* These wrappers around lowlevel functions provide consistent error checking
* and warnings.
*/
static int
pg_get_prop(scf_propertygroup_t *pg, const char *propname, scf_property_t *prop)
{
if (scf_pg_get_property(pg, propname, prop) == SCF_SUCCESS)
return (0);
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
if (g_verbose) {
ssize_t len;
char *fmri;
len = scf_pg_to_fmri(pg, NULL, 0);
if (len < 0)
scfdie();
fmri = safe_malloc(len + 1);
if (scf_pg_to_fmri(pg, fmri, len + 1) < 0)
scfdie();
warn(gettext("Expected property %s of property group %s is "
"missing.\n"), propname, fmri);
free(fmri);
}
return (-1);
}
static int
prop_check_type(scf_property_t *prop, scf_type_t ty)
{
scf_type_t pty;
if (scf_property_type(prop, &pty) != SCF_SUCCESS)
scfdie();
if (ty == pty)
return (0);
if (g_verbose) {
ssize_t len;
char *fmri;
const char *tystr;
len = scf_property_to_fmri(prop, NULL, 0);
if (len < 0)
scfdie();
fmri = safe_malloc(len + 1);
if (scf_property_to_fmri(prop, fmri, len + 1) < 0)
scfdie();
tystr = scf_type_to_string(ty);
if (tystr == NULL)
tystr = "?";
warn(gettext("Property %s is not of expected type %s.\n"),
fmri, tystr);
free(fmri);
}
return (-1);
}
static int
prop_get_val(scf_property_t *prop, scf_value_t *val)
{
scf_error_t err;
if (scf_property_get_value(prop, val) == SCF_SUCCESS)
return (0);
err = scf_error();
if (err != SCF_ERROR_NOT_FOUND &&
err != SCF_ERROR_CONSTRAINT_VIOLATED &&
err != SCF_ERROR_PERMISSION_DENIED)
scfdie();
if (g_verbose) {
ssize_t len;
char *fmri, *emsg;
len = scf_property_to_fmri(prop, NULL, 0);
if (len < 0)
scfdie();
fmri = safe_malloc(len + 1);
if (scf_property_to_fmri(prop, fmri, len + 1) < 0)
scfdie();
if (err == SCF_ERROR_NOT_FOUND)
emsg = gettext("Property %s has no values; expected "
"one.\n");
else if (err == SCF_ERROR_CONSTRAINT_VIOLATED)
emsg = gettext("Property %s has multiple values; "
"expected one.\n");
else
emsg = gettext("No permission to read property %s.\n");
warn(emsg, fmri);
free(fmri);
}
return (-1);
}
static boolean_t
snaplevel_is_instance(const scf_snaplevel_t *level)
{
if (scf_snaplevel_get_instance_name(level, NULL, 0) < 0) {
if (scf_error() != SCF_ERROR_CONSTRAINT_VIOLATED)
scfdie();
return (0);
} else {
return (1);
}
}
/*
* Decode FMRI into a service or instance, and put the result in *ep. If
* memory cannot be allocated, return SCF_ERROR_NO_MEMORY. If the FMRI is
* invalid, return SCF_ERROR_INVALID_ARGUMENT. If the FMRI does not specify
* an entity, return SCF_ERROR_CONSTRAINT_VIOLATED. If the entity cannot be
* found, return SCF_ERROR_NOT_FOUND. Otherwise return SCF_ERROR_NONE, point
* *ep to a valid scf_service_t or scf_instance_t, and set *isservice to
* whether *ep is a service.
*/
static scf_error_t
fmri_to_entity(scf_handle_t *h, const char *fmri, void **ep, int *isservice)
{
char *fmri_copy;
const char *sstr, *istr, *pgstr;
scf_service_t *svc;
scf_instance_t *inst;
fmri_copy = strdup(fmri);
if (fmri_copy == NULL)
return (SCF_ERROR_NO_MEMORY);
if (scf_parse_svc_fmri(fmri_copy, NULL, &sstr, &istr, &pgstr, NULL) !=
SCF_SUCCESS) {
free(fmri_copy);
return (SCF_ERROR_INVALID_ARGUMENT);
}
free(fmri_copy);
if (sstr == NULL || pgstr != NULL)
return (SCF_ERROR_CONSTRAINT_VIOLATED);
if (istr == NULL) {
svc = scf_service_create(h);
if (svc == NULL)
return (SCF_ERROR_NO_MEMORY);
if (scf_handle_decode_fmri(h, fmri, NULL, svc, NULL, NULL, NULL,
SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
return (SCF_ERROR_NOT_FOUND);
}
*ep = svc;
*isservice = 1;
} else {
inst = scf_instance_create(h);
if (inst == NULL)
return (SCF_ERROR_NO_MEMORY);
if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL,
NULL, SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
return (SCF_ERROR_NOT_FOUND);
}
*ep = inst;
*isservice = 0;
}
return (SCF_ERROR_NONE);
}
/*
* Create the entity named by fmri. Place a pointer to its libscf handle in
* *ep, and set or clear *isservicep if it is a service or an instance.
* Returns
* SCF_ERROR_NONE - success
* SCF_ERROR_NO_MEMORY - scf_*_create() failed
* SCF_ERROR_INVALID_ARGUMENT - fmri is invalid
* SCF_ERROR_CONSTRAINT_VIOLATED - fmri is not a service or instance
* SCF_ERROR_NOT_FOUND - no such scope
* SCF_ERROR_PERMISSION_DENIED
* SCF_ERROR_BACKEND_READONLY
* SCF_ERROR_BACKEND_ACCESS
*/
static scf_error_t
create_entity(scf_handle_t *h, const char *fmri, void **ep, int *isservicep)
{
char *fmri_copy;
const char *scstr, *sstr, *istr, *pgstr;
scf_scope_t *scope = NULL;
scf_service_t *svc = NULL;
scf_instance_t *inst = NULL;
scf_error_t scfe;
fmri_copy = safe_strdup(fmri);
if (scf_parse_svc_fmri(fmri_copy, &scstr, &sstr, &istr, &pgstr, NULL) !=
0) {
free(fmri_copy);
return (SCF_ERROR_INVALID_ARGUMENT);
}
if (scstr == NULL || sstr == NULL || pgstr != NULL) {
free(fmri_copy);
return (SCF_ERROR_CONSTRAINT_VIOLATED);
}
*ep = NULL;
if ((scope = scf_scope_create(h)) == NULL ||
(svc = scf_service_create(h)) == NULL ||
(inst = scf_instance_create(h)) == NULL) {
scfe = SCF_ERROR_NO_MEMORY;
goto out;
}
get_scope:
if (scf_handle_get_scope(h, scstr, scope) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
scfdie();
/* NOTREACHED */
case SCF_ERROR_NOT_FOUND:
scfe = SCF_ERROR_NOT_FOUND;
goto out;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_INVALID_ARGUMENT:
default:
bad_error("scf_handle_get_scope", scf_error());
}
}
get_svc:
if (scf_scope_get_service(scope, sstr, svc) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
scfdie();
/* NOTREACHED */
case SCF_ERROR_DELETED:
goto get_scope;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_scope_get_service", scf_error());
}
if (scf_scope_add_service(scope, sstr, svc) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
scfdie();
/* NOTREACHED */
case SCF_ERROR_DELETED:
goto get_scope;
case SCF_ERROR_PERMISSION_DENIED:
case SCF_ERROR_BACKEND_READONLY:
case SCF_ERROR_BACKEND_ACCESS:
scfe = scf_error();
goto out;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_scope_get_service", scf_error());
}
}
}
if (istr == NULL) {
scfe = SCF_ERROR_NONE;
*ep = svc;
*isservicep = 1;
goto out;
}
get_inst:
if (scf_service_get_instance(svc, istr, inst) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
scfdie();
/* NOTREACHED */
case SCF_ERROR_DELETED:
goto get_svc;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_service_get_instance", scf_error());
}
if (scf_service_add_instance(svc, istr, inst) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
scfdie();
/* NOTREACHED */
case SCF_ERROR_DELETED:
goto get_svc;
case SCF_ERROR_PERMISSION_DENIED:
case SCF_ERROR_BACKEND_READONLY:
case SCF_ERROR_BACKEND_ACCESS:
scfe = scf_error();
goto out;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_service_add_instance",
scf_error());
}
}
}
scfe = SCF_ERROR_NONE;
*ep = inst;
*isservicep = 0;
out:
if (*ep != inst)
scf_instance_destroy(inst);
if (*ep != svc)
scf_service_destroy(svc);
scf_scope_destroy(scope);
free(fmri_copy);
return (scfe);
}
/*
* Create or update a snapshot of inst. snap is a required scratch object.
*
* Returns
* 0 - success
* ECONNABORTED - repository connection broken
* EPERM - permission denied
* ENOSPC - configd is out of resources
* ECANCELED - inst was deleted
* -1 - unknown libscf error (message printed)
*/
static int
take_snap(scf_instance_t *inst, const char *name, scf_snapshot_t *snap)
{
again:
if (scf_instance_get_snapshot(inst, name, snap) == 0) {
if (_scf_snapshot_take_attach(inst, snap) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_PERMISSION_DENIED:
case SCF_ERROR_NO_RESOURCES:
return (scferror2errno(scf_error()));
case SCF_ERROR_NOT_SET:
case SCF_ERROR_INVALID_ARGUMENT:
default:
bad_error("_scf_snapshot_take_attach",
scf_error());
}
}
} else {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
return (scferror2errno(scf_error()));
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_instance_get_snapshot", scf_error());
}
if (_scf_snapshot_take_new(inst, name, snap) != 0) {
switch (scf_error()) {
case SCF_ERROR_EXISTS:
goto again;
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_NO_RESOURCES:
case SCF_ERROR_PERMISSION_DENIED:
return (scferror2errno(scf_error()));
default:
scfwarn();
return (-1);
case SCF_ERROR_NOT_SET:
case SCF_ERROR_INTERNAL:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
bad_error("_scf_snapshot_take_new",
scf_error());
}
}
}
return (0);
}
static int
refresh_running_snapshot(void *entity)
{
scf_snapshot_t *snap;
int r;
if ((snap = scf_snapshot_create(g_hndl)) == NULL)
scfdie();
r = take_snap(entity, snap_running, snap);
scf_snapshot_destroy(snap);
return (r);
}
/*
* Refresh entity. If isservice is zero, take entity to be an scf_instance_t *.
* Otherwise take entity to be an scf_service_t * and refresh all of its child
* instances. fmri is used for messages. inst, iter, and name_buf are used
* for scratch space. Returns
* 0 - success
* ECONNABORTED - repository connection broken
* ECANCELED - entity was deleted
* EACCES - backend denied access
* EPERM - permission denied
* ENOSPC - repository server out of resources
* -1 - _smf_refresh_instance_i() failed. scf_error() should be set.
*/
static int
refresh_entity(int isservice, void *entity, const char *fmri,
scf_instance_t *inst, scf_iter_t *iter, char *name_buf)
{
scf_error_t scfe;
int r;
if (!isservice) {
/*
* Let restarter handles refreshing and making new running
* snapshot only if operating on a live repository and not
* running in early import.
*/
if (est->sc_repo_filename == NULL &&
est->sc_repo_doorname == NULL &&
est->sc_in_emi == 0) {
if (_smf_refresh_instance_i(entity) == 0) {
if (g_verbose)
warn(gettext("Refreshed %s.\n"), fmri);
return (0);
}
switch (scf_error()) {
case SCF_ERROR_BACKEND_ACCESS:
return (EACCES);
case SCF_ERROR_PERMISSION_DENIED:
return (EPERM);
default:
return (-1);
}
} else {
r = refresh_running_snapshot(entity);
switch (r) {
case 0:
break;
case ECONNABORTED:
case ECANCELED:
case EPERM:
case ENOSPC:
break;
default:
bad_error("refresh_running_snapshot",
scf_error());
}
return (r);
}
}
if (scf_iter_service_instances(iter, entity) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_iter_service_instances", scf_error());
}
}
for (;;) {
r = scf_iter_next_instance(iter, inst);
if (r == 0)
break;
if (r != 1) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
case SCF_ERROR_INVALID_ARGUMENT:
default:
bad_error("scf_iter_next_instance",
scf_error());
}
}
/*
* Similarly, just take a new running snapshot if operating on
* a non-live repository or running during early import.
*/
if (est->sc_repo_filename != NULL ||
est->sc_repo_doorname != NULL ||
est->sc_in_emi == 1) {
r = refresh_running_snapshot(inst);
switch (r) {
case 0:
continue;
case ECONNABORTED:
case ECANCELED:
case EPERM:
case ENOSPC:
break;
default:
bad_error("refresh_running_snapshot",
scf_error());
}
return (r);
}
if (_smf_refresh_instance_i(inst) == 0) {
if (g_verbose) {
if (scf_instance_get_name(inst, name_buf,
max_scf_name_len + 1) < 0)
(void) strcpy(name_buf, "?");
warn(gettext("Refreshed %s:%s.\n"),
fmri, name_buf);
}
} else {
if (scf_error() != SCF_ERROR_BACKEND_ACCESS ||
g_verbose) {
scfe = scf_error();
if (scf_instance_to_fmri(inst, name_buf,
max_scf_name_len + 1) < 0)
(void) strcpy(name_buf, "?");
warn(gettext(
"Refresh of %s:%s failed: %s.\n"), fmri,
name_buf, scf_strerror(scfe));
}
}
}
return (0);
}
static void
private_refresh(void)
{
scf_instance_t *pinst = NULL;
scf_iter_t *piter = NULL;
ssize_t fmrilen;
size_t bufsz;
char *fmribuf;
void *ent;
int issvc;
int r;
if (est->sc_repo_filename == NULL && est->sc_repo_doorname == NULL)
return;
assert(cur_svc != NULL);
bufsz = max_scf_fmri_len + 1;
fmribuf = safe_malloc(bufsz);
if (cur_inst) {
issvc = 0;
ent = cur_inst;
fmrilen = scf_instance_to_fmri(ent, fmribuf, bufsz);
} else {
issvc = 1;
ent = cur_svc;
fmrilen = scf_service_to_fmri(ent, fmribuf, bufsz);
if ((pinst = scf_instance_create(g_hndl)) == NULL)
scfdie();
if ((piter = scf_iter_create(g_hndl)) == NULL)
scfdie();
}
if (fmrilen < 0) {
free(fmribuf);
if (scf_error() != SCF_ERROR_DELETED)
scfdie();
warn(emsg_deleted);
return;
}
assert(fmrilen < bufsz);
r = refresh_entity(issvc, ent, fmribuf, pinst, piter, NULL);
switch (r) {
case 0:
break;
case ECONNABORTED:
warn(gettext("Could not refresh %s "
"(repository connection broken).\n"), fmribuf);
break;
case ECANCELED:
warn(emsg_deleted);
break;
case EPERM:
warn(gettext("Could not refresh %s "
"(permission denied).\n"), fmribuf);
break;
case ENOSPC:
warn(gettext("Could not refresh %s "
"(repository server out of resources).\n"),
fmribuf);
break;
case EACCES:
default:
bad_error("refresh_entity", scf_error());
}
if (issvc) {
scf_instance_destroy(pinst);
scf_iter_destroy(piter);
}
free(fmribuf);
}
static int
stash_scferror_err(scf_callback_t *cbp, scf_error_t err)
{
cbp->sc_err = scferror2errno(err);
return (UU_WALK_ERROR);
}
static int
stash_scferror(scf_callback_t *cbp)
{
return (stash_scferror_err(cbp, scf_error()));
}
static int select_inst(const char *);
static int select_svc(const char *);
/*
* Take a property that does not have a type and check to see if a type
* exists or can be gleened from the current data. Set the type.
*
* Check the current level (instance) and then check the higher level
* (service). This could be the case for adding a new property to
* the instance that's going to "override" a service level property.
*
* For a property :
* 1. Take the type from an existing property
* 2. Take the type from a template entry
*
* If the type can not be found, then leave the type as is, and let the import
* report the problem of the missing type.
*/
static int
find_current_prop_type(void *p, void *g)
{
property_t *prop = p;
scf_callback_t *lcb = g;
pgroup_t *pg = NULL;
const char *fmri = NULL;
char *lfmri = NULL;
char *cur_selection = NULL;
scf_propertygroup_t *sc_pg = NULL;
scf_property_t *sc_prop = NULL;
scf_pg_tmpl_t *t_pg = NULL;
scf_prop_tmpl_t *t_prop = NULL;
scf_type_t prop_type;
value_t *vp;
int issvc = lcb->sc_service;
int r = UU_WALK_ERROR;
if (prop->sc_value_type != SCF_TYPE_INVALID)
return (UU_WALK_NEXT);
t_prop = scf_tmpl_prop_create(g_hndl);
sc_prop = scf_property_create(g_hndl);
if (sc_prop == NULL || t_prop == NULL) {
warn(gettext("Unable to create the property to attempt and "
"find a missing type.\n"));
scf_property_destroy(sc_prop);
scf_tmpl_prop_destroy(t_prop);
return (UU_WALK_ERROR);
}
if (lcb->sc_flags == 1) {
pg = lcb->sc_parent;
issvc = (pg->sc_parent->sc_etype == SVCCFG_SERVICE_OBJECT);
fmri = pg->sc_parent->sc_fmri;
retry_pg:
if (cur_svc && cur_selection == NULL) {
cur_selection = safe_malloc(max_scf_fmri_len + 1);
lscf_get_selection_str(cur_selection,
max_scf_fmri_len + 1);
if (strcmp(cur_selection, fmri) != 0) {
lscf_select(fmri);
} else {
free(cur_selection);
cur_selection = NULL;
}
} else {
lscf_select(fmri);
}
if (sc_pg == NULL && (sc_pg = scf_pg_create(g_hndl)) == NULL) {
warn(gettext("Unable to create property group to "
"find a missing property type.\n"));
goto out;
}
if (get_pg(pg->sc_pgroup_name, sc_pg) != SCF_SUCCESS) {
/*
* If this is the sc_pg from the parent
* let the caller clean up the sc_pg,
* and just throw it away in this case.
*/
if (sc_pg != lcb->sc_parent)
scf_pg_destroy(sc_pg);
sc_pg = NULL;
if ((t_pg = scf_tmpl_pg_create(g_hndl)) == NULL) {
warn(gettext("Unable to create template "
"property group to find a property "
"type.\n"));
goto out;
}
if (scf_tmpl_get_by_pg_name(fmri, NULL,
pg->sc_pgroup_name, NULL, t_pg,
SCF_PG_TMPL_FLAG_EXACT) != SCF_SUCCESS) {
/*
* if instance get service and jump back
*/
scf_tmpl_pg_destroy(t_pg);
t_pg = NULL;
if (issvc == 0) {
entity_t *e = pg->sc_parent->sc_parent;
fmri = e->sc_fmri;
issvc = 1;
goto retry_pg;
} else {
goto out;
}
}
}
} else {
sc_pg = lcb->sc_parent;
}
/*
* Attempt to get the type from an existing property. If the property
* cannot be found then attempt to get the type from a template entry
* for the property.
*
* Finally, if at the instance level look at the service level.
*/
if (sc_pg != NULL &&
pg_get_prop(sc_pg, prop->sc_property_name,
sc_prop) == SCF_SUCCESS &&
scf_property_type(sc_prop, &prop_type) == SCF_SUCCESS) {
prop->sc_value_type = prop_type;
/*
* Found a type, update the value types and validate
* the actual value against this type.
*/
for (vp = uu_list_first(prop->sc_property_values);
vp != NULL;
vp = uu_list_next(prop->sc_property_values, vp)) {
vp->sc_type = prop->sc_value_type;
lxml_store_value(vp, 0, NULL);
}
r = UU_WALK_NEXT;
goto out;
}
/*
* If we get here with t_pg set to NULL then we had to have
* gotten an sc_pg but that sc_pg did not have the property
* we are looking for. So if the t_pg is not null look up
* the template entry for the property.
*
* If the t_pg is null then need to attempt to get a matching
* template entry for the sc_pg, and see if there is a property
* entry for that template entry.
*/
do_tmpl :
if (t_pg != NULL &&
scf_tmpl_get_by_prop(t_pg, prop->sc_property_name,
t_prop, 0) == SCF_SUCCESS) {
if (scf_tmpl_prop_type(t_prop, &prop_type) == SCF_SUCCESS) {
prop->sc_value_type = prop_type;
/*
* Found a type, update the value types and validate
* the actual value against this type.
*/
for (vp = uu_list_first(prop->sc_property_values);
vp != NULL;
vp = uu_list_next(prop->sc_property_values, vp)) {
vp->sc_type = prop->sc_value_type;
lxml_store_value(vp, 0, NULL);
}
r = UU_WALK_NEXT;
goto out;
}
} else {
if (t_pg == NULL && sc_pg) {
if ((t_pg = scf_tmpl_pg_create(g_hndl)) == NULL) {
warn(gettext("Unable to create template "
"property group to find a property "
"type.\n"));
goto out;
}
if (scf_tmpl_get_by_pg(sc_pg, t_pg, 0) != SCF_SUCCESS) {
scf_tmpl_pg_destroy(t_pg);
t_pg = NULL;
} else {
goto do_tmpl;
}
}
}
if (issvc == 0) {
scf_instance_t *i;
scf_service_t *s;
issvc = 1;
if (lcb->sc_flags == 1) {
entity_t *e = pg->sc_parent->sc_parent;
fmri = e->sc_fmri;
goto retry_pg;
}
/*
* because lcb->sc_flags was not set then this means
* the pg was not used and can be used here.
*/
if ((pg = internal_pgroup_new()) == NULL) {
warn(gettext("Could not create internal property group "
"to find a missing type."));
goto out;
}
pg->sc_pgroup_name = safe_malloc(max_scf_name_len + 1);
if (scf_pg_get_name(sc_pg, (char *)pg->sc_pgroup_name,
max_scf_name_len + 1) < 0)
goto out;
i = scf_instance_create(g_hndl);
s = scf_service_create(g_hndl);
if (i == NULL || s == NULL ||
scf_pg_get_parent_instance(sc_pg, i) != SCF_SUCCESS) {
warn(gettext("Could not get a service for the instance "
"to find a missing type."));
goto out;
}
/*
* Check to see truly at the instance level.
*/
lfmri = safe_malloc(max_scf_fmri_len + 1);
if (scf_instance_get_parent(i, s) == SCF_SUCCESS &&
scf_service_to_fmri(s, lfmri, max_scf_fmri_len + 1) < 0)
goto out;
else
fmri = (const char *)lfmri;
goto retry_pg;
}
out :
if (sc_pg != lcb->sc_parent) {
scf_pg_destroy(sc_pg);
}
/*
* If this is true then the pg was allocated
* here, and the name was set so need to free
* the name and the pg.
*/
if (pg != NULL && pg != lcb->sc_parent) {
free((char *)pg->sc_pgroup_name);
internal_pgroup_free(pg);
}
if (cur_selection) {
lscf_select(cur_selection);
free(cur_selection);
}
scf_tmpl_pg_destroy(t_pg);
scf_tmpl_prop_destroy(t_prop);
scf_property_destroy(sc_prop);
if (r != UU_WALK_NEXT)
warn(gettext("Could not find property type for \"%s\" "
"from \"%s\"\n"), prop->sc_property_name,
fmri != NULL ? fmri : lcb->sc_source_fmri);
free(lfmri);
return (r);
}
/*
* Take a property group that does not have a type and check to see if a type
* exists or can be gleened from the current data. Set the type.
*
* Check the current level (instance) and then check the higher level
* (service). This could be the case for adding a new property to
* the instance that's going to "override" a service level property.
*
* For a property group
* 1. Take the type from an existing property group
* 2. Take the type from a template entry
*
* If the type can not be found, then leave the type as is, and let the import
* report the problem of the missing type.
*/
static int
find_current_pg_type(void *p, void *sori)
{
entity_t *si = sori;
pgroup_t *pg = p;
const char *ofmri, *fmri;
char *cur_selection = NULL;
char *pg_type = NULL;
scf_propertygroup_t *sc_pg = NULL;
scf_pg_tmpl_t *t_pg = NULL;
int issvc = (si->sc_etype == SVCCFG_SERVICE_OBJECT);
int r = UU_WALK_ERROR;
ofmri = fmri = si->sc_fmri;
if (pg->sc_pgroup_type != NULL) {
r = UU_WALK_NEXT;
goto out;
}
sc_pg = scf_pg_create(g_hndl);
if (sc_pg == NULL) {
warn(gettext("Unable to create property group to attempt "
"and find a missing type.\n"));
return (UU_WALK_ERROR);
}
/*
* Using get_pg() requires that the cur_svc/cur_inst be
* via lscf_select. Need to preserve the current selection
* if going to use lscf_select() to set up the cur_svc/cur_inst
*/
if (cur_svc) {
cur_selection = safe_malloc(max_scf_fmri_len + 1);
lscf_get_selection_str(cur_selection, max_scf_fmri_len + 1);
}
/*
* If the property group exists get the type, and set
* the pgroup_t type of that type.
*
* If not the check for a template pg_pattern entry
* and take the type from that.
*/
retry_svc:
lscf_select(fmri);
if (get_pg(pg->sc_pgroup_name, sc_pg) == SCF_SUCCESS) {
pg_type = safe_malloc(max_scf_pg_type_len + 1);
if (pg_type != NULL && scf_pg_get_type(sc_pg, pg_type,
max_scf_pg_type_len + 1) != -1) {
pg->sc_pgroup_type = pg_type;
r = UU_WALK_NEXT;
goto out;
} else {
free(pg_type);
}
} else {
if ((t_pg == NULL) &&
(t_pg = scf_tmpl_pg_create(g_hndl)) == NULL)
goto out;
if (scf_tmpl_get_by_pg_name(fmri, NULL, pg->sc_pgroup_name,
NULL, t_pg, SCF_PG_TMPL_FLAG_EXACT) == SCF_SUCCESS &&
scf_tmpl_pg_type(t_pg, &pg_type) != -1) {
pg->sc_pgroup_type = pg_type;
r = UU_WALK_NEXT;
goto out;
}
}
/*
* If type is not found at the instance level then attempt to
* find the type at the service level.
*/
if (!issvc) {
si = si->sc_parent;
fmri = si->sc_fmri;
issvc = (si->sc_etype == SVCCFG_SERVICE_OBJECT);
goto retry_svc;
}
out :
if (cur_selection) {
lscf_select(cur_selection);
free(cur_selection);
}
/*
* Now walk the properties of the property group to make sure that
* all properties have the correct type and values are valid for
* those types.
*/
if (r == UU_WALK_NEXT) {
scf_callback_t cb;
cb.sc_service = issvc;
cb.sc_source_fmri = ofmri;
if (sc_pg != NULL) {
cb.sc_parent = sc_pg;
cb.sc_flags = 0;
} else {
cb.sc_parent = pg;
cb.sc_flags = 1;
}
if (uu_list_walk(pg->sc_pgroup_props, find_current_prop_type,
&cb, UU_DEFAULT) != 0) {
if (uu_error() != UU_ERROR_CALLBACK_FAILED)
bad_error("uu_list_walk", uu_error());
r = UU_WALK_ERROR;
}
} else {
warn(gettext("Could not find property group type for "
"\"%s\" from \"%s\"\n"), pg->sc_pgroup_name, fmri);
}
scf_tmpl_pg_destroy(t_pg);
scf_pg_destroy(sc_pg);
return (r);
}
/*
* Import. These functions import a bundle into the repository.
*/
/*
* Add a transaction entry to lcbdata->sc_trans for this property_t. Uses
* sc_handle, sc_trans, and sc_flags (SCI_NOENABLED) in lcbdata. On success,
* returns UU_WALK_NEXT. On error returns UU_WALK_ERROR and sets
* lcbdata->sc_err to
* ENOMEM - out of memory
* ECONNABORTED - repository connection broken
* ECANCELED - sc_trans's property group was deleted
* EINVAL - p's name is invalid (error printed)
* - p has an invalid value (error printed)
*/
static int
lscf_property_import(void *v, void *pvt)
{
property_t *p = v;
scf_callback_t *lcbdata = pvt;
value_t *vp;
scf_transaction_t *trans = lcbdata->sc_trans;
scf_transaction_entry_t *entr;
scf_value_t *val;
scf_type_t tp;
if ((lcbdata->sc_flags & SCI_NOENABLED ||
lcbdata->sc_flags & SCI_DELAYENABLE) &&
strcmp(p->sc_property_name, SCF_PROPERTY_ENABLED) == 0) {
lcbdata->sc_enable = p;
return (UU_WALK_NEXT);
}
entr = scf_entry_create(lcbdata->sc_handle);
if (entr == NULL) {
switch (scf_error()) {
case SCF_ERROR_NO_MEMORY:
return (stash_scferror(lcbdata));
case SCF_ERROR_INVALID_ARGUMENT:
default:
bad_error("scf_entry_create", scf_error());
}
}
tp = p->sc_value_type;
if (scf_transaction_property_new(trans, entr,
p->sc_property_name, tp) != 0) {
switch (scf_error()) {
case SCF_ERROR_INVALID_ARGUMENT:
semerr(emsg_invalid_prop_name, p->sc_property_name);
scf_entry_destroy(entr);
return (stash_scferror(lcbdata));
case SCF_ERROR_EXISTS:
break;
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
scf_entry_destroy(entr);
return (stash_scferror(lcbdata));
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_transaction_property_new", scf_error());
}
if (scf_transaction_property_change_type(trans, entr,
p->sc_property_name, tp) != 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
scf_entry_destroy(entr);
return (stash_scferror(lcbdata));
case SCF_ERROR_INVALID_ARGUMENT:
semerr(emsg_invalid_prop_name,
p->sc_property_name);
scf_entry_destroy(entr);
return (stash_scferror(lcbdata));
case SCF_ERROR_NOT_FOUND:
case SCF_ERROR_NOT_SET:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
default:
bad_error(
"scf_transaction_property_change_type",
scf_error());
}
}
}
for (vp = uu_list_first(p->sc_property_values);
vp != NULL;
vp = uu_list_next(p->sc_property_values, vp)) {
val = scf_value_create(g_hndl);
if (val == NULL) {
switch (scf_error()) {
case SCF_ERROR_NO_MEMORY:
return (stash_scferror(lcbdata));
case SCF_ERROR_INVALID_ARGUMENT:
default:
bad_error("scf_value_create", scf_error());
}
}
switch (tp) {
case SCF_TYPE_BOOLEAN:
scf_value_set_boolean(val, vp->sc_u.sc_count);
break;
case SCF_TYPE_COUNT:
scf_value_set_count(val, vp->sc_u.sc_count);
break;
case SCF_TYPE_INTEGER:
scf_value_set_integer(val, vp->sc_u.sc_integer);
break;
default:
assert(vp->sc_u.sc_string != NULL);
if (scf_value_set_from_string(val, tp,
vp->sc_u.sc_string) != 0) {
if (scf_error() != SCF_ERROR_INVALID_ARGUMENT)
bad_error("scf_value_set_from_string",
scf_error());
warn(gettext("Value \"%s\" is not a valid "
"%s.\n"), vp->sc_u.sc_string,
scf_type_to_string(tp));
scf_value_destroy(val);
return (stash_scferror(lcbdata));
}
break;
}
if (scf_entry_add_value(entr, val) != 0)
bad_error("scf_entry_add_value", scf_error());
}
return (UU_WALK_NEXT);
}
/*
* Import a pgroup_t into the repository. Uses sc_handle, sc_parent,
* sc_service, sc_flags (SCI_GENERALLAST, SCI_FORCE, & SCI_KEEP),
* sc_source_fmri, and sc_target_fmri in lcbdata, and uses imp_pg and imp_tx.
* On success, returns UU_WALK_NEXT. On error returns UU_WALK_ERROR and sets
* lcbdata->sc_err to
* ECONNABORTED - repository connection broken
* ENOMEM - out of memory
* ENOSPC - svc.configd is out of resources
* ECANCELED - sc_parent was deleted
* EPERM - could not create property group (permission denied) (error printed)
* - could not modify property group (permission denied) (error printed)
* - could not delete property group (permission denied) (error printed)
* EROFS - could not create property group (repository is read-only)
* - could not delete property group (repository is read-only)
* EACCES - could not create property group (backend access denied)
* - could not delete property group (backend access denied)
* EEXIST - could not create property group (already exists)
* EINVAL - invalid property group name (error printed)
* - invalid property name (error printed)
* - invalid value (error printed)
* EBUSY - new property group deleted (error printed)
* - new property group changed (error printed)
* - property group added (error printed)
* - property group deleted (error printed)
*/
static int
entity_pgroup_import(void *v, void *pvt)
{
pgroup_t *p = v;
scf_callback_t cbdata;
scf_callback_t *lcbdata = pvt;
void *ent = lcbdata->sc_parent;
int issvc = lcbdata->sc_service;
int r;
const char * const pg_changed = gettext("%s changed unexpectedly "
"(new property group \"%s\" changed).\n");
/* Never import deleted property groups. */
if (p->sc_pgroup_delete) {
if ((lcbdata->sc_flags & SCI_OP_APPLY) == SCI_OP_APPLY &&
entity_get_pg(ent, issvc, p->sc_pgroup_name, imp_pg) == 0) {
goto delete_pg;
}
return (UU_WALK_NEXT);
}
if (!issvc && (lcbdata->sc_flags & SCI_GENERALLAST) &&
strcmp(p->sc_pgroup_name, SCF_PG_GENERAL) == 0) {
lcbdata->sc_general = p;
return (UU_WALK_NEXT);
}
add_pg:
if (issvc)
r = scf_service_add_pg(ent, p->sc_pgroup_name,
p->sc_pgroup_type, p->sc_pgroup_flags, imp_pg);
else
r = scf_instance_add_pg(ent, p->sc_pgroup_name,
p->sc_pgroup_type, p->sc_pgroup_flags, imp_pg);
if (r != 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_BACKEND_READONLY:
case SCF_ERROR_BACKEND_ACCESS:
case SCF_ERROR_NO_RESOURCES:
return (stash_scferror(lcbdata));
case SCF_ERROR_EXISTS:
if (lcbdata->sc_flags & SCI_FORCE)
break;
return (stash_scferror(lcbdata));
case SCF_ERROR_INVALID_ARGUMENT:
warn(emsg_fmri_invalid_pg_name_type,
lcbdata->sc_source_fmri,
p->sc_pgroup_name, p->sc_pgroup_type);
return (stash_scferror(lcbdata));
case SCF_ERROR_PERMISSION_DENIED:
warn(emsg_pg_add_perm, p->sc_pgroup_name,
lcbdata->sc_target_fmri);
return (stash_scferror(lcbdata));
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_service_add_pg", scf_error());
}
if (entity_get_pg(ent, issvc, p->sc_pgroup_name, imp_pg) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_DELETED:
return (stash_scferror(lcbdata));
case SCF_ERROR_INVALID_ARGUMENT:
warn(emsg_fmri_invalid_pg_name,
lcbdata->sc_source_fmri,
p->sc_pgroup_name);
return (stash_scferror(lcbdata));
case SCF_ERROR_NOT_FOUND:
warn(emsg_pg_deleted, lcbdata->sc_target_fmri,
p->sc_pgroup_name);
lcbdata->sc_err = EBUSY;
return (UU_WALK_ERROR);
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_SET:
default:
bad_error("entity_get_pg", scf_error());
}
}
if (lcbdata->sc_flags & SCI_KEEP)
goto props;
delete_pg:
if (scf_pg_delete(imp_pg) != 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
warn(emsg_pg_deleted, lcbdata->sc_target_fmri,
p->sc_pgroup_name);
lcbdata->sc_err = EBUSY;
return (UU_WALK_ERROR);
case SCF_ERROR_PERMISSION_DENIED:
warn(emsg_pg_del_perm, p->sc_pgroup_name,
lcbdata->sc_target_fmri);
return (stash_scferror(lcbdata));
case SCF_ERROR_BACKEND_READONLY:
case SCF_ERROR_BACKEND_ACCESS:
case SCF_ERROR_CONNECTION_BROKEN:
return (stash_scferror(lcbdata));
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_pg_delete", scf_error());
}
}
if (p->sc_pgroup_delete)
return (UU_WALK_NEXT);
goto add_pg;
}
props:
/*
* Add properties to property group, if any.
*/
cbdata.sc_handle = lcbdata->sc_handle;
cbdata.sc_parent = imp_pg;
cbdata.sc_flags = lcbdata->sc_flags;
cbdata.sc_trans = imp_tx;
cbdata.sc_enable = NULL;
if (scf_transaction_start(imp_tx, imp_pg) != 0) {
switch (scf_error()) {
case SCF_ERROR_BACKEND_ACCESS:
case SCF_ERROR_BACKEND_READONLY:
case SCF_ERROR_CONNECTION_BROKEN:
return (stash_scferror(lcbdata));
case SCF_ERROR_DELETED:
warn(pg_changed, lcbdata->sc_target_fmri,
p->sc_pgroup_name);
lcbdata->sc_err = EBUSY;
return (UU_WALK_ERROR);
case SCF_ERROR_PERMISSION_DENIED:
warn(emsg_pg_mod_perm, p->sc_pgroup_name,
lcbdata->sc_target_fmri);
return (stash_scferror(lcbdata));
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
case SCF_ERROR_IN_USE:
case SCF_ERROR_HANDLE_MISMATCH:
default:
bad_error("scf_transaction_start", scf_error());
}
}
if (uu_list_walk(p->sc_pgroup_props, lscf_property_import, &cbdata,
UU_DEFAULT) != 0) {
if (uu_error() != UU_ERROR_CALLBACK_FAILED)
bad_error("uu_list_walk", uu_error());
scf_transaction_reset(imp_tx);
lcbdata->sc_err = cbdata.sc_err;
if (cbdata.sc_err == ECANCELED) {
warn(pg_changed, lcbdata->sc_target_fmri,
p->sc_pgroup_name);
lcbdata->sc_err = EBUSY;
}
return (UU_WALK_ERROR);
}
if ((lcbdata->sc_flags & SCI_DELAYENABLE) && cbdata.sc_enable) {
cbdata.sc_flags = cbdata.sc_flags & (~SCI_DELAYENABLE);
/*
* take the snapshot running snapshot then
* import the stored general/enable property
*/
r = take_snap(ent, snap_running, imp_rsnap);
switch (r) {
case 0:
break;
case ECONNABORTED:
warn(gettext("Could not take %s snapshot on import "
"(repository connection broken).\n"),
snap_running);
lcbdata->sc_err = r;
return (UU_WALK_ERROR);
case ECANCELED:
warn(emsg_deleted);
lcbdata->sc_err = r;
return (UU_WALK_ERROR);
case EPERM:
warn(gettext("Could not take %s snapshot "
"(permission denied).\n"), snap_running);
lcbdata->sc_err = r;
return (UU_WALK_ERROR);
case ENOSPC:
warn(gettext("Could not take %s snapshot"
"(repository server out of resources).\n"),
snap_running);
lcbdata->sc_err = r;
return (UU_WALK_ERROR);
default:
bad_error("take_snap", r);
}
r = lscf_property_import(cbdata.sc_enable, &cbdata);
if (r != UU_WALK_NEXT) {
if (r != UU_WALK_ERROR)
bad_error("lscf_property_import", r);
return (EINVAL);
}
}
r = scf_transaction_commit(imp_tx);
switch (r) {
case 1:
r = UU_WALK_NEXT;
break;
case 0:
warn(pg_changed, lcbdata->sc_target_fmri, p->sc_pgroup_name);
lcbdata->sc_err = EBUSY;
r = UU_WALK_ERROR;
break;
case -1:
switch (scf_error()) {
case SCF_ERROR_BACKEND_READONLY:
case SCF_ERROR_BACKEND_ACCESS:
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_NO_RESOURCES:
r = stash_scferror(lcbdata);
break;
case SCF_ERROR_DELETED:
warn(emsg_pg_deleted, lcbdata->sc_target_fmri,
p->sc_pgroup_name);
lcbdata->sc_err = EBUSY;
r = UU_WALK_ERROR;
break;
case SCF_ERROR_PERMISSION_DENIED:
warn(emsg_pg_mod_perm, p->sc_pgroup_name,
lcbdata->sc_target_fmri);
r = stash_scferror(lcbdata);
break;
case SCF_ERROR_NOT_SET:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_BOUND:
default:
bad_error("scf_transaction_commit", scf_error());
}
break;
default:
bad_error("scf_transaction_commit", r);
}
scf_transaction_destroy_children(imp_tx);
return (r);
}
/*
* Returns
* 0 - success
* ECONNABORTED - repository connection broken
* ENOMEM - out of memory
* ENOSPC - svc.configd is out of resources
* ECANCELED - inst was deleted
* EPERM - could not create property group (permission denied) (error printed)
* - could not modify property group (permission denied) (error printed)
* EROFS - could not create property group (repository is read-only)
* EACCES - could not create property group (backend access denied)
* EEXIST - could not create property group (already exists)
* EINVAL - invalid property group name (error printed)
* - invalid property name (error printed)
* - invalid value (error printed)
* EBUSY - new property group changed (error printed)
*/
static int
lscf_import_service_pgs(scf_service_t *svc, const char *target_fmri,
const entity_t *isvc, int flags)
{
scf_callback_t cbdata;
cbdata.sc_handle = scf_service_handle(svc);
cbdata.sc_parent = svc;
cbdata.sc_service = 1;
cbdata.sc_general = 0;
cbdata.sc_enable = 0;
cbdata.sc_flags = flags;
cbdata.sc_source_fmri = isvc->sc_fmri;
cbdata.sc_target_fmri = target_fmri;
/*
* If the op is set, then add the flag to the callback
* flags for later use.
*/
if (isvc->sc_op != SVCCFG_OP_NONE) {
switch (isvc->sc_op) {
case SVCCFG_OP_IMPORT :
cbdata.sc_flags |= SCI_OP_IMPORT;
break;
case SVCCFG_OP_APPLY :
cbdata.sc_flags |= SCI_OP_APPLY;
break;
case SVCCFG_OP_RESTORE :
cbdata.sc_flags |= SCI_OP_RESTORE;
break;
default :
uu_die(gettext("lscf_import_service_pgs : "
"Unknown op stored in the service entity\n"));
}
}
if (uu_list_walk(isvc->sc_pgroups, entity_pgroup_import, &cbdata,
UU_DEFAULT) != 0) {
if (uu_error() != UU_ERROR_CALLBACK_FAILED)
bad_error("uu_list_walk", uu_error());
return (cbdata.sc_err);
}
return (0);
}
/*
* Returns
* 0 - success
* ECONNABORTED - repository connection broken
* ENOMEM - out of memory
* ENOSPC - svc.configd is out of resources
* ECANCELED - inst was deleted
* EPERM - could not create property group (permission denied) (error printed)
* - could not modify property group (permission denied) (error printed)
* EROFS - could not create property group (repository is read-only)
* EACCES - could not create property group (backend access denied)
* EEXIST - could not create property group (already exists)
* EINVAL - invalid property group name (error printed)
* - invalid property name (error printed)
* - invalid value (error printed)
* EBUSY - new property group changed (error printed)
*/
static int
lscf_import_instance_pgs(scf_instance_t *inst, const char *target_fmri,
const entity_t *iinst, int flags)
{
scf_callback_t cbdata;
cbdata.sc_handle = scf_instance_handle(inst);
cbdata.sc_parent = inst;
cbdata.sc_service = 0;
cbdata.sc_general = NULL;
cbdata.sc_enable = NULL;
cbdata.sc_flags = flags;
cbdata.sc_source_fmri = iinst->sc_fmri;
cbdata.sc_target_fmri = target_fmri;
/*
* If the op is set, then add the flag to the callback
* flags for later use.
*/
if (iinst->sc_op != SVCCFG_OP_NONE) {
switch (iinst->sc_op) {
case SVCCFG_OP_IMPORT :
cbdata.sc_flags |= SCI_OP_IMPORT;
break;
case SVCCFG_OP_APPLY :
cbdata.sc_flags |= SCI_OP_APPLY;
break;
case SVCCFG_OP_RESTORE :
cbdata.sc_flags |= SCI_OP_RESTORE;
break;
default :
uu_die(gettext("lscf_import_instance_pgs : "
"Unknown op stored in the instance entity\n"));
}
}
if (uu_list_walk(iinst->sc_pgroups, entity_pgroup_import, &cbdata,
UU_DEFAULT) != 0) {
if (uu_error() != UU_ERROR_CALLBACK_FAILED)
bad_error("uu_list_walk", uu_error());
return (cbdata.sc_err);
}
if ((flags & SCI_GENERALLAST) && cbdata.sc_general) {
cbdata.sc_flags = flags & (~SCI_GENERALLAST);
/*
* If importing with the SCI_NOENABLED flag then
* skip the delay, but if not then add the delay
* of the enable property.
*/
if (!(cbdata.sc_flags & SCI_NOENABLED)) {
cbdata.sc_flags |= SCI_DELAYENABLE;
}
if (entity_pgroup_import(cbdata.sc_general, &cbdata)
!= UU_WALK_NEXT)
return (cbdata.sc_err);
}
return (0);
}
/*
* Report the reasons why we can't upgrade pg2 to pg1.
*/
static void
report_pg_diffs(const pgroup_t *pg1, const pgroup_t *pg2, const char *fmri,
int new)
{
property_t *p1, *p2;
assert(strcmp(pg1->sc_pgroup_name, pg2->sc_pgroup_name) == 0);
if (!pg_attrs_equal(pg1, pg2, fmri, new))
return;
for (p1 = uu_list_first(pg1->sc_pgroup_props);
p1 != NULL;
p1 = uu_list_next(pg1->sc_pgroup_props, p1)) {
p2 = uu_list_find(pg2->sc_pgroup_props, p1, NULL, NULL);
if (p2 != NULL) {
(void) prop_equal(p1, p2, fmri, pg1->sc_pgroup_name,
new);
continue;
}
if (new)
warn(gettext("Conflict upgrading %s (new property "
"group \"%s\" is missing property \"%s\").\n"),
fmri, pg1->sc_pgroup_name, p1->sc_property_name);
else
warn(gettext("Conflict upgrading %s (property "
"\"%s/%s\" is missing).\n"), fmri,
pg1->sc_pgroup_name, p1->sc_property_name);
}
/*
* Since pg1 should be from the manifest, any properties in pg2 which
* aren't in pg1 shouldn't be reported as conflicts.
*/
}
/*
* Add transaction entries to tx which will upgrade cur's pg according to old
* & new.
*
* Returns
* 0 - success
* EINVAL - new has a property with an invalid name or value (message emitted)
* ENOMEM - out of memory
*/
static int
add_upgrade_entries(scf_transaction_t *tx, pgroup_t *old, pgroup_t *new,
pgroup_t *cur, int speak, const char *fmri)
{
property_t *p, *new_p, *cur_p;
scf_transaction_entry_t *e;
int r;
int is_general;
int is_protected;
if (uu_list_walk(new->sc_pgroup_props, clear_int,
(void *)offsetof(property_t, sc_seen), UU_DEFAULT) != 0)
bad_error("uu_list_walk", uu_error());
is_general = strcmp(old->sc_pgroup_name, SCF_PG_GENERAL) == 0;
for (p = uu_list_first(old->sc_pgroup_props);
p != NULL;
p = uu_list_next(old->sc_pgroup_props, p)) {
/* p is a property in the old property group. */
/* Protect live properties. */
is_protected = 0;
if (is_general) {
if (strcmp(p->sc_property_name, SCF_PROPERTY_ENABLED) ==
0 ||
strcmp(p->sc_property_name,
SCF_PROPERTY_RESTARTER) == 0)
is_protected = 1;
}
/* Look for the same property in the new properties. */
new_p = uu_list_find(new->sc_pgroup_props, p, NULL, NULL);
if (new_p != NULL) {
new_p->sc_seen = 1;
/*
* If the new property is the same as the old, don't do
* anything (leave any user customizations).
*/
if (prop_equal(p, new_p, NULL, NULL, 0))
continue;
if (new_p->sc_property_override)
goto upgrade;
}
cur_p = uu_list_find(cur->sc_pgroup_props, p, NULL, NULL);
if (cur_p == NULL) {
/*
* p has been deleted from the repository. If we were
* going to delete it anyway, do nothing. Otherwise
* report a conflict.
*/
if (new_p == NULL)
continue;
if (is_protected)
continue;
warn(gettext("Conflict upgrading %s "
"(property \"%s/%s\" is missing).\n"), fmri,
old->sc_pgroup_name, p->sc_property_name);
continue;
}
if (!prop_equal(p, cur_p, NULL, NULL, 0)) {
/*
* Conflict. Don't warn if the property is already the
* way we want it, though.
*/
if (is_protected)
continue;
if (new_p == NULL)
(void) prop_equal(p, cur_p, fmri,
old->sc_pgroup_name, 0);
else
(void) prop_equal(cur_p, new_p, fmri,
old->sc_pgroup_name, 0);
continue;
}
if (is_protected) {
if (speak)
warn(gettext("%s: Refusing to upgrade "
"\"%s/%s\" (live property).\n"), fmri,
old->sc_pgroup_name, p->sc_property_name);
continue;
}
upgrade:
/* p hasn't been customized in the repository. Upgrade it. */
if (new_p == NULL) {
/* p was deleted. Delete from cur if unchanged. */
if (speak)
warn(gettext(
"%s: Deleting property \"%s/%s\".\n"),
fmri, old->sc_pgroup_name,
p->sc_property_name);
e = scf_entry_create(g_hndl);
if (e == NULL)
return (ENOMEM);
if (scf_transaction_property_delete(tx, e,
p->sc_property_name) != 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
scf_entry_destroy(e);
return (ECANCELED);
case SCF_ERROR_CONNECTION_BROKEN:
scf_entry_destroy(e);
return (ECONNABORTED);
case SCF_ERROR_NOT_FOUND:
/*
* This can happen if cur is from the
* running snapshot (and it differs
* from the live properties).
*/
scf_entry_destroy(e);
break;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
case SCF_ERROR_INVALID_ARGUMENT:
default:
bad_error(
"scf_transaction_property_delete",
scf_error());
}
}
} else {
scf_callback_t ctx;
if (speak)
warn(gettext(
"%s: Upgrading property \"%s/%s\".\n"),
fmri, old->sc_pgroup_name,
p->sc_property_name);
ctx.sc_handle = g_hndl;
ctx.sc_trans = tx;
ctx.sc_flags = 0;
r = lscf_property_import(new_p, &ctx);
if (r != UU_WALK_NEXT) {
if (r != UU_WALK_ERROR)
bad_error("lscf_property_import", r);
return (EINVAL);
}
}
}
/* Go over the properties which were added. */
for (new_p = uu_list_first(new->sc_pgroup_props);
new_p != NULL;
new_p = uu_list_next(new->sc_pgroup_props, new_p)) {
if (new_p->sc_seen)
continue;
/* This is a new property. */
cur_p = uu_list_find(cur->sc_pgroup_props, new_p, NULL, NULL);
if (cur_p == NULL) {
scf_callback_t ctx;
ctx.sc_handle = g_hndl;
ctx.sc_trans = tx;
ctx.sc_flags = 0;
r = lscf_property_import(new_p, &ctx);
if (r != UU_WALK_NEXT) {
if (r != UU_WALK_ERROR)
bad_error("lscf_property_import", r);
return (EINVAL);
}
continue;
}
/*
* Report a conflict if the new property differs from the
* current one. Unless it's general/enabled, since that's
* never in the last-import snapshot.
*/
if (strcmp(new_p->sc_property_name, SCF_PROPERTY_ENABLED) ==
0 &&
strcmp(cur->sc_pgroup_name, SCF_PG_GENERAL) == 0)
continue;
(void) prop_equal(cur_p, new_p, fmri, old->sc_pgroup_name, 1);
}
return (0);
}
/*
* Upgrade pg according to old & new.
*
* Returns
* 0 - success
* ECONNABORTED - repository connection broken
* ENOMEM - out of memory
* ENOSPC - svc.configd is out of resources
* ECANCELED - pg was deleted
* EPERM - couldn't modify pg (permission denied)
* EROFS - couldn't modify pg (backend read-only)
* EACCES - couldn't modify pg (backend access denied)
* EINVAL - new has a property with invalid name or value (error printed)
* EBUSY - pg changed unexpectedly
*/
static int
upgrade_pg(scf_propertygroup_t *pg, pgroup_t *cur, pgroup_t *old,
pgroup_t *new, int speak, const char *fmri)
{
int r;
if (scf_transaction_start(imp_tx, pg) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_DELETED:
case SCF_ERROR_PERMISSION_DENIED:
case SCF_ERROR_BACKEND_READONLY:
case SCF_ERROR_BACKEND_ACCESS:
return (scferror2errno(scf_error()));
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_IN_USE:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_transaction_start", scf_error());
}
}
r = add_upgrade_entries(imp_tx, old, new, cur, speak, fmri);
switch (r) {
case 0:
break;
case EINVAL:
case ENOMEM:
scf_transaction_destroy_children(imp_tx);
return (r);
default:
bad_error("add_upgrade_entries", r);
}
r = scf_transaction_commit(imp_tx);
scf_transaction_destroy_children(imp_tx);
switch (r) {
case 1:
break;
case 0:
return (EBUSY);
case -1:
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_NO_RESOURCES:
case SCF_ERROR_PERMISSION_DENIED:
case SCF_ERROR_BACKEND_READONLY:
case SCF_ERROR_BACKEND_ACCESS:
case SCF_ERROR_DELETED:
return (scferror2errno(scf_error()));
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_transaction_commit", scf_error());
}
default:
bad_error("scf_transaction_commit", r);
}
return (0);
}
/*
* Compares two entity FMRIs. Returns
*
* 1 - equal
* 0 - not equal
* -1 - f1 is invalid or not an entity
* -2 - f2 is invalid or not an entity
*/
static int
fmri_equal(const char *f1, const char *f2)
{
int r;
const char *s1, *i1, *pg1;
const char *s2, *i2, *pg2;
if (strlcpy(imp_fe1, f1, max_scf_fmri_len + 1) >= max_scf_fmri_len + 1)
return (-1);
if (scf_parse_svc_fmri(imp_fe1, NULL, &s1, &i1, &pg1, NULL) != 0)
return (-1);
if (s1 == NULL || pg1 != NULL)
return (-1);
if (strlcpy(imp_fe2, f2, max_scf_fmri_len + 1) >= max_scf_fmri_len + 1)
return (-2);
if (scf_parse_svc_fmri(imp_fe2, NULL, &s2, &i2, &pg2, NULL) != 0)
return (-2);
if (s2 == NULL || pg2 != NULL)
return (-2);
r = strcmp(s1, s2);
if (r != 0)
return (0);
if (i1 == NULL && i2 == NULL)
return (1);
if (i1 == NULL || i2 == NULL)
return (0);
return (strcmp(i1, i2) == 0);
}
/*
* Import a dependent by creating a dependency property group in the dependent
* entity. If lcbdata->sc_trans is set, assume it's been started on the
* dependents pg, and add an entry to create a new property for this
* dependent. Uses sc_handle, sc_trans, and sc_fmri in lcbdata.
*
* On success, returns UU_WALK_NEXT. On error, returns UU_WALK_ERROR and sets
* lcbdata->sc_err to
* ECONNABORTED - repository connection broken
* ENOMEM - out of memory
* ENOSPC - configd is out of resources
* EINVAL - target is invalid (error printed)
* - target is not an entity (error printed)
* - dependent has invalid name (error printed)
* - invalid property name (error printed)
* - invalid value (error printed)
* - scope of target does not exist (error printed)
* EPERM - couldn't create target (permission denied) (error printed)
* - couldn't create dependency pg (permission denied) (error printed)
* - couldn't modify dependency pg (permission denied) (error printed)
* EROFS - couldn't create target (repository read-only)
* - couldn't create dependency pg (repository read-only)
* EACCES - couldn't create target (backend access denied)
* - couldn't create dependency pg (backend access denied)
* ECANCELED - sc_trans's pg was deleted
* EALREADY - property for dependent already exists in sc_trans's pg
* EEXIST - dependency pg already exists in target (error printed)
* EBUSY - target deleted (error printed)
* - property group changed during import (error printed)
*/
static int
lscf_dependent_import(void *a1, void *pvt)
{
pgroup_t *pgrp = a1;
scf_callback_t *lcbdata = pvt;
int isservice;
int ret;
scf_transaction_entry_t *e;
scf_value_t *val;
scf_callback_t dependent_cbdata;
scf_error_t scfe;
/*
* Decode the FMRI into dependent_cbdata->sc_parent. Do it here so if
* it's invalid, we fail before modifying the repository.
*/
scfe = fmri_to_entity(lcbdata->sc_handle, pgrp->sc_pgroup_fmri,
&dependent_cbdata.sc_parent, &isservice);
switch (scfe) {
case SCF_ERROR_NONE:
break;
case SCF_ERROR_NO_MEMORY:
return (stash_scferror_err(lcbdata, scfe));
case SCF_ERROR_INVALID_ARGUMENT:
semerr(gettext("The FMRI for the \"%s\" dependent is "
"invalid.\n"), pgrp->sc_pgroup_name);
return (stash_scferror_err(lcbdata, scfe));
case SCF_ERROR_CONSTRAINT_VIOLATED:
semerr(gettext("The FMRI \"%s\" for the \"%s\" dependent "
"specifies neither a service nor an instance.\n"),
pgrp->sc_pgroup_fmri, pgrp->sc_pgroup_name);
return (stash_scferror_err(lcbdata, scfe));
case SCF_ERROR_NOT_FOUND:
scfe = create_entity(lcbdata->sc_handle, pgrp->sc_pgroup_fmri,
&dependent_cbdata.sc_parent, &isservice);
switch (scfe) {
case SCF_ERROR_NONE:
break;
case SCF_ERROR_NO_MEMORY:
case SCF_ERROR_BACKEND_READONLY:
case SCF_ERROR_BACKEND_ACCESS:
return (stash_scferror_err(lcbdata, scfe));
case SCF_ERROR_NOT_FOUND:
semerr(gettext("The scope in FMRI \"%s\" for the "
"\"%s\" dependent does not exist.\n"),
pgrp->sc_pgroup_fmri, pgrp->sc_pgroup_name);
lcbdata->sc_err = EINVAL;
return (UU_WALK_ERROR);
case SCF_ERROR_PERMISSION_DENIED:
warn(gettext(
"Could not create %s (permission denied).\n"),
pgrp->sc_pgroup_fmri);
return (stash_scferror_err(lcbdata, scfe));
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_CONSTRAINT_VIOLATED:
default:
bad_error("create_entity", scfe);
}
break;
default:
bad_error("fmri_to_entity", scfe);
}
if (lcbdata->sc_trans != NULL) {
e = scf_entry_create(lcbdata->sc_handle);
if (e == NULL) {
if (scf_error() != SCF_ERROR_NO_MEMORY)
bad_error("scf_entry_create", scf_error());
entity_destroy(dependent_cbdata.sc_parent, isservice);
return (stash_scferror(lcbdata));
}
if (scf_transaction_property_new(lcbdata->sc_trans, e,
pgrp->sc_pgroup_name, SCF_TYPE_FMRI) != 0) {
switch (scf_error()) {
case SCF_ERROR_INVALID_ARGUMENT:
warn(gettext("Dependent of %s has invalid name "
"\"%s\".\n"), pgrp->sc_parent->sc_fmri,
pgrp->sc_pgroup_name);
/* FALLTHROUGH */
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
scf_entry_destroy(e);
entity_destroy(dependent_cbdata.sc_parent,
isservice);
return (stash_scferror(lcbdata));
case SCF_ERROR_EXISTS:
scf_entry_destroy(e);
entity_destroy(dependent_cbdata.sc_parent,
isservice);
lcbdata->sc_err = EALREADY;
return (UU_WALK_ERROR);
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_transaction_property_new",
scf_error());
}
}
val = scf_value_create(lcbdata->sc_handle);
if (val == NULL) {
if (scf_error() != SCF_ERROR_NO_MEMORY)
bad_error("scf_value_create", scf_error());
entity_destroy(dependent_cbdata.sc_parent, isservice);
return (stash_scferror(lcbdata));
}
if (scf_value_set_from_string(val, SCF_TYPE_FMRI,
pgrp->sc_pgroup_fmri) != 0)
/* invalid should have been caught above */
bad_error("scf_value_set_from_string", scf_error());
if (scf_entry_add_value(e, val) != 0)
bad_error("scf_entry_add_value", scf_error());
}
/* Add the property group to the target entity. */
dependent_cbdata.sc_handle = lcbdata->sc_handle;
dependent_cbdata.sc_flags = lcbdata->sc_flags;
dependent_cbdata.sc_source_fmri = lcbdata->sc_source_fmri;
dependent_cbdata.sc_target_fmri = pgrp->sc_pgroup_fmri;
ret = entity_pgroup_import(pgrp, &dependent_cbdata);
entity_destroy(dependent_cbdata.sc_parent, isservice);
if (ret == UU_WALK_NEXT)
return (ret);
if (ret != UU_WALK_ERROR)
bad_error("entity_pgroup_import", ret);
switch (dependent_cbdata.sc_err) {
case ECANCELED:
warn(gettext("%s deleted unexpectedly.\n"),
pgrp->sc_pgroup_fmri);
lcbdata->sc_err = EBUSY;
break;
case EEXIST:
warn(gettext("Could not create \"%s\" dependency in %s "
"(already exists).\n"), pgrp->sc_pgroup_name,
pgrp->sc_pgroup_fmri);
/* FALLTHROUGH */
default:
lcbdata->sc_err = dependent_cbdata.sc_err;
}
return (UU_WALK_ERROR);
}
static int upgrade_dependent(const scf_property_t *, const entity_t *,
const scf_snaplevel_t *, scf_transaction_t *);
static int handle_dependent_conflict(const entity_t *, const scf_property_t *,
const pgroup_t *);
/*
* Upgrade uncustomized dependents of ent to those specified in ient. Read
* the current dependent targets from running (the snaplevel of a running
* snapshot which corresponds to ient) if not NULL (ent, an scf_service_t * or
* scf_instance_t * according to ient, otherwise). Draw the ancestral
* dependent targets and dependency properties from li_dpts_pg (the
* "dependents" property group in snpl) and snpl (the snaplevel which
* corresponds to ent in a last-import snapshot). If li_dpts_pg is NULL, then
* snpl doesn't have a "dependents" property group, and any dependents in ient
* are new.
*
* Returns
* 0 - success
* ECONNABORTED - repository connection broken
* ENOMEM - out of memory
* ENOSPC - configd is out of resources
* ECANCELED - ent was deleted
* ENODEV - the entity containing li_dpts_pg was deleted
* EPERM - could not modify dependents pg (permission denied) (error printed)
* - couldn't upgrade dependent (permission denied) (error printed)
* - couldn't create dependent (permission denied) (error printed)
* EROFS - could not modify dependents pg (repository read-only)
* - couldn't upgrade dependent (repository read-only)
* - couldn't create dependent (repository read-only)
* EACCES - could not modify dependents pg (backend access denied)
* - could not upgrade dependent (backend access denied)
* - could not create dependent (backend access denied)
* EBUSY - "dependents" pg of ent added, changed, or deleted (error printed)
* - dependent target deleted (error printed)
* - dependent pg changed (error printed)
* EINVAL - new dependent is invalid (error printed)
* EBADF - snpl is corrupt (error printed)
* - snpl has corrupt pg (error printed)
* - dependency pg in target is corrupt (error printed)
* - target has corrupt snapshot (error printed)
* EEXIST - dependency pg already existed in target service (error printed)
*/
static int
upgrade_dependents(const scf_propertygroup_t *li_dpts_pg,
const scf_snaplevel_t *snpl, const entity_t *ient,
const scf_snaplevel_t *running, void *ent)
{
pgroup_t *new_dpt_pgroup;
scf_callback_t cbdata;
int r, unseen, tx_started = 0;
int have_cur_depts;
const char * const dependents = "dependents";
const int issvc = (ient->sc_etype == SVCCFG_SERVICE_OBJECT);
if (li_dpts_pg == NULL && uu_list_numnodes(ient->sc_dependents) == 0)
/* Nothing to do. */
return (0);
/* Fetch the current version of the "dependents" property group. */
have_cur_depts = 1;
if (entity_get_pg(ent, issvc, dependents, ud_cur_depts_pg) != 0) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
return (scferror2errno(scf_error()));
case SCF_ERROR_NOT_SET:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
default:
bad_error("entity_get_pg", scf_error());
}
have_cur_depts = 0;
}
/* Fetch the running version of the "dependents" property group. */
ud_run_dpts_pg_set = 0;
if (running != NULL)
r = scf_snaplevel_get_pg(running, dependents, ud_run_dpts_pg);
else
r = entity_get_pg(ent, issvc, dependents, ud_run_dpts_pg);
if (r == 0) {
ud_run_dpts_pg_set = 1;
} else {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
return (scferror2errno(scf_error()));
case SCF_ERROR_NOT_SET:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
default:
bad_error(running ? "scf_snaplevel_get_pg" :
"entity_get_pg", scf_error());
}
}
/*
* Clear the seen fields of the dependents, so we can tell which ones
* are new.
*/
if (uu_list_walk(ient->sc_dependents, clear_int,
(void *)offsetof(pgroup_t, sc_pgroup_seen), UU_DEFAULT) != 0)
bad_error("uu_list_walk", uu_error());
if (li_dpts_pg != NULL) {
/*
* Each property in li_dpts_pg represents a dependent tag in
* the old manifest. For each, call upgrade_dependent(),
* which will change ud_cur_depts_pg or dependencies in other
* services as appropriate. Note (a) that changes to
* ud_cur_depts_pg are accumulated in ud_tx so they can all be
* made en masse, and (b) it's ok if the entity doesn't have
* a current version of the "dependents" property group,
* because we'll just consider all dependents as customized
* (by being deleted).
*/
if (scf_iter_pg_properties(ud_iter, li_dpts_pg) != 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
return (ENODEV);
case SCF_ERROR_CONNECTION_BROKEN:
return (ECONNABORTED);
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_iter_pg_properties",
scf_error());
}
}
if (have_cur_depts &&
scf_transaction_start(ud_tx, ud_cur_depts_pg) != 0) {
switch (scf_error()) {
case SCF_ERROR_BACKEND_ACCESS:
case SCF_ERROR_BACKEND_READONLY:
case SCF_ERROR_CONNECTION_BROKEN:
return (scferror2errno(scf_error()));
case SCF_ERROR_DELETED:
warn(emsg_pg_deleted, ient->sc_fmri,
dependents);
return (EBUSY);
case SCF_ERROR_PERMISSION_DENIED:
warn(emsg_pg_mod_perm, dependents,
ient->sc_fmri);
return (scferror2errno(scf_error()));
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_IN_USE:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_transaction_start", scf_error());
}
}
tx_started = have_cur_depts;
for (;;) {
r = scf_iter_next_property(ud_iter, ud_dpt_prop);
if (r == 0)
break;
if (r == 1) {
r = upgrade_dependent(ud_dpt_prop, ient, snpl,
tx_started ? ud_tx : NULL);
switch (r) {
case 0:
continue;
case ECONNABORTED:
case ENOMEM:
case ENOSPC:
case EBADF:
case EBUSY:
case EINVAL:
case EPERM:
case EROFS:
case EACCES:
case EEXIST:
break;
case ECANCELED:
r = ENODEV;
break;
default:
bad_error("upgrade_dependent", r);
}
if (tx_started)
scf_transaction_destroy_children(ud_tx);
return (r);
}
if (r != -1)
bad_error("scf_iter_next_property", r);
switch (scf_error()) {
case SCF_ERROR_DELETED:
r = ENODEV;
break;
case SCF_ERROR_CONNECTION_BROKEN:
r = ECONNABORTED;
break;
case SCF_ERROR_NOT_SET:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
default:
bad_error("scf_iter_next_property",
scf_error());
}
if (tx_started)
scf_transaction_destroy_children(ud_tx);
return (r);
}
}
/* import unseen dependents */
unseen = 0;
for (new_dpt_pgroup = uu_list_first(ient->sc_dependents);
new_dpt_pgroup != NULL;
new_dpt_pgroup = uu_list_next(ient->sc_dependents,
new_dpt_pgroup)) {
if (!new_dpt_pgroup->sc_pgroup_seen) {
unseen = 1;
break;
}
}
/* If there are none, exit early. */
if (unseen == 0)
goto commit;
/* Set up for lscf_dependent_import() */
cbdata.sc_handle = g_hndl;
cbdata.sc_parent = ent;
cbdata.sc_service = issvc;
cbdata.sc_flags = 0;
if (!have_cur_depts) {
/*
* We have new dependents to import, so we need a "dependents"
* property group.
*/
if (issvc)
r = scf_service_add_pg(ent, dependents,
SCF_GROUP_FRAMEWORK, 0, ud_cur_depts_pg);
else
r = scf_instance_add_pg(ent, dependents,
SCF_GROUP_FRAMEWORK, 0, ud_cur_depts_pg);
if (r != 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_BACKEND_READONLY:
case SCF_ERROR_BACKEND_ACCESS:
case SCF_ERROR_NO_RESOURCES:
return (scferror2errno(scf_error()));
case SCF_ERROR_EXISTS:
warn(emsg_pg_added, ient->sc_fmri, dependents);
return (EBUSY);
case SCF_ERROR_PERMISSION_DENIED:
warn(emsg_pg_add_perm, dependents,
ient->sc_fmri);
return (scferror2errno(scf_error()));
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_service_add_pg", scf_error());
}
}
}
cbdata.sc_trans = ud_tx;
if (!tx_started && scf_transaction_start(ud_tx, ud_cur_depts_pg) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_BACKEND_ACCESS:
case SCF_ERROR_BACKEND_READONLY:
return (scferror2errno(scf_error()));
case SCF_ERROR_DELETED:
warn(emsg_pg_deleted, ient->sc_fmri, dependents);
return (EBUSY);
case SCF_ERROR_PERMISSION_DENIED:
warn(emsg_pg_mod_perm, dependents, ient->sc_fmri);
return (scferror2errno(scf_error()));
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_IN_USE:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_transaction_start", scf_error());
}
}
tx_started = 1;
for (new_dpt_pgroup = uu_list_first(ient->sc_dependents);
new_dpt_pgroup != NULL;
new_dpt_pgroup = uu_list_next(ient->sc_dependents,
new_dpt_pgroup)) {
if (new_dpt_pgroup->sc_pgroup_seen)
continue;
if (ud_run_dpts_pg_set) {
/*
* If the dependent is already there, then we have
* a conflict.
*/
if (scf_pg_get_property(ud_run_dpts_pg,
new_dpt_pgroup->sc_pgroup_name, ud_prop) == 0) {
r = handle_dependent_conflict(ient, ud_prop,
new_dpt_pgroup);
switch (r) {
case 0:
continue;
case ECONNABORTED:
case ENOMEM:
case EBUSY:
case EBADF:
case EINVAL:
scf_transaction_destroy_children(ud_tx);
return (r);
default:
bad_error("handle_dependent_conflict",
r);
}
} else {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_INVALID_ARGUMENT:
warn(emsg_fmri_invalid_pg_name,
ient->sc_fmri,
new_dpt_pgroup->sc_pgroup_name);
scf_transaction_destroy_children(ud_tx);
return (EINVAL);
case SCF_ERROR_DELETED:
warn(emsg_pg_deleted, ient->sc_fmri,
new_dpt_pgroup->sc_pgroup_name);
scf_transaction_destroy_children(ud_tx);
return (EBUSY);
case SCF_ERROR_CONNECTION_BROKEN:
scf_transaction_destroy_children(ud_tx);
return (ECONNABORTED);
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_pg_get_property",
scf_error());
}
}
}
r = lscf_dependent_import(new_dpt_pgroup, &cbdata);
if (r != UU_WALK_NEXT) {
if (r != UU_WALK_ERROR)
bad_error("lscf_dependent_import", r);
if (cbdata.sc_err == EALREADY) {
/* Collisions were handled preemptively. */
bad_error("lscf_dependent_import",
cbdata.sc_err);
}
scf_transaction_destroy_children(ud_tx);
return (cbdata.sc_err);
}
}
commit:
if (!tx_started)
return (0);
r = scf_transaction_commit(ud_tx);
scf_transaction_destroy_children(ud_tx);
switch (r) {
case 1:
return (0);
case 0:
warn(emsg_pg_changed, ient->sc_fmri, dependents);
return (EBUSY);
case -1:
break;
default:
bad_error("scf_transaction_commit", r);
}
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_BACKEND_READONLY:
case SCF_ERROR_BACKEND_ACCESS:
case SCF_ERROR_NO_RESOURCES:
return (scferror2errno(scf_error()));
case SCF_ERROR_DELETED:
warn(emsg_pg_deleted, ient->sc_fmri, dependents);
return (EBUSY);
case SCF_ERROR_PERMISSION_DENIED:
warn(emsg_pg_mod_perm, dependents, ient->sc_fmri);
return (scferror2errno(scf_error()));
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_transaction_destroy", scf_error());
/* NOTREACHED */
}
}
/*
* Used to add the manifests to the list of currently supported manifests.
* We can modify the existing manifest list removing entries if the files
* don't exist.
*
* Get the old list and the new file name
* If the new file name is in the list return
* If not then add the file to the list.
* As we process the list check to see if the files in the old list exist
* if not then remove the file from the list.
* Commit the list of manifest file names.
*
*/
static int
upgrade_manifestfiles(pgroup_t *pg, const entity_t *ient,
const scf_snaplevel_t *running, void *ent)
{
scf_propertygroup_t *ud_mfsts_pg = NULL;
scf_property_t *ud_prop = NULL;
scf_iter_t *ud_prop_iter;
scf_value_t *fname_value;
scf_callback_t cbdata;
pgroup_t *mfst_pgroup;
property_t *mfst_prop;
property_t *old_prop;
char *pname;
char *fval;
char *old_pname;
char *old_fval;
int no_upgrade_pg;
int mfst_seen;
int r;
const int issvc = (ient->sc_etype == SVCCFG_SERVICE_OBJECT);
/*
* This should always be the service base on the code
* path, and the fact that the manifests pg is a service
* level property group only.
*/
ud_mfsts_pg = scf_pg_create(g_hndl);
ud_prop = scf_property_create(g_hndl);
ud_prop_iter = scf_iter_create(g_hndl);
fname_value = scf_value_create(g_hndl);
/* Fetch the "manifests" property group */
no_upgrade_pg = 0;
r = entity_get_pg(ent, issvc, SCF_PG_MANIFESTFILES,
ud_mfsts_pg);
if (r != 0) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
no_upgrade_pg = 1;
break;
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
return (scferror2errno(scf_error()));
case SCF_ERROR_NOT_SET:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
default:
bad_error(running ? "scf_snaplevel_get_pg" :
"entity_get_pg", scf_error());
}
}
if (no_upgrade_pg) {
cbdata.sc_handle = g_hndl;
cbdata.sc_parent = ent;
cbdata.sc_service = issvc;
cbdata.sc_flags = SCI_FORCE;
cbdata.sc_source_fmri = ient->sc_fmri;
cbdata.sc_target_fmri = ient->sc_fmri;
if (entity_pgroup_import(pg, &cbdata) != UU_WALK_NEXT)
return (cbdata.sc_err);
return (0);
}
/* Fetch the new manifests property group */
for (mfst_pgroup = uu_list_first(ient->sc_pgroups);
mfst_pgroup != NULL;
mfst_pgroup = uu_list_next(ient->sc_pgroups, mfst_pgroup)) {
if (strcmp(mfst_pgroup->sc_pgroup_name,
SCF_PG_MANIFESTFILES) == 0)
break;
}
if ((r = scf_iter_pg_properties(ud_prop_iter, ud_mfsts_pg)) !=
SCF_SUCCESS)
return (-1);
if ((pname = malloc(MAXPATHLEN)) == NULL)
return (ENOMEM);
if ((fval = malloc(MAXPATHLEN)) == NULL) {
free(pname);
return (ENOMEM);
}
while ((r = scf_iter_next_property(ud_prop_iter, ud_prop)) == 1) {
mfst_seen = 0;
if (scf_property_get_name(ud_prop, pname, MAXPATHLEN) < 0)
continue;
for (mfst_prop = uu_list_first(mfst_pgroup->sc_pgroup_props);
mfst_prop != NULL;
mfst_prop = uu_list_next(mfst_pgroup->sc_pgroup_props,
mfst_prop)) {
if (strcmp(mfst_prop->sc_property_name, pname) == 0) {
mfst_seen = 1;
}
}
/*
* If the manifest is not seen then add it to the new mfst
* property list to get proccessed into the repo.
*/
if (mfst_seen == 0) {
/*
* If we cannot get the value then there is no
* reason to attempt to attach the value to
* the property group
*/
if (prop_get_val(ud_prop, fname_value) == 0 &&
scf_value_get_astring(fname_value, fval,
MAXPATHLEN) != -1) {
old_pname = safe_strdup(pname);
old_fval = safe_strdup(fval);
old_prop = internal_property_create(old_pname,
SCF_TYPE_ASTRING, 1, old_fval);
/*
* Already checked to see if the property exists
* in the group, and it does not.
*/
(void) internal_attach_property(mfst_pgroup,
old_prop);
}
}
}
free(pname);
free(fval);
cbdata.sc_handle = g_hndl;
cbdata.sc_parent = ent;
cbdata.sc_service = issvc;
cbdata.sc_flags = SCI_FORCE;
cbdata.sc_source_fmri = ient->sc_fmri;
cbdata.sc_target_fmri = ient->sc_fmri;
if (entity_pgroup_import(mfst_pgroup, &cbdata) != UU_WALK_NEXT)
return (cbdata.sc_err);
return (r);
}
/*
* prop is taken to be a property in the "dependents" property group of snpl,
* which is taken to be the snaplevel of a last-import snapshot corresponding
* to ient. If prop is a valid dependents property, upgrade the dependent it
* represents according to the repository & ient. If ud_run_dpts_pg_set is
* true, then ud_run_dpts_pg is taken to be the "dependents" property group
* of the entity ient represents (possibly in the running snapshot). If it
* needs to be changed, an entry will be added to tx, if not NULL.
*
* Returns
* 0 - success
* ECONNABORTED - repository connection broken
* ENOMEM - out of memory
* ENOSPC - configd was out of resources
* ECANCELED - snpl's entity was deleted
* EINVAL - dependent target is invalid (error printed)
* - dependent is invalid (error printed)
* EBADF - snpl is corrupt (error printed)
* - snpl has corrupt pg (error printed)
* - dependency pg in target is corrupt (error printed)
* - running snapshot in dependent is missing snaplevel (error printed)
* EPERM - couldn't delete dependency pg (permission denied) (error printed)
* - couldn't create dependent (permission denied) (error printed)
* - couldn't modify dependent pg (permission denied) (error printed)
* EROFS - couldn't delete dependency pg (repository read-only)
* - couldn't create dependent (repository read-only)
* EACCES - couldn't delete dependency pg (backend access denied)
* - couldn't create dependent (backend access denied)
* EBUSY - ud_run_dpts_pg was deleted (error printed)
* - tx's pg was deleted (error printed)
* - dependent pg was changed or deleted (error printed)
* EEXIST - dependency pg already exists in new target (error printed)
*/
static int
upgrade_dependent(const scf_property_t *prop, const entity_t *ient,
const scf_snaplevel_t *snpl, scf_transaction_t *tx)
{
pgroup_t pgrp;
scf_type_t ty;
pgroup_t *new_dpt_pgroup;
pgroup_t *old_dpt_pgroup = NULL;
pgroup_t *current_pg;
pgroup_t *dpt;
scf_callback_t cbdata;
int tissvc;
void *target_ent;
scf_error_t serr;
int r;
scf_transaction_entry_t *ent;
const char * const cf_inval = gettext("Conflict upgrading %s "
"(dependent \"%s\" has invalid dependents property).\n");
const char * const cf_missing = gettext("Conflict upgrading %s "
"(dependent \"%s\" is missing).\n");
const char * const cf_newdpg = gettext("Conflict upgrading %s "
"(dependent \"%s\" has new dependency property group).\n");
const char * const cf_newtarg = gettext("Conflict upgrading %s "
"(dependent \"%s\" has new target).\n");
const char * const li_corrupt =
gettext("%s: \"last-import\" snapshot is corrupt.\n");
const char * const upgrading =
gettext("%s: Upgrading dependent \"%s\".\n");
const char * const r_no_lvl = gettext("%s: \"running\" snapshot is "
"corrupt (missing snaplevel).\n");
if (scf_property_type(prop, &ty) != 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
return (scferror2errno(scf_error()));
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_property_type", scf_error());
}
}
if (!(ty == SCF_TYPE_FMRI || ty == SCF_TYPE_ASTRING)) {
warn(li_corrupt, ient->sc_fmri);
return (EBADF);
}
/*
* prop represents a dependent in the old manifest. It is named after
* the dependent.
*/
if (scf_property_get_name(prop, ud_name, max_scf_name_len + 1) < 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
return (scferror2errno(scf_error()));
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_property_get_name", scf_error());
}
}
/* See if it's in the new manifest. */
pgrp.sc_pgroup_name = ud_name;
new_dpt_pgroup =
uu_list_find(ient->sc_dependents, &pgrp, NULL, UU_DEFAULT);
/* If it's not, delete it... if it hasn't been customized. */
if (new_dpt_pgroup == NULL) {
if (!ud_run_dpts_pg_set)
return (0);
if (scf_property_get_value(prop, ud_val) != 0) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
case SCF_ERROR_CONSTRAINT_VIOLATED:
warn(li_corrupt, ient->sc_fmri);
return (EBADF);
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
return (scferror2errno(scf_error()));
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
case SCF_ERROR_PERMISSION_DENIED:
default:
bad_error("scf_property_get_value",
scf_error());
}
}
if (scf_value_get_as_string(ud_val, ud_oldtarg,
max_scf_value_len + 1) < 0)
bad_error("scf_value_get_as_string", scf_error());
if (scf_pg_get_property(ud_run_dpts_pg, ud_name, ud_prop) !=
0) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
return (0);
case SCF_ERROR_CONNECTION_BROKEN:
return (scferror2errno(scf_error()));
case SCF_ERROR_DELETED:
warn(emsg_pg_deleted, ient->sc_fmri,
"dependents");
return (EBUSY);
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_pg_get_property", scf_error());
}
}
if (scf_property_get_value(ud_prop, ud_val) != 0) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
case SCF_ERROR_CONSTRAINT_VIOLATED:
warn(cf_inval, ient->sc_fmri, ud_name);
return (0);
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
return (scferror2errno(scf_error()));
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
case SCF_ERROR_PERMISSION_DENIED:
default:
bad_error("scf_property_get_value",
scf_error());
}
}
ty = scf_value_type(ud_val);
assert(ty != SCF_TYPE_INVALID);
if (!(ty == SCF_TYPE_FMRI || ty == SCF_TYPE_ASTRING)) {
warn(cf_inval, ient->sc_fmri, ud_name);
return (0);
}
if (scf_value_get_as_string(ud_val, ud_ctarg,
max_scf_value_len + 1) < 0)
bad_error("scf_value_get_as_string", scf_error());
r = fmri_equal(ud_ctarg, ud_oldtarg);
switch (r) {
case 1:
break;
case 0:
case -1: /* warn? */
warn(cf_newtarg, ient->sc_fmri, ud_name);
return (0);
case -2:
warn(li_corrupt, ient->sc_fmri);
return (EBADF);
default:
bad_error("fmri_equal", r);
}
if (scf_snaplevel_get_pg(snpl, ud_name, ud_pg) != 0) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
warn(li_corrupt, ient->sc_fmri);
return (EBADF);
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
return (scferror2errno(scf_error()));
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_snaplevel_get_pg", scf_error());
}
}
r = load_pg(ud_pg, &old_dpt_pgroup, ient->sc_fmri,
snap_lastimport);
switch (r) {
case 0:
break;
case ECANCELED:
case ECONNABORTED:
case ENOMEM:
case EBADF:
return (r);
case EACCES:
default:
bad_error("load_pg", r);
}
serr = fmri_to_entity(g_hndl, ud_ctarg, &target_ent, &tissvc);
switch (serr) {
case SCF_ERROR_NONE:
break;
case SCF_ERROR_NO_MEMORY:
internal_pgroup_free(old_dpt_pgroup);
return (ENOMEM);
case SCF_ERROR_NOT_FOUND:
internal_pgroup_free(old_dpt_pgroup);
goto delprop;
case SCF_ERROR_CONSTRAINT_VIOLATED: /* caught above */
case SCF_ERROR_INVALID_ARGUMENT: /* caught above */
default:
bad_error("fmri_to_entity", serr);
}
r = entity_get_running_pg(target_ent, tissvc, ud_name,
ud_pg, ud_iter2, ud_inst, imp_snap, ud_snpl);
switch (r) {
case 0:
break;
case ECONNABORTED:
internal_pgroup_free(old_dpt_pgroup);
return (r);
case ECANCELED:
case ENOENT:
internal_pgroup_free(old_dpt_pgroup);
goto delprop;
case EBADF:
warn(r_no_lvl, ud_ctarg);
internal_pgroup_free(old_dpt_pgroup);
return (r);
case EINVAL:
default:
bad_error("entity_get_running_pg", r);
}
/* load it */
r = load_pg(ud_pg, &current_pg, ud_ctarg, NULL);
switch (r) {
case 0:
break;
case ECANCELED:
internal_pgroup_free(old_dpt_pgroup);
goto delprop;
case ECONNABORTED:
case ENOMEM:
case EBADF:
internal_pgroup_free(old_dpt_pgroup);
return (r);
case EACCES:
default:
bad_error("load_pg", r);
}
/* compare property groups */
if (!pg_equal(old_dpt_pgroup, current_pg)) {
warn(cf_newdpg, ient->sc_fmri, ud_name);
internal_pgroup_free(old_dpt_pgroup);
internal_pgroup_free(current_pg);
return (0);
}
internal_pgroup_free(old_dpt_pgroup);
internal_pgroup_free(current_pg);
if (g_verbose)
warn(gettext("%s: Deleting dependent \"%s\".\n"),
ient->sc_fmri, ud_name);
if (entity_get_pg(target_ent, tissvc, ud_name, ud_pg) != 0) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
case SCF_ERROR_DELETED:
internal_pgroup_free(old_dpt_pgroup);
goto delprop;
case SCF_ERROR_CONNECTION_BROKEN:
internal_pgroup_free(old_dpt_pgroup);
return (ECONNABORTED);
case SCF_ERROR_NOT_SET:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
default:
bad_error("entity_get_pg", scf_error());
}
}
if (scf_pg_delete(ud_pg) != 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
break;
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_BACKEND_READONLY:
case SCF_ERROR_BACKEND_ACCESS:
return (scferror2errno(scf_error()));
case SCF_ERROR_PERMISSION_DENIED:
warn(emsg_pg_del_perm, ud_name, ient->sc_fmri);
return (scferror2errno(scf_error()));
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_pg_delete", scf_error());
}
}
/*
* This service was changed, so it must be refreshed. But
* since it's not mentioned in the new manifest, we have to
* record its FMRI here for use later. We record the name
* & the entity (via sc_parent) in case we need to print error
* messages during the refresh.
*/
dpt = internal_pgroup_new();
if (dpt == NULL)
return (ENOMEM);
dpt->sc_pgroup_name = strdup(ud_name);
dpt->sc_pgroup_fmri = strdup(ud_ctarg);
if (dpt->sc_pgroup_name == NULL || dpt->sc_pgroup_fmri == NULL)
return (ENOMEM);
dpt->sc_parent = (entity_t *)ient;
if (uu_list_insert_after(imp_deleted_dpts, NULL, dpt) != 0)
uu_die(gettext("libuutil error: %s\n"),
uu_strerror(uu_error()));
delprop:
if (tx == NULL)
return (0);
ent = scf_entry_create(g_hndl);
if (ent == NULL)
return (ENOMEM);
if (scf_transaction_property_delete(tx, ent, ud_name) != 0) {
scf_entry_destroy(ent);
switch (scf_error()) {
case SCF_ERROR_DELETED:
warn(emsg_pg_deleted, ient->sc_fmri,
"dependents");
return (EBUSY);
case SCF_ERROR_CONNECTION_BROKEN:
return (scferror2errno(scf_error()));
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_transaction_property_delete",
scf_error());
}
}
return (0);
}
new_dpt_pgroup->sc_pgroup_seen = 1;
/*
* Decide whether the dependent has changed in the manifest.
*/
/* Compare the target. */
if (scf_property_get_value(prop, ud_val) != 0) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
case SCF_ERROR_CONSTRAINT_VIOLATED:
warn(li_corrupt, ient->sc_fmri);
return (EBADF);
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
return (scferror2errno(scf_error()));
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
case SCF_ERROR_PERMISSION_DENIED:
default:
bad_error("scf_property_get_value", scf_error());
}
}
if (scf_value_get_as_string(ud_val, ud_oldtarg, max_scf_value_len + 1) <
0)
bad_error("scf_value_get_as_string", scf_error());
/*
* If the fmri's are not equal then the old fmri will need to
* be refreshed to ensure that the changes are properly updated
* in that service.
*/
r = fmri_equal(ud_oldtarg, new_dpt_pgroup->sc_pgroup_fmri);
switch (r) {
case 0:
dpt = internal_pgroup_new();
if (dpt == NULL)
return (ENOMEM);
dpt->sc_pgroup_name = strdup(ud_name);
dpt->sc_pgroup_fmri = strdup(ud_oldtarg);
if (dpt->sc_pgroup_name == NULL || dpt->sc_pgroup_fmri == NULL)
return (ENOMEM);
dpt->sc_parent = (entity_t *)ient;
if (uu_list_insert_after(imp_deleted_dpts, NULL, dpt) != 0)
uu_die(gettext("libuutil error: %s\n"),
uu_strerror(uu_error()));
break;
case 1:
/* Compare the dependency pgs. */
if (scf_snaplevel_get_pg(snpl, ud_name, ud_pg) != 0) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
warn(li_corrupt, ient->sc_fmri);
return (EBADF);
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
return (scferror2errno(scf_error()));
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_snaplevel_get_pg", scf_error());
}
}
r = load_pg(ud_pg, &old_dpt_pgroup, ient->sc_fmri,
snap_lastimport);
switch (r) {
case 0:
break;
case ECANCELED:
case ECONNABORTED:
case ENOMEM:
case EBADF:
return (r);
case EACCES:
default:
bad_error("load_pg", r);
}
if (pg_equal(old_dpt_pgroup, new_dpt_pgroup)) {
/* no change, leave customizations */
internal_pgroup_free(old_dpt_pgroup);
return (0);
}
break;
case -1:
warn(li_corrupt, ient->sc_fmri);
return (EBADF);
case -2:
warn(gettext("Dependent \"%s\" has invalid target \"%s\".\n"),
ud_name, new_dpt_pgroup->sc_pgroup_fmri);
return (EINVAL);
default:
bad_error("fmri_equal", r);
}
/*
* The dependent has changed in the manifest. Upgrade the current
* properties if they haven't been customized.
*/
/*
* If new_dpt_pgroup->sc_override, then act as though the property
* group hasn't been customized.
*/
if (new_dpt_pgroup->sc_pgroup_override) {
(void) strcpy(ud_ctarg, ud_oldtarg);
goto nocust;
}
if (!ud_run_dpts_pg_set) {
warn(cf_missing, ient->sc_fmri, ud_name);
r = 0;
goto out;
} else if (scf_pg_get_property(ud_run_dpts_pg, ud_name, ud_prop) != 0) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
warn(cf_missing, ient->sc_fmri, ud_name);
r = 0;
goto out;
case SCF_ERROR_CONNECTION_BROKEN:
r = scferror2errno(scf_error());
goto out;
case SCF_ERROR_DELETED:
warn(emsg_pg_deleted, ient->sc_fmri, "dependents");
r = EBUSY;
goto out;
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_pg_get_property", scf_error());
}
}
if (scf_property_get_value(ud_prop, ud_val) != 0) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
case SCF_ERROR_CONSTRAINT_VIOLATED:
warn(cf_inval, ient->sc_fmri, ud_name);
r = 0;
goto out;
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
r = scferror2errno(scf_error());
goto out;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
case SCF_ERROR_PERMISSION_DENIED:
default:
bad_error("scf_property_get_value", scf_error());
}
}
ty = scf_value_type(ud_val);
assert(ty != SCF_TYPE_INVALID);
if (!(ty == SCF_TYPE_FMRI || ty == SCF_TYPE_ASTRING)) {
warn(cf_inval, ient->sc_fmri, ud_name);
r = 0;
goto out;
}
if (scf_value_get_as_string(ud_val, ud_ctarg, max_scf_value_len + 1) <
0)
bad_error("scf_value_get_as_string", scf_error());
r = fmri_equal(ud_ctarg, ud_oldtarg);
if (r == -1) {
warn(cf_inval, ient->sc_fmri, ud_name);
r = 0;
goto out;
} else if (r == -2) {
warn(li_corrupt, ient->sc_fmri);
r = EBADF;
goto out;
} else if (r == 0) {
/*
* Target has been changed. Only abort now if it's been
* changed to something other than what's in the manifest.
*/
r = fmri_equal(ud_ctarg, new_dpt_pgroup->sc_pgroup_fmri);
if (r == -1) {
warn(cf_inval, ient->sc_fmri, ud_name);
r = 0;
goto out;
} else if (r == 0) {
warn(cf_newtarg, ient->sc_fmri, ud_name);
r = 0;
goto out;
} else if (r != 1) {
/* invalid sc_pgroup_fmri caught above */
bad_error("fmri_equal", r);
}
/*
* Fetch the current dependency pg. If it's what the manifest
* says, then no problem.
*/
serr = fmri_to_entity(g_hndl, ud_ctarg, &target_ent, &tissvc);
switch (serr) {
case SCF_ERROR_NONE:
break;
case SCF_ERROR_NOT_FOUND:
warn(cf_missing, ient->sc_fmri, ud_name);
r = 0;
goto out;
case SCF_ERROR_NO_MEMORY:
r = ENOMEM;
goto out;
case SCF_ERROR_CONSTRAINT_VIOLATED:
case SCF_ERROR_INVALID_ARGUMENT:
default:
bad_error("fmri_to_entity", serr);
}
r = entity_get_running_pg(target_ent, tissvc, ud_name,
ud_pg, ud_iter2, ud_inst, imp_snap, ud_snpl);
switch (r) {
case 0:
break;
case ECONNABORTED:
goto out;
case ECANCELED:
case ENOENT:
warn(cf_missing, ient->sc_fmri, ud_name);
r = 0;
goto out;
case EBADF:
warn(r_no_lvl, ud_ctarg);
goto out;
case EINVAL:
default:
bad_error("entity_get_running_pg", r);
}
r = load_pg(ud_pg, &current_pg, ud_ctarg, NULL);
switch (r) {
case 0:
break;
case ECANCELED:
warn(cf_missing, ient->sc_fmri, ud_name);
r = 0;
goto out;
case ECONNABORTED:
case ENOMEM:
case EBADF:
goto out;
case EACCES:
default:
bad_error("load_pg", r);
}
if (!pg_equal(current_pg, new_dpt_pgroup))
warn(cf_newdpg, ient->sc_fmri, ud_name);
internal_pgroup_free(current_pg);
r = 0;
goto out;
} else if (r != 1) {
bad_error("fmri_equal", r);
}
nocust:
/*
* Target has not been customized. Check the dependency property
* group.
*/
if (old_dpt_pgroup == NULL) {
if (scf_snaplevel_get_pg(snpl, new_dpt_pgroup->sc_pgroup_name,
ud_pg) != 0) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
warn(li_corrupt, ient->sc_fmri);
return (EBADF);
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
return (scferror2errno(scf_error()));
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_snaplevel_get_pg", scf_error());
}
}
r = load_pg(ud_pg, &old_dpt_pgroup, ient->sc_fmri,
snap_lastimport);
switch (r) {
case 0:
break;
case ECANCELED:
case ECONNABORTED:
case ENOMEM:
case EBADF:
return (r);
case EACCES:
default:
bad_error("load_pg", r);
}
}
serr = fmri_to_entity(g_hndl, ud_ctarg, &target_ent, &tissvc);
switch (serr) {
case SCF_ERROR_NONE:
break;
case SCF_ERROR_NOT_FOUND:
warn(cf_missing, ient->sc_fmri, ud_name);
r = 0;
goto out;
case SCF_ERROR_NO_MEMORY:
r = ENOMEM;
goto out;
case SCF_ERROR_CONSTRAINT_VIOLATED:
case SCF_ERROR_INVALID_ARGUMENT:
default:
bad_error("fmri_to_entity", serr);
}
r = entity_get_running_pg(target_ent, tissvc, ud_name, ud_pg,
ud_iter2, ud_inst, imp_snap, ud_snpl);
switch (r) {
case 0:
break;
case ECONNABORTED:
goto out;
case ECANCELED:
case ENOENT:
warn(cf_missing, ient->sc_fmri, ud_name);
r = 0;
goto out;
case EBADF:
warn(r_no_lvl, ud_ctarg);
goto out;
case EINVAL:
default:
bad_error("entity_get_running_pg", r);
}
r = load_pg(ud_pg, &current_pg, ud_ctarg, NULL);
switch (r) {
case 0:
break;
case ECANCELED:
warn(cf_missing, ient->sc_fmri, ud_name);
goto out;
case ECONNABORTED:
case ENOMEM:
case EBADF:
goto out;
case EACCES:
default:
bad_error("load_pg", r);
}
if (!pg_equal(current_pg, old_dpt_pgroup)) {
if (!pg_equal(current_pg, new_dpt_pgroup))
warn(cf_newdpg, ient->sc_fmri, ud_name);
internal_pgroup_free(current_pg);
r = 0;
goto out;
}
/* Uncustomized. Upgrade. */
r = fmri_equal(new_dpt_pgroup->sc_pgroup_fmri, ud_oldtarg);
switch (r) {
case 1:
if (pg_equal(current_pg, new_dpt_pgroup)) {
/* Already upgraded. */
internal_pgroup_free(current_pg);
r = 0;
goto out;
}
internal_pgroup_free(current_pg);
/* upgrade current_pg */
if (entity_get_pg(target_ent, tissvc, ud_name, ud_pg) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
r = scferror2errno(scf_error());
goto out;
case SCF_ERROR_DELETED:
warn(cf_missing, ient->sc_fmri, ud_name);
r = 0;
goto out;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
case SCF_ERROR_HANDLE_MISMATCH:
default:
bad_error("entity_get_pg", scf_error());
}
if (tissvc)
r = scf_service_add_pg(target_ent, ud_name,
SCF_GROUP_DEPENDENCY, 0, ud_pg);
else
r = scf_instance_add_pg(target_ent, ud_name,
SCF_GROUP_DEPENDENCY, 0, ud_pg);
if (r != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_NO_RESOURCES:
case SCF_ERROR_BACKEND_READONLY:
case SCF_ERROR_BACKEND_ACCESS:
r = scferror2errno(scf_error());
goto out;
case SCF_ERROR_DELETED:
warn(cf_missing, ient->sc_fmri,
ud_name);
r = 0;
goto out;
case SCF_ERROR_PERMISSION_DENIED:
warn(emsg_pg_deleted, ud_ctarg,
ud_name);
r = EPERM;
goto out;
case SCF_ERROR_EXISTS:
warn(emsg_pg_added, ud_ctarg, ud_name);
r = EBUSY;
goto out;
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
default:
bad_error("entity_add_pg", scf_error());
}
}
}
r = load_pg(ud_pg, &current_pg, ud_ctarg, NULL);
switch (r) {
case 0:
break;
case ECANCELED:
warn(cf_missing, ient->sc_fmri, ud_name);
goto out;
case ECONNABORTED:
case ENOMEM:
case EBADF:
goto out;
case EACCES:
default:
bad_error("load_pg", r);
}
if (g_verbose)
warn(upgrading, ient->sc_fmri, ud_name);
r = upgrade_pg(ud_pg, current_pg, old_dpt_pgroup,
new_dpt_pgroup, 0, ient->sc_fmri);
switch (r) {
case 0:
break;
case ECANCELED:
warn(emsg_pg_deleted, ud_ctarg, ud_name);
r = EBUSY;
goto out;
case EPERM:
warn(emsg_pg_mod_perm, ud_name, ud_ctarg);
goto out;
case EBUSY:
warn(emsg_pg_changed, ud_ctarg, ud_name);
goto out;
case ECONNABORTED:
case ENOMEM:
case ENOSPC:
case EROFS:
case EACCES:
case EINVAL:
goto out;
default:
bad_error("upgrade_pg", r);
}
break;
case 0: {
scf_transaction_entry_t *ent;
scf_value_t *val;
internal_pgroup_free(current_pg);
/* delete old pg */
if (g_verbose)
warn(upgrading, ient->sc_fmri, ud_name);
if (entity_get_pg(target_ent, tissvc, ud_name, ud_pg) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
r = scferror2errno(scf_error());
goto out;
case SCF_ERROR_DELETED:
warn(cf_missing, ient->sc_fmri, ud_name);
r = 0;
goto out;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
case SCF_ERROR_HANDLE_MISMATCH:
default:
bad_error("entity_get_pg", scf_error());
}
} else if (scf_pg_delete(ud_pg) != 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
break;
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_BACKEND_READONLY:
case SCF_ERROR_BACKEND_ACCESS:
r = scferror2errno(scf_error());
goto out;
case SCF_ERROR_PERMISSION_DENIED:
warn(emsg_pg_del_perm, ud_name, ient->sc_fmri);
r = scferror2errno(scf_error());
goto out;
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_pg_delete", scf_error());
}
}
/* import new one */
cbdata.sc_handle = g_hndl;
cbdata.sc_trans = NULL; /* handled below */
cbdata.sc_flags = 0;
r = lscf_dependent_import(new_dpt_pgroup, &cbdata);
if (r != UU_WALK_NEXT) {
if (r != UU_WALK_ERROR)
bad_error("lscf_dependent_import", r);
r = cbdata.sc_err;
goto out;
}
if (tx == NULL)
break;
if ((ent = scf_entry_create(g_hndl)) == NULL ||
(val = scf_value_create(g_hndl)) == NULL) {
if (scf_error() == SCF_ERROR_NO_MEMORY)
return (ENOMEM);
bad_error("scf_entry_create", scf_error());
}
if (scf_transaction_property_change_type(tx, ent, ud_name,
SCF_TYPE_FMRI) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
r = scferror2errno(scf_error());
goto out;
case SCF_ERROR_DELETED:
warn(emsg_pg_deleted, ient->sc_fmri,
"dependents");
r = EBUSY;
goto out;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_transaction_property_"
"change_type", scf_error());
}
if (scf_transaction_property_new(tx, ent, ud_name,
SCF_TYPE_FMRI) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
r = scferror2errno(scf_error());
goto out;
case SCF_ERROR_DELETED:
warn(emsg_pg_deleted, ient->sc_fmri,
"dependents");
r = EBUSY;
goto out;
case SCF_ERROR_EXISTS:
warn(emsg_pg_changed, ient->sc_fmri,
"dependents");
r = EBUSY;
goto out;
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_transaction_property_"
"new", scf_error());
}
}
}
if (scf_value_set_from_string(val, SCF_TYPE_FMRI,
new_dpt_pgroup->sc_pgroup_fmri) != 0)
/* invalid sc_pgroup_fmri caught above */
bad_error("scf_value_set_from_string",
scf_error());
if (scf_entry_add_value(ent, val) != 0)
bad_error("scf_entry_add_value", scf_error());
break;
}
case -2:
warn(li_corrupt, ient->sc_fmri);
internal_pgroup_free(current_pg);
r = EBADF;
goto out;
case -1:
default:
/* invalid sc_pgroup_fmri caught above */
bad_error("fmri_equal", r);
}
r = 0;
out:
if (old_dpt_pgroup != NULL)
internal_pgroup_free(old_dpt_pgroup);
return (r);
}
/*
* new_dpt_pgroup was in the manifest but not the last-import snapshot, so we
* would import it, except it seems to exist in the service anyway. Compare
* the existent dependent with the one we would import, and report any
* differences (if there are none, be silent). prop is the property which
* represents the existent dependent (in the dependents property group) in the
* entity corresponding to ient.
*
* Returns
* 0 - success (Sort of. At least, we can continue importing.)
* ECONNABORTED - repository connection broken
* EBUSY - ancestor of prop was deleted (error printed)
* ENOMEM - out of memory
* EBADF - corrupt property group (error printed)
* EINVAL - new_dpt_pgroup has invalid target (error printed)
*/
static int
handle_dependent_conflict(const entity_t * const ient,
const scf_property_t * const prop, const pgroup_t * const new_dpt_pgroup)
{
int r;
scf_type_t ty;
scf_error_t scfe;
void *tptr;
int tissvc;
pgroup_t *pgroup;
if (scf_property_get_value(prop, ud_val) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
return (scferror2errno(scf_error()));
case SCF_ERROR_DELETED:
warn(emsg_pg_deleted, ient->sc_fmri,
new_dpt_pgroup->sc_pgroup_name);
return (EBUSY);
case SCF_ERROR_CONSTRAINT_VIOLATED:
case SCF_ERROR_NOT_FOUND:
warn(gettext("Conflict upgrading %s (not importing "
"dependent \"%s\" because it already exists.) "
"Warning: The \"%s/%2$s\" property has more or "
"fewer than one value)).\n"), ient->sc_fmri,
new_dpt_pgroup->sc_pgroup_name, "dependents");
return (0);
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
case SCF_ERROR_PERMISSION_DENIED:
default:
bad_error("scf_property_get_value",
scf_error());
}
}
ty = scf_value_type(ud_val);
assert(ty != SCF_TYPE_INVALID);
if (!(ty == SCF_TYPE_FMRI || ty == SCF_TYPE_ASTRING)) {
warn(gettext("Conflict upgrading %s (not importing dependent "
"\"%s\" because it already exists). Warning: The "
"\"%s/%s\" property has unexpected type \"%s\")).\n"),
ient->sc_fmri, new_dpt_pgroup->sc_pgroup_name,
scf_type_to_string(ty), "dependents");
return (0);
}
if (scf_value_get_as_string(ud_val, ud_ctarg, max_scf_value_len + 1) <
0)
bad_error("scf_value_get_as_string", scf_error());
r = fmri_equal(ud_ctarg, new_dpt_pgroup->sc_pgroup_fmri);
switch (r) {
case 0:
warn(gettext("Conflict upgrading %s (not importing dependent "
"\"%s\" (target \"%s\") because it already exists with "
"target \"%s\").\n"), ient->sc_fmri,
new_dpt_pgroup->sc_pgroup_name,
new_dpt_pgroup->sc_pgroup_fmri, ud_ctarg);
return (0);
case 1:
break;
case -1:
warn(gettext("Conflict upgrading %s (not importing dependent "
"\"%s\" because it already exists). Warning: The current "
"dependent's target (%s) is invalid.\n"), ient->sc_fmri,
new_dpt_pgroup->sc_pgroup_name, ud_ctarg);
return (0);
case -2:
warn(gettext("Dependent \"%s\" of %s has invalid target "
"\"%s\".\n"), new_dpt_pgroup->sc_pgroup_name, ient->sc_fmri,
new_dpt_pgroup->sc_pgroup_fmri);
return (EINVAL);
default:
bad_error("fmri_equal", r);
}
/* compare dependency pgs in target */
scfe = fmri_to_entity(g_hndl, ud_ctarg, &tptr, &tissvc);
switch (scfe) {
case SCF_ERROR_NONE:
break;
case SCF_ERROR_NO_MEMORY:
return (ENOMEM);
case SCF_ERROR_NOT_FOUND:
warn(emsg_dpt_dangling, ient->sc_fmri,
new_dpt_pgroup->sc_pgroup_name, ud_ctarg);
return (0);
case SCF_ERROR_CONSTRAINT_VIOLATED:
case SCF_ERROR_INVALID_ARGUMENT:
default:
bad_error("fmri_to_entity", scfe);
}
r = entity_get_running_pg(tptr, tissvc, new_dpt_pgroup->sc_pgroup_name,
ud_pg, ud_iter, ud_inst, imp_snap, ud_snpl);
switch (r) {
case 0:
break;
case ECONNABORTED:
return (r);
case ECANCELED:
warn(emsg_dpt_dangling, ient->sc_fmri,
new_dpt_pgroup->sc_pgroup_name, ud_ctarg);
return (0);
case EBADF:
if (tissvc)
warn(gettext("%s has an instance with a \"%s\" "
"snapshot which is missing a snaplevel.\n"),
ud_ctarg, "running");
else
warn(gettext("%s has a \"%s\" snapshot which is "
"missing a snaplevel.\n"), ud_ctarg, "running");
/* FALLTHROUGH */
case ENOENT:
warn(emsg_dpt_no_dep, ient->sc_fmri,
new_dpt_pgroup->sc_pgroup_name, ud_ctarg,
new_dpt_pgroup->sc_pgroup_name);
return (0);
case EINVAL:
default:
bad_error("entity_get_running_pg", r);
}
pgroup = internal_pgroup_new();
if (pgroup == NULL)
return (ENOMEM);
r = load_pg(ud_pg, &pgroup, ud_ctarg, NULL);
switch (r) {
case 0:
break;
case ECONNABORTED:
case EBADF:
case ENOMEM:
internal_pgroup_free(pgroup);
return (r);
case ECANCELED:
warn(emsg_dpt_no_dep, ient->sc_fmri,
new_dpt_pgroup->sc_pgroup_name, ud_ctarg,
new_dpt_pgroup->sc_pgroup_name);
internal_pgroup_free(pgroup);
return (0);
case EACCES:
default:
bad_error("load_pg", r);
}
/* report differences */
report_pg_diffs(new_dpt_pgroup, pgroup, ud_ctarg, 1);
internal_pgroup_free(pgroup);
return (0);
}
/*
* lipg is a property group in the last-import snapshot of ent, which is an
* scf_service_t or an scf_instance_t (according to ient). If lipg is not in
* ient's pgroups, delete it from ent if it hasn't been customized. If it is
* in ents's property groups, compare and upgrade ent appropriately.
*
* Returns
* 0 - success
* ECONNABORTED - repository connection broken
* ENOMEM - out of memory
* ENOSPC - configd is out of resources
* EINVAL - ient has invalid dependent (error printed)
* - ient has invalid pgroup_t (error printed)
* ECANCELED - ent has been deleted
* ENODEV - entity containing lipg has been deleted
* - entity containing running has been deleted
* EPERM - could not delete pg (permission denied) (error printed)
* - couldn't upgrade dependents (permission denied) (error printed)
* - couldn't import pg (permission denied) (error printed)
* - couldn't upgrade pg (permission denied) (error printed)
* EROFS - could not delete pg (repository read-only)
* - couldn't upgrade dependents (repository read-only)
* - couldn't import pg (repository read-only)
* - couldn't upgrade pg (repository read-only)
* EACCES - could not delete pg (backend access denied)
* - couldn't upgrade dependents (backend access denied)
* - couldn't import pg (backend access denied)
* - couldn't upgrade pg (backend access denied)
* - couldn't read property (backend access denied)
* EBUSY - property group was added (error printed)
* - property group was deleted (error printed)
* - property group changed (error printed)
* - "dependents" pg was added, changed, or deleted (error printed)
* - dependent target deleted (error printed)
* - dependent pg changed (error printed)
* EBADF - imp_snpl is corrupt (error printed)
* - ent has bad pg (error printed)
* EEXIST - dependent collision in target service (error printed)
*/
static int
process_old_pg(const scf_propertygroup_t *lipg, entity_t *ient, void *ent,
const scf_snaplevel_t *running)
{
int r;
pgroup_t *mpg, *lipg_i, *curpg_i, pgrp;
scf_callback_t cbdata;
const char * const cf_pg_missing =
gettext("Conflict upgrading %s (property group %s is missing)\n");
const char * const deleting =
gettext("%s: Deleting property group \"%s\".\n");
const int issvc = (ient->sc_etype == SVCCFG_SERVICE_OBJECT);
/* Skip dependent property groups. */
if (scf_pg_get_type(lipg, imp_str, imp_str_sz) < 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
return (ENODEV);
case SCF_ERROR_CONNECTION_BROKEN:
return (ECONNABORTED);
case SCF_ERROR_NOT_SET:
case SCF_ERROR_NOT_BOUND:
default:
bad_error("scf_pg_get_type", scf_error());
}
}
if (strcmp(imp_str, SCF_GROUP_DEPENDENCY) == 0) {
if (scf_pg_get_property(lipg, "external", NULL) == 0)
return (0);
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_CONNECTION_BROKEN:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ENODEV);
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_pg_get_property", scf_error());
}
}
/* lookup pg in new properties */
if (scf_pg_get_name(lipg, imp_str, imp_str_sz) < 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
return (ENODEV);
case SCF_ERROR_CONNECTION_BROKEN:
return (ECONNABORTED);
case SCF_ERROR_NOT_SET:
case SCF_ERROR_NOT_BOUND:
default:
bad_error("scf_pg_get_name", scf_error());
}
}
pgrp.sc_pgroup_name = imp_str;
mpg = uu_list_find(ient->sc_pgroups, &pgrp, NULL, NULL);
if (mpg != NULL)
mpg->sc_pgroup_seen = 1;
/* Special handling for dependents */
if (strcmp(imp_str, "dependents") == 0)
return (upgrade_dependents(lipg, imp_snpl, ient, running, ent));
if (strcmp(imp_str, SCF_PG_MANIFESTFILES) == 0)
return (upgrade_manifestfiles(NULL, ient, running, ent));
if (mpg == NULL || mpg->sc_pgroup_delete) {
/* property group was deleted from manifest */
if (entity_get_pg(ent, issvc, imp_str, imp_pg2) != 0) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
return (0);
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
return (scferror2errno(scf_error()));
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("entity_get_pg", scf_error());
}
}
if (mpg != NULL && mpg->sc_pgroup_delete) {
if (g_verbose)
warn(deleting, ient->sc_fmri, imp_str);
if (scf_pg_delete(imp_pg2) == 0)
return (0);
switch (scf_error()) {
case SCF_ERROR_DELETED:
return (0);
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_BACKEND_READONLY:
case SCF_ERROR_BACKEND_ACCESS:
return (scferror2errno(scf_error()));
case SCF_ERROR_PERMISSION_DENIED:
warn(emsg_pg_del_perm, imp_str, ient->sc_fmri);
return (scferror2errno(scf_error()));
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_pg_delete", scf_error());
}
}
r = load_pg(lipg, &lipg_i, ient->sc_fmri, snap_lastimport);
switch (r) {
case 0:
break;
case ECANCELED:
return (ENODEV);
case ECONNABORTED:
case ENOMEM:
case EBADF:
case EACCES:
return (r);
default:
bad_error("load_pg", r);
}
r = load_pg(imp_pg2, &curpg_i, ient->sc_fmri, NULL);
switch (r) {
case 0:
break;
case ECANCELED:
case ECONNABORTED:
case ENOMEM:
case EBADF:
case EACCES:
internal_pgroup_free(lipg_i);
return (r);
default:
bad_error("load_pg", r);
}
if (pg_equal(lipg_i, curpg_i)) {
if (g_verbose)
warn(deleting, ient->sc_fmri, imp_str);
if (scf_pg_delete(imp_pg2) != 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
break;
case SCF_ERROR_CONNECTION_BROKEN:
internal_pgroup_free(lipg_i);
internal_pgroup_free(curpg_i);
return (ECONNABORTED);
case SCF_ERROR_NOT_SET:
case SCF_ERROR_NOT_BOUND:
default:
bad_error("scf_pg_delete", scf_error());
}
}
} else {
report_pg_diffs(lipg_i, curpg_i, ient->sc_fmri, 0);
}
internal_pgroup_free(lipg_i);
internal_pgroup_free(curpg_i);
return (0);
}
/*
* Only dependent pgs can have override set, and we skipped those
* above.
*/
assert(!mpg->sc_pgroup_override);
/* compare */
r = load_pg(lipg, &lipg_i, ient->sc_fmri, snap_lastimport);
switch (r) {
case 0:
break;
case ECANCELED:
return (ENODEV);
case ECONNABORTED:
case EBADF:
case ENOMEM:
case EACCES:
return (r);
default:
bad_error("load_pg", r);
}
if (pg_equal(mpg, lipg_i)) {
/* The manifest pg has not changed. Move on. */
r = 0;
goto out;
}
/* upgrade current properties according to lipg & mpg */
if (running != NULL)
r = scf_snaplevel_get_pg(running, imp_str, imp_pg2);
else
r = entity_get_pg(ent, issvc, imp_str, imp_pg2);
if (r != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
r = scferror2errno(scf_error());
goto out;
case SCF_ERROR_DELETED:
if (running != NULL)
r = ENODEV;
else
r = ECANCELED;
goto out;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("entity_get_pg", scf_error());
}
warn(cf_pg_missing, ient->sc_fmri, imp_str);
r = 0;
goto out;
}
r = load_pg_attrs(imp_pg2, &curpg_i);
switch (r) {
case 0:
break;
case ECANCELED:
warn(cf_pg_missing, ient->sc_fmri, imp_str);
r = 0;
goto out;
case ECONNABORTED:
case ENOMEM:
goto out;
default:
bad_error("load_pg_attrs", r);
}
if (!pg_attrs_equal(lipg_i, curpg_i, NULL, 0)) {
(void) pg_attrs_equal(curpg_i, mpg, ient->sc_fmri, 0);
internal_pgroup_free(curpg_i);
r = 0;
goto out;
}
internal_pgroup_free(curpg_i);
r = load_pg(imp_pg2, &curpg_i, ient->sc_fmri, NULL);
switch (r) {
case 0:
break;
case ECANCELED:
warn(cf_pg_missing, ient->sc_fmri, imp_str);
r = 0;
goto out;
case ECONNABORTED:
case EBADF:
case ENOMEM:
case EACCES:
goto out;
default:
bad_error("load_pg", r);
}
if (pg_equal(lipg_i, curpg_i) &&
!pg_attrs_equal(lipg_i, mpg, NULL, 0)) {
int do_delete = 1;
if (g_verbose)
warn(gettext("%s: Upgrading property group \"%s\".\n"),
ient->sc_fmri, mpg->sc_pgroup_name);
internal_pgroup_free(curpg_i);
if (running != NULL &&
entity_get_pg(ent, issvc, imp_str, imp_pg2) != 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
r = ECANCELED;
goto out;
case SCF_ERROR_NOT_FOUND:
do_delete = 0;
break;
case SCF_ERROR_CONNECTION_BROKEN:
r = scferror2errno(scf_error());
goto out;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
case SCF_ERROR_NOT_BOUND:
default:
bad_error("entity_get_pg", scf_error());
}
}
if (do_delete && scf_pg_delete(imp_pg2) != 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
break;
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_BACKEND_READONLY:
case SCF_ERROR_BACKEND_ACCESS:
r = scferror2errno(scf_error());
goto out;
case SCF_ERROR_PERMISSION_DENIED:
warn(emsg_pg_del_perm, mpg->sc_pgroup_name,
ient->sc_fmri);
r = scferror2errno(scf_error());
goto out;
case SCF_ERROR_NOT_SET:
case SCF_ERROR_NOT_BOUND:
default:
bad_error("scf_pg_delete", scf_error());
}
}
cbdata.sc_handle = g_hndl;
cbdata.sc_parent = ent;
cbdata.sc_service = issvc;
cbdata.sc_flags = 0;
cbdata.sc_source_fmri = ient->sc_fmri;
cbdata.sc_target_fmri = ient->sc_fmri;
r = entity_pgroup_import(mpg, &cbdata);
switch (r) {
case UU_WALK_NEXT:
r = 0;
goto out;
case UU_WALK_ERROR:
if (cbdata.sc_err == EEXIST) {
warn(emsg_pg_added, ient->sc_fmri,
mpg->sc_pgroup_name);
r = EBUSY;
} else {
r = cbdata.sc_err;
}
goto out;
default:
bad_error("entity_pgroup_import", r);
}
}
if (running != NULL &&
entity_get_pg(ent, issvc, imp_str, imp_pg2) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_DELETED:
r = scferror2errno(scf_error());
goto out;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
case SCF_ERROR_NOT_BOUND:
default:
bad_error("entity_get_pg", scf_error());
}
cbdata.sc_handle = g_hndl;
cbdata.sc_parent = ent;
cbdata.sc_service = issvc;
cbdata.sc_flags = SCI_FORCE;
cbdata.sc_source_fmri = ient->sc_fmri;
cbdata.sc_target_fmri = ient->sc_fmri;
r = entity_pgroup_import(mpg, &cbdata);
switch (r) {
case UU_WALK_NEXT:
r = 0;
goto out;
case UU_WALK_ERROR:
if (cbdata.sc_err == EEXIST) {
warn(emsg_pg_added, ient->sc_fmri,
mpg->sc_pgroup_name);
r = EBUSY;
} else {
r = cbdata.sc_err;
}
goto out;
default:
bad_error("entity_pgroup_import", r);
}
}
r = upgrade_pg(imp_pg2, curpg_i, lipg_i, mpg, g_verbose, ient->sc_fmri);
internal_pgroup_free(curpg_i);
switch (r) {
case 0:
ient->sc_import_state = IMPORT_PROP_BEGUN;
break;
case ECANCELED:
warn(emsg_pg_deleted, ient->sc_fmri, mpg->sc_pgroup_name);
r = EBUSY;
break;
case EPERM:
warn(emsg_pg_mod_perm, mpg->sc_pgroup_name, ient->sc_fmri);
break;
case EBUSY:
warn(emsg_pg_changed, ient->sc_fmri, mpg->sc_pgroup_name);
break;
case ECONNABORTED:
case ENOMEM:
case ENOSPC:
case EROFS:
case EACCES:
case EINVAL:
break;
default:
bad_error("upgrade_pg", r);
}
out:
internal_pgroup_free(lipg_i);
return (r);
}
/*
* Upgrade the properties of ent according to snpl & ient.
*
* Returns
* 0 - success
* ECONNABORTED - repository connection broken
* ENOMEM - out of memory
* ENOSPC - configd is out of resources
* ECANCELED - ent was deleted
* ENODEV - entity containing snpl was deleted
* - entity containing running was deleted
* EBADF - imp_snpl is corrupt (error printed)
* - ent has corrupt pg (error printed)
* - dependent has corrupt pg (error printed)
* - dependent target has a corrupt snapshot (error printed)
* EBUSY - pg was added, changed, or deleted (error printed)
* - dependent target was deleted (error printed)
* - dependent pg changed (error printed)
* EINVAL - invalid property group name (error printed)
* - invalid property name (error printed)
* - invalid value (error printed)
* - ient has invalid pgroup or dependent (error printed)
* EPERM - could not create property group (permission denied) (error printed)
* - could not modify property group (permission denied) (error printed)
* - couldn't delete, upgrade, or import pg or dependent (error printed)
* EROFS - could not create property group (repository read-only)
* - couldn't delete, upgrade, or import pg or dependent
* EACCES - could not create property group (backend access denied)
* - couldn't delete, upgrade, or import pg or dependent
* EEXIST - dependent collision in target service (error printed)
*/
static int
upgrade_props(void *ent, scf_snaplevel_t *running, scf_snaplevel_t *snpl,
entity_t *ient)
{
pgroup_t *pg, *rpg;
int r;
uu_list_t *pgs = ient->sc_pgroups;
const int issvc = (ient->sc_etype == SVCCFG_SERVICE_OBJECT);
/* clear sc_sceen for pgs */
if (uu_list_walk(pgs, clear_int,
(void *)offsetof(pgroup_t, sc_pgroup_seen), UU_DEFAULT) != 0)
bad_error("uu_list_walk", uu_error());
if (scf_iter_snaplevel_pgs(imp_up_iter, snpl) != 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
return (ENODEV);
case SCF_ERROR_CONNECTION_BROKEN:
return (ECONNABORTED);
case SCF_ERROR_NOT_SET:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
default:
bad_error("scf_iter_snaplevel_pgs", scf_error());
}
}
for (;;) {
r = scf_iter_next_pg(imp_up_iter, imp_pg);
if (r == 0)
break;
if (r == 1) {
r = process_old_pg(imp_pg, ient, ent, running);
switch (r) {
case 0:
break;
case ECONNABORTED:
case ENOMEM:
case ENOSPC:
case ECANCELED:
case ENODEV:
case EPERM:
case EROFS:
case EACCES:
case EBADF:
case EBUSY:
case EINVAL:
case EEXIST:
return (r);
default:
bad_error("process_old_pg", r);
}
continue;
}
if (r != -1)
bad_error("scf_iter_next_pg", r);
switch (scf_error()) {
case SCF_ERROR_DELETED:
return (ENODEV);
case SCF_ERROR_CONNECTION_BROKEN:
return (ECONNABORTED);
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
case SCF_ERROR_INVALID_ARGUMENT:
default:
bad_error("scf_iter_next_pg", scf_error());
}
}
for (pg = uu_list_first(pgs); pg != NULL; pg = uu_list_next(pgs, pg)) {
if (pg->sc_pgroup_seen)
continue;
/* pg is new */
if (strcmp(pg->sc_pgroup_name, "dependents") == 0) {
r = upgrade_dependents(NULL, imp_snpl, ient, running,
ent);
switch (r) {
case 0:
break;
case ECONNABORTED:
case ENOMEM:
case ENOSPC:
case ECANCELED:
case ENODEV:
case EBADF:
case EBUSY:
case EINVAL:
case EPERM:
case EROFS:
case EACCES:
case EEXIST:
return (r);
default:
bad_error("upgrade_dependents", r);
}
continue;
}
if (strcmp(pg->sc_pgroup_name, SCF_PG_MANIFESTFILES) == 0) {
r = upgrade_manifestfiles(pg, ient, running, ent);
switch (r) {
case 0:
break;
case ECONNABORTED:
case ENOMEM:
case ENOSPC:
case ECANCELED:
case ENODEV:
case EBADF:
case EBUSY:
case EINVAL:
case EPERM:
case EROFS:
case EACCES:
case EEXIST:
return (r);
default:
bad_error("upgrade_manifestfiles", r);
}
continue;
}
if (running != NULL) {
r = scf_snaplevel_get_pg(running, pg->sc_pgroup_name,
imp_pg);
} else {
r = entity_get_pg(ent, issvc, pg->sc_pgroup_name,
imp_pg);
}
if (r != 0) {
scf_callback_t cbdata;
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_CONNECTION_BROKEN:
return (scferror2errno(scf_error()));
case SCF_ERROR_DELETED:
if (running != NULL)
return (ENODEV);
else
return (scferror2errno(scf_error()));
case SCF_ERROR_INVALID_ARGUMENT:
warn(emsg_fmri_invalid_pg_name, ient->sc_fmri,
pg->sc_pgroup_name);
return (EINVAL);
case SCF_ERROR_NOT_SET:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
default:
bad_error("entity_get_pg", scf_error());
}
/* User doesn't have pg, so import it. */
cbdata.sc_handle = g_hndl;
cbdata.sc_parent = ent;
cbdata.sc_service = issvc;
cbdata.sc_flags = SCI_FORCE;
cbdata.sc_source_fmri = ient->sc_fmri;
cbdata.sc_target_fmri = ient->sc_fmri;
r = entity_pgroup_import(pg, &cbdata);
switch (r) {
case UU_WALK_NEXT:
ient->sc_import_state = IMPORT_PROP_BEGUN;
continue;
case UU_WALK_ERROR:
if (cbdata.sc_err == EEXIST) {
warn(emsg_pg_added, ient->sc_fmri,
pg->sc_pgroup_name);
return (EBUSY);
}
return (cbdata.sc_err);
default:
bad_error("entity_pgroup_import", r);
}
}
/* report differences between pg & current */
r = load_pg(imp_pg, &rpg, ient->sc_fmri, NULL);
switch (r) {
case 0:
break;
case ECANCELED:
warn(emsg_pg_deleted, ient->sc_fmri,
pg->sc_pgroup_name);
return (EBUSY);
case ECONNABORTED:
case EBADF:
case ENOMEM:
case EACCES:
return (r);
default:
bad_error("load_pg", r);
}
report_pg_diffs(pg, rpg, ient->sc_fmri, 1);
internal_pgroup_free(rpg);
rpg = NULL;
}
return (0);
}
/*
* Import an instance. If it doesn't exist, create it. If it has
* a last-import snapshot, upgrade its properties. Finish by updating its
* last-import snapshot. If it doesn't have a last-import snapshot then it
* could have been created for a dependent tag in another manifest. Import the
* new properties. If there's a conflict, don't override, like now?
*
* On success, returns UU_WALK_NEXT. On error returns UU_WALK_ERROR and sets
* lcbdata->sc_err to
* ECONNABORTED - repository connection broken
* ENOMEM - out of memory
* ENOSPC - svc.configd is out of resources
* EEXIST - dependency collision in dependent service (error printed)
* EPERM - couldn't create temporary instance (permission denied)
* - couldn't import into temporary instance (permission denied)
* - couldn't take snapshot (permission denied)
* - couldn't upgrade properties (permission denied)
* - couldn't import properties (permission denied)
* - couldn't import dependents (permission denied)
* EROFS - couldn't create temporary instance (repository read-only)
* - couldn't import into temporary instance (repository read-only)
* - couldn't upgrade properties (repository read-only)
* - couldn't import properties (repository read-only)
* - couldn't import dependents (repository read-only)
* EACCES - couldn't create temporary instance (backend access denied)
* - couldn't import into temporary instance (backend access denied)
* - couldn't upgrade properties (backend access denied)
* - couldn't import properties (backend access denied)
* - couldn't import dependents (backend access denied)
* EINVAL - invalid instance name (error printed)
* - invalid pgroup_t's (error printed)
* - invalid dependents (error printed)
* EBUSY - temporary service deleted (error printed)
* - temporary instance deleted (error printed)
* - temporary instance changed (error printed)
* - temporary instance already exists (error printed)
* - instance deleted (error printed)
* EBADF - instance has corrupt last-import snapshot (error printed)
* - instance is corrupt (error printed)
* - dependent has corrupt pg (error printed)
* - dependent target has a corrupt snapshot (error printed)
* -1 - unknown libscf error (error printed)
*/
static int
lscf_instance_import(void *v, void *pvt)
{
entity_t *inst = v;
scf_callback_t ctx;
scf_callback_t *lcbdata = pvt;
scf_service_t *rsvc = lcbdata->sc_parent;
int r;
scf_snaplevel_t *running;
int flags = lcbdata->sc_flags;
const char * const emsg_tdel =
gettext("Temporary instance svc:/%s:%s was deleted.\n");
const char * const emsg_tchg = gettext("Temporary instance svc:/%s:%s "
"changed unexpectedly.\n");
const char * const emsg_del = gettext("%s changed unexpectedly "
"(instance \"%s\" was deleted.)\n");
const char * const emsg_badsnap = gettext(
"\"%s\" snapshot of %s is corrupt (missing a snaplevel).\n");
/*
* prepare last-import snapshot:
* create temporary instance (service was precreated)
* populate with properties from bundle
* take snapshot
*/
if (scf_service_add_instance(imp_tsvc, inst->sc_name, imp_tinst) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_NO_RESOURCES:
case SCF_ERROR_BACKEND_READONLY:
case SCF_ERROR_BACKEND_ACCESS:
return (stash_scferror(lcbdata));
case SCF_ERROR_EXISTS:
warn(gettext("Temporary service svc:/%s "
"changed unexpectedly (instance \"%s\" added).\n"),
imp_tsname, inst->sc_name);
lcbdata->sc_err = EBUSY;
return (UU_WALK_ERROR);
case SCF_ERROR_DELETED:
warn(gettext("Temporary service svc:/%s "
"was deleted unexpectedly.\n"), imp_tsname);
lcbdata->sc_err = EBUSY;
return (UU_WALK_ERROR);
case SCF_ERROR_INVALID_ARGUMENT:
warn(gettext("Invalid instance name \"%s\".\n"),
inst->sc_name);
return (stash_scferror(lcbdata));
case SCF_ERROR_PERMISSION_DENIED:
warn(gettext("Could not create temporary instance "
"\"%s\" in svc:/%s (permission denied).\n"),
inst->sc_name, imp_tsname);
return (stash_scferror(lcbdata));
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_service_add_instance", scf_error());
}
}
r = snprintf(imp_str, imp_str_sz, "svc:/%s:%s", imp_tsname,
inst->sc_name);
if (r < 0)
bad_error("snprintf", errno);
r = lscf_import_instance_pgs(imp_tinst, imp_str, inst,
lcbdata->sc_flags | SCI_NOENABLED);
switch (r) {
case 0:
break;
case ECANCELED:
warn(emsg_tdel, imp_tsname, inst->sc_name);
lcbdata->sc_err = EBUSY;
r = UU_WALK_ERROR;
goto deltemp;
case EEXIST:
warn(emsg_tchg, imp_tsname, inst->sc_name);
lcbdata->sc_err = EBUSY;
r = UU_WALK_ERROR;
goto deltemp;
case ECONNABORTED:
goto connaborted;
case ENOMEM:
case ENOSPC:
case EPERM:
case EROFS:
case EACCES:
case EINVAL:
case EBUSY:
lcbdata->sc_err = r;
r = UU_WALK_ERROR;
goto deltemp;
default:
bad_error("lscf_import_instance_pgs", r);
}
r = snprintf(imp_str, imp_str_sz, "svc:/%s:%s", imp_tsname,
inst->sc_name);
if (r < 0)
bad_error("snprintf", errno);
ctx.sc_handle = lcbdata->sc_handle;
ctx.sc_parent = imp_tinst;
ctx.sc_service = 0;
ctx.sc_source_fmri = inst->sc_fmri;
ctx.sc_target_fmri = imp_str;
if (uu_list_walk(inst->sc_dependents, entity_pgroup_import, &ctx,
UU_DEFAULT) != 0) {
if (uu_error() != UU_ERROR_CALLBACK_FAILED)
bad_error("uu_list_walk", uu_error());
switch (ctx.sc_err) {
case ECONNABORTED:
goto connaborted;
case ECANCELED:
warn(emsg_tdel, imp_tsname, inst->sc_name);
lcbdata->sc_err = EBUSY;
break;
case EEXIST:
warn(emsg_tchg, imp_tsname, inst->sc_name);
lcbdata->sc_err = EBUSY;
break;
default:
lcbdata->sc_err = ctx.sc_err;
}
r = UU_WALK_ERROR;
goto deltemp;
}
if (_scf_snapshot_take_new_named(imp_tinst, inst->sc_parent->sc_name,
inst->sc_name, snap_lastimport, imp_tlisnap) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
goto connaborted;
case SCF_ERROR_NO_RESOURCES:
r = stash_scferror(lcbdata);
goto deltemp;
case SCF_ERROR_EXISTS:
warn(emsg_tchg, imp_tsname, inst->sc_name);
lcbdata->sc_err = EBUSY;
r = UU_WALK_ERROR;
goto deltemp;
case SCF_ERROR_PERMISSION_DENIED:
warn(gettext("Could not take \"%s\" snapshot of %s "
"(permission denied).\n"), snap_lastimport,
imp_str);
r = stash_scferror(lcbdata);
goto deltemp;
default:
scfwarn();
lcbdata->sc_err = -1;
r = UU_WALK_ERROR;
goto deltemp;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
bad_error("_scf_snapshot_take_new_named", scf_error());
}
}
if (lcbdata->sc_flags & SCI_FRESH)
goto fresh;
if (scf_service_get_instance(rsvc, inst->sc_name, imp_inst) == 0) {
if (scf_instance_get_snapshot(imp_inst, snap_lastimport,
imp_lisnap) != 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
warn(emsg_del, inst->sc_parent->sc_fmri,
inst->sc_name);
lcbdata->sc_err = EBUSY;
r = UU_WALK_ERROR;
goto deltemp;
case SCF_ERROR_NOT_FOUND:
flags |= SCI_FORCE;
goto nosnap;
case SCF_ERROR_CONNECTION_BROKEN:
goto connaborted;
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_instance_get_snapshot",
scf_error());
}
}
/* upgrade */
/*
* compare new properties with last-import properties
* upgrade current properties
*/
/* clear sc_sceen for pgs */
if (uu_list_walk(inst->sc_pgroups, clear_int,
(void *)offsetof(pgroup_t, sc_pgroup_seen), UU_DEFAULT) !=
0)
bad_error("uu_list_walk", uu_error());
r = get_snaplevel(imp_lisnap, 0, imp_snpl);
switch (r) {
case 0:
break;
case ECONNABORTED:
goto connaborted;
case ECANCELED:
warn(emsg_del, inst->sc_parent->sc_fmri, inst->sc_name);
lcbdata->sc_err = EBUSY;
r = UU_WALK_ERROR;
goto deltemp;
case ENOENT:
warn(emsg_badsnap, snap_lastimport, inst->sc_fmri);
lcbdata->sc_err = EBADF;
r = UU_WALK_ERROR;
goto deltemp;
default:
bad_error("get_snaplevel", r);
}
if (scf_instance_get_snapshot(imp_inst, snap_running,
imp_rsnap) != 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
warn(emsg_del, inst->sc_parent->sc_fmri,
inst->sc_name);
lcbdata->sc_err = EBUSY;
r = UU_WALK_ERROR;
goto deltemp;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_CONNECTION_BROKEN:
goto connaborted;
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_instance_get_snapshot",
scf_error());
}
running = NULL;
} else {
r = get_snaplevel(imp_rsnap, 0, imp_rsnpl);
switch (r) {
case 0:
running = imp_rsnpl;
break;
case ECONNABORTED:
goto connaborted;
case ECANCELED:
warn(emsg_del, inst->sc_parent->sc_fmri,
inst->sc_name);
lcbdata->sc_err = EBUSY;
r = UU_WALK_ERROR;
goto deltemp;
case ENOENT:
warn(emsg_badsnap, snap_running, inst->sc_fmri);
lcbdata->sc_err = EBADF;
r = UU_WALK_ERROR;
goto deltemp;
default:
bad_error("get_snaplevel", r);
}
}
r = upgrade_props(imp_inst, running, imp_snpl, inst);
switch (r) {
case 0:
break;
case ECANCELED:
case ENODEV:
warn(emsg_del, inst->sc_parent->sc_fmri, inst->sc_name);
lcbdata->sc_err = EBUSY;
r = UU_WALK_ERROR;
goto deltemp;
case ECONNABORTED:
goto connaborted;
case ENOMEM:
case ENOSPC:
case EBADF:
case EBUSY:
case EINVAL:
case EPERM:
case EROFS:
case EACCES:
case EEXIST:
lcbdata->sc_err = r;
r = UU_WALK_ERROR;
goto deltemp;
default:
bad_error("upgrade_props", r);
}
inst->sc_import_state = IMPORT_PROP_DONE;
} else {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
goto connaborted;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_INVALID_ARGUMENT: /* caught above */
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_service_get_instance", scf_error());
}
fresh:
/* create instance */
if (scf_service_add_instance(rsvc, inst->sc_name,
imp_inst) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
goto connaborted;
case SCF_ERROR_NO_RESOURCES:
case SCF_ERROR_BACKEND_READONLY:
case SCF_ERROR_BACKEND_ACCESS:
r = stash_scferror(lcbdata);
goto deltemp;
case SCF_ERROR_EXISTS:
warn(gettext("%s changed unexpectedly "
"(instance \"%s\" added).\n"),
inst->sc_parent->sc_fmri, inst->sc_name);
lcbdata->sc_err = EBUSY;
r = UU_WALK_ERROR;
goto deltemp;
case SCF_ERROR_PERMISSION_DENIED:
warn(gettext("Could not create \"%s\" instance "
"in %s (permission denied).\n"),
inst->sc_name, inst->sc_parent->sc_fmri);
r = stash_scferror(lcbdata);
goto deltemp;
case SCF_ERROR_INVALID_ARGUMENT: /* caught above */
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_service_add_instance",
scf_error());
}
}
nosnap:
/*
* Create a last-import snapshot to serve as an attachment
* point for the real one from the temporary instance. Since
* the contents is irrelevant, take it now, while the instance
* is empty, to minimize svc.configd's work.
*/
if (_scf_snapshot_take_new(imp_inst, snap_lastimport,
imp_lisnap) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
goto connaborted;
case SCF_ERROR_NO_RESOURCES:
r = stash_scferror(lcbdata);
goto deltemp;
case SCF_ERROR_EXISTS:
warn(gettext("%s changed unexpectedly "
"(snapshot \"%s\" added).\n"),
inst->sc_fmri, snap_lastimport);
lcbdata->sc_err = EBUSY;
r = UU_WALK_ERROR;
goto deltemp;
case SCF_ERROR_PERMISSION_DENIED:
warn(gettext("Could not take \"%s\" snapshot "
"of %s (permission denied).\n"),
snap_lastimport, inst->sc_fmri);
r = stash_scferror(lcbdata);
goto deltemp;
default:
scfwarn();
lcbdata->sc_err = -1;
r = UU_WALK_ERROR;
goto deltemp;
case SCF_ERROR_NOT_SET:
case SCF_ERROR_INTERNAL:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
bad_error("_scf_snapshot_take_new",
scf_error());
}
}
if (li_only)
goto lionly;
inst->sc_import_state = IMPORT_PROP_BEGUN;
r = lscf_import_instance_pgs(imp_inst, inst->sc_fmri, inst,
flags);
switch (r) {
case 0:
break;
case ECONNABORTED:
goto connaborted;
case ECANCELED:
warn(gettext("%s changed unexpectedly "
"(instance \"%s\" deleted).\n"),
inst->sc_parent->sc_fmri, inst->sc_name);
lcbdata->sc_err = EBUSY;
r = UU_WALK_ERROR;
goto deltemp;
case EEXIST:
warn(gettext("%s changed unexpectedly "
"(property group added).\n"), inst->sc_fmri);
lcbdata->sc_err = EBUSY;
r = UU_WALK_ERROR;
goto deltemp;
default:
lcbdata->sc_err = r;
r = UU_WALK_ERROR;
goto deltemp;
case EINVAL: /* caught above */
bad_error("lscf_import_instance_pgs", r);
}
ctx.sc_parent = imp_inst;
ctx.sc_service = 0;
ctx.sc_trans = NULL;
ctx.sc_flags = 0;
if (uu_list_walk(inst->sc_dependents, lscf_dependent_import,
&ctx, UU_DEFAULT) != 0) {
if (uu_error() != UU_ERROR_CALLBACK_FAILED)
bad_error("uu_list_walk", uu_error());
if (ctx.sc_err == ECONNABORTED)
goto connaborted;
lcbdata->sc_err = ctx.sc_err;
r = UU_WALK_ERROR;
goto deltemp;
}
inst->sc_import_state = IMPORT_PROP_DONE;
if (g_verbose)
warn(gettext("Taking \"%s\" snapshot for %s.\n"),
snap_initial, inst->sc_fmri);
r = take_snap(imp_inst, snap_initial, imp_snap);
switch (r) {
case 0:
break;
case ECONNABORTED:
goto connaborted;
case ENOSPC:
case -1:
lcbdata->sc_err = r;
r = UU_WALK_ERROR;
goto deltemp;
case ECANCELED:
warn(gettext("%s changed unexpectedly "
"(instance %s deleted).\n"),
inst->sc_parent->sc_fmri, inst->sc_name);
lcbdata->sc_err = r;
r = UU_WALK_ERROR;
goto deltemp;
case EPERM:
warn(emsg_snap_perm, snap_initial, inst->sc_fmri);
lcbdata->sc_err = r;
r = UU_WALK_ERROR;
goto deltemp;
default:
bad_error("take_snap", r);
}
}
lionly:
if (lcbdata->sc_flags & SCI_NOSNAP)
goto deltemp;
/* transfer snapshot from temporary instance */
if (g_verbose)
warn(gettext("Taking \"%s\" snapshot for %s.\n"),
snap_lastimport, inst->sc_fmri);
if (_scf_snapshot_attach(imp_tlisnap, imp_lisnap) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
goto connaborted;
case SCF_ERROR_NO_RESOURCES:
r = stash_scferror(lcbdata);
goto deltemp;
case SCF_ERROR_PERMISSION_DENIED:
warn(gettext("Could not take \"%s\" snapshot for %s "
"(permission denied).\n"), snap_lastimport,
inst->sc_fmri);
r = stash_scferror(lcbdata);
goto deltemp;
case SCF_ERROR_NOT_SET:
case SCF_ERROR_HANDLE_MISMATCH:
default:
bad_error("_scf_snapshot_attach", scf_error());
}
}
inst->sc_import_state = IMPORT_COMPLETE;
r = UU_WALK_NEXT;
deltemp:
/* delete temporary instance */
if (scf_instance_delete(imp_tinst) != 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
break;
case SCF_ERROR_CONNECTION_BROKEN:
goto connaborted;
case SCF_ERROR_NOT_SET:
case SCF_ERROR_NOT_BOUND:
default:
bad_error("scf_instance_delete", scf_error());
}
}
return (r);
connaborted:
warn(gettext("Could not delete svc:/%s:%s "
"(repository connection broken).\n"), imp_tsname, inst->sc_name);
lcbdata->sc_err = ECONNABORTED;
return (UU_WALK_ERROR);
}
/*
* When an instance is imported we end up telling configd about it. Once we tell
* configd about these changes, startd eventually notices. If this is a new
* instance, the manifest may not specify the SCF_PG_RESTARTER (restarter)
* property group. However, many of the other tools expect that this property
* group exists and has certain values.
*
* These values are added asynchronously by startd. We should not return from
* this routine until we can verify that the property group we need is there.
*
* Before we go ahead and verify this, we have to ask ourselves an important
* question: Is the early manifest service currently running? Because if it is
* running and it has invoked us, then the service will never get a restarter
* property because svc.startd is blocked on EMI finishing before it lets itself
* fully connect to svc.configd. Of course, this means that this race condition
* is in fact impossible to 100% eliminate.
*
* svc.startd makes sure that EMI only runs once and has succeeded by checking
* the state of the EMI instance. If it is online it bails out and makes sure
* that it doesn't run again. In this case, we're going to do something similar,
* only if the state is online, then we're going to actually verify. EMI always
* has to be present, but it can be explicitly disabled to reduce the amount of
* damage it can cause. If EMI has been disabled then we no longer have to worry
* about the implicit race condition and can go ahead and check things. If EMI
* is in some state that isn't online or disabled and isn't runinng, then we
* assume that things are rather bad and we're not going to get in your way,
* even if the rest of SMF does.
*
* Returns 0 on success or returns an errno.
*/
#ifndef NATIVE_BUILD
static int
lscf_instance_verify(scf_scope_t *scope, entity_t *svc, entity_t *inst)
{
int ret, err;
struct timespec ts;
char *emi_state;
/*
* smf_get_state does not distinguish between its different failure
* modes: memory allocation failures, SMF internal failures, and a lack
* of EMI entirely because it's been removed. In these cases, we're
* going to be conservative and opt to say that if we don't know, better
* to not block import or falsely warn to the user.
*/
if ((emi_state = smf_get_state(SCF_INSTANCE_EMI)) == NULL) {
return (0);
}
/*
* As per the block comment for this function check the state of EMI
*/
if (strcmp(emi_state, SCF_STATE_STRING_ONLINE) != 0 &&
strcmp(emi_state, SCF_STATE_STRING_DISABLED) != 0) {
warn(gettext("Not validating instance %s:%s because EMI's "
"state is %s\n"), svc->sc_name, inst->sc_name, emi_state);
free(emi_state);
return (0);
}
free(emi_state);
/*
* First we have to get the property.
*/
if ((ret = scf_scope_get_service(scope, svc->sc_name, imp_svc)) != 0) {
ret = scf_error();
warn(gettext("Failed to look up service: %s\n"), svc->sc_name);
return (ret);
}
/*
* We should always be able to get the instance. It should already
* exist because we just created it or got it. There probably is a
* slim chance that someone may have come in and deleted it though from
* under us.
*/
if ((ret = scf_service_get_instance(imp_svc, inst->sc_name, imp_inst))
!= 0) {
ret = scf_error();
warn(gettext("Failed to verify instance: %s\n"), inst->sc_name);
switch (ret) {
case SCF_ERROR_DELETED:
err = ENODEV;
break;
case SCF_ERROR_CONNECTION_BROKEN:
warn(gettext("Lost repository connection\n"));
err = ECONNABORTED;
break;
case SCF_ERROR_NOT_FOUND:
warn(gettext("Instance \"%s\" disappeared out from "
"under us.\n"), inst->sc_name);
err = ENOENT;
break;
default:
bad_error("scf_service_get_instance", ret);
}
return (err);
}
/*
* An astute observer may want to use _scf_wait_pg which would notify us
* of a property group change, unfortunately that does not work if the
* property group in question does not exist. So instead we have to
* manually poll and ask smf the best way to get to it.
*/
while ((ret = scf_instance_get_pg(imp_inst, SCF_PG_RESTARTER, imp_pg))
!= SCF_SUCCESS) {
ret = scf_error();
if (ret != SCF_ERROR_NOT_FOUND) {
warn(gettext("Failed to get restarter property "
"group for instance: %s\n"), inst->sc_name);
switch (ret) {
case SCF_ERROR_DELETED:
err = ENODEV;
break;
case SCF_ERROR_CONNECTION_BROKEN:
warn(gettext("Lost repository connection\n"));
err = ECONNABORTED;
break;
default:
bad_error("scf_service_get_instance", ret);
}
return (err);
}
ts.tv_sec = pg_timeout / NANOSEC;
ts.tv_nsec = pg_timeout % NANOSEC;
(void) nanosleep(&ts, NULL);
}
/*
* svcadm also expects that the SCF_PROPERTY_STATE property is present.
* So in addition to the property group being present, we need to wait
* for the property to be there in some form.
*
* Note that a property group is a frozen snapshot in time. To properly
* get beyond this, you have to refresh the property group each time.
*/
while ((ret = scf_pg_get_property(imp_pg, SCF_PROPERTY_STATE,
imp_prop)) != 0) {
ret = scf_error();
if (ret != SCF_ERROR_NOT_FOUND) {
warn(gettext("Failed to get property %s from the "
"restarter property group of instance %s\n"),
SCF_PROPERTY_STATE, inst->sc_name);
switch (ret) {
case SCF_ERROR_CONNECTION_BROKEN:
warn(gettext("Lost repository connection\n"));
err = ECONNABORTED;
break;
case SCF_ERROR_DELETED:
err = ENODEV;
break;
default:
bad_error("scf_pg_get_property", ret);
}
return (err);
}
ts.tv_sec = pg_timeout / NANOSEC;
ts.tv_nsec = pg_timeout % NANOSEC;
(void) nanosleep(&ts, NULL);
ret = scf_instance_get_pg(imp_inst, SCF_PG_RESTARTER, imp_pg);
if (ret != SCF_SUCCESS) {
warn(gettext("Failed to get restarter property "
"group for instance: %s\n"), inst->sc_name);
switch (ret) {
case SCF_ERROR_DELETED:
err = ENODEV;
break;
case SCF_ERROR_CONNECTION_BROKEN:
warn(gettext("Lost repository connection\n"));
err = ECONNABORTED;
break;
default:
bad_error("scf_service_get_instance", ret);
}
return (err);
}
}
/*
* We don't have to free the property groups or other values that we got
* because we stored them in global variables that are allocated and
* freed by the routines that call into these functions. Unless of
* course the rest of the code here that we are basing this on is
* mistaken.
*/
return (0);
}
#endif
/*
* If the service is missing, create it, import its properties, and import the
* instances. Since the service is brand new, it should be empty, and if we
* run into any existing entities (SCF_ERROR_EXISTS), abort.
*
* If the service exists, we want to upgrade its properties and import the
* instances. Upgrade requires a last-import snapshot, though, which are
* children of instances, so first we'll have to go through the instances
* looking for a last-import snapshot. If we don't find one then we'll just
* override-import the service properties (but don't delete existing
* properties: another service might have declared us as a dependent). Before
* we change anything, though, we want to take the previous snapshots. We
* also give lscf_instance_import() a leg up on taking last-import snapshots
* by importing the manifest's service properties into a temporary service.
*
* On success, returns UU_WALK_NEXT. On failure, returns UU_WALK_ERROR and
* sets lcbdata->sc_err to
* ECONNABORTED - repository connection broken
* ENOMEM - out of memory
* ENOSPC - svc.configd is out of resources
* EPERM - couldn't create temporary service (error printed)
* - couldn't import into temp service (error printed)
* - couldn't create service (error printed)
* - couldn't import dependent (error printed)
* - couldn't take snapshot (error printed)
* - couldn't create instance (error printed)
* - couldn't create, modify, or delete pg (error printed)
* - couldn't create, modify, or delete dependent (error printed)
* - couldn't import instance (error printed)
* EROFS - couldn't create temporary service (repository read-only)
* - couldn't import into temporary service (repository read-only)
* - couldn't create service (repository read-only)
* - couldn't import dependent (repository read-only)
* - couldn't create instance (repository read-only)
* - couldn't create, modify, or delete pg or dependent
* - couldn't import instance (repository read-only)
* EACCES - couldn't create temporary service (backend access denied)
* - couldn't import into temporary service (backend access denied)
* - couldn't create service (backend access denied)
* - couldn't import dependent (backend access denied)
* - couldn't create instance (backend access denied)
* - couldn't create, modify, or delete pg or dependent
* - couldn't import instance (backend access denied)
* EINVAL - service name is invalid (error printed)
* - service name is too long (error printed)
* - s has invalid pgroup (error printed)
* - s has invalid dependent (error printed)
* - instance name is invalid (error printed)
* - instance entity_t is invalid (error printed)
* EEXIST - couldn't create temporary service (already exists) (error printed)
* - couldn't import dependent (dependency pg already exists) (printed)
* - dependency collision in dependent service (error printed)
* EBUSY - temporary service deleted (error printed)
* - property group added to temporary service (error printed)
* - new property group changed or was deleted (error printed)
* - service was added unexpectedly (error printed)
* - service was deleted unexpectedly (error printed)
* - property group added to new service (error printed)
* - instance added unexpectedly (error printed)
* - instance deleted unexpectedly (error printed)
* - dependent service deleted unexpectedly (error printed)
* - pg was added, changed, or deleted (error printed)
* - dependent pg changed (error printed)
* - temporary instance added, changed, or deleted (error printed)
* EBADF - a last-import snapshot is corrupt (error printed)
* - the service is corrupt (error printed)
* - a dependent is corrupt (error printed)
* - an instance is corrupt (error printed)
* - an instance has a corrupt last-import snapshot (error printed)
* - dependent target has a corrupt snapshot (error printed)
* -1 - unknown libscf error (error printed)
*/
static int
lscf_service_import(void *v, void *pvt)
{
entity_t *s = v;
scf_callback_t cbdata;
scf_callback_t *lcbdata = pvt;
scf_scope_t *scope = lcbdata->sc_parent;
entity_t *inst, linst;
int r;
int fresh = 0;
scf_snaplevel_t *running;
int have_ge = 0;
boolean_t retried = B_FALSE;
const char * const ts_deleted = gettext("Temporary service svc:/%s "
"was deleted unexpectedly.\n");
const char * const ts_pg_added = gettext("Temporary service svc:/%s "
"changed unexpectedly (property group added).\n");
const char * const s_deleted =
gettext("%s was deleted unexpectedly.\n");
const char * const i_deleted =
gettext("%s changed unexpectedly (instance \"%s\" deleted).\n");
const char * const badsnap = gettext("\"%s\" snapshot of svc:/%s:%s "
"is corrupt (missing service snaplevel).\n");
const char * const s_mfile_upd =
gettext("Unable to update the manifest file connection "
"for %s\n");
li_only = 0;
/* Validate the service name */
if (scf_scope_get_service(scope, s->sc_name, imp_svc) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
return (stash_scferror(lcbdata));
case SCF_ERROR_INVALID_ARGUMENT:
warn(gettext("\"%s\" is an invalid service name. "
"Cannot import.\n"), s->sc_name);
return (stash_scferror(lcbdata));
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_scope_get_service", scf_error());
}
}
/* create temporary service */
/*
* the size of the buffer was reduced to max_scf_name_len to prevent
* hitting bug 6681151. After the bug fix, the size of the buffer
* should be restored to its original value (max_scf_name_len +1)
*/
r = snprintf(imp_tsname, max_scf_name_len, "TEMP/%s", s->sc_name);
if (r < 0)
bad_error("snprintf", errno);
if (r > max_scf_name_len) {
warn(gettext(
"Service name \"%s\" is too long. Cannot import.\n"),
s->sc_name);
lcbdata->sc_err = EINVAL;
return (UU_WALK_ERROR);
}
retry:
if (scf_scope_add_service(imp_scope, imp_tsname, imp_tsvc) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_NO_RESOURCES:
case SCF_ERROR_BACKEND_READONLY:
case SCF_ERROR_BACKEND_ACCESS:
return (stash_scferror(lcbdata));
case SCF_ERROR_EXISTS:
if (!retried) {
lscf_delete(imp_tsname, 0);
retried = B_TRUE;
goto retry;
}
warn(gettext(
"Temporary service \"%s\" must be deleted before "
"this manifest can be imported.\n"), imp_tsname);
return (stash_scferror(lcbdata));
case SCF_ERROR_PERMISSION_DENIED:
warn(gettext("Could not create temporary service "
"\"%s\" (permission denied).\n"), imp_tsname);
return (stash_scferror(lcbdata));
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_scope_add_service", scf_error());
}
}
r = snprintf(imp_str, imp_str_sz, "svc:/%s", imp_tsname);
if (r < 0)
bad_error("snprintf", errno);
cbdata.sc_handle = lcbdata->sc_handle;
cbdata.sc_parent = imp_tsvc;
cbdata.sc_service = 1;
cbdata.sc_source_fmri = s->sc_fmri;
cbdata.sc_target_fmri = imp_str;
cbdata.sc_flags = 0;
if (uu_list_walk(s->sc_pgroups, entity_pgroup_import, &cbdata,
UU_DEFAULT) != 0) {
if (uu_error() != UU_ERROR_CALLBACK_FAILED)
bad_error("uu_list_walk", uu_error());
lcbdata->sc_err = cbdata.sc_err;
switch (cbdata.sc_err) {
case ECONNABORTED:
goto connaborted;
case ECANCELED:
warn(ts_deleted, imp_tsname);
lcbdata->sc_err = EBUSY;
return (UU_WALK_ERROR);
case EEXIST:
warn(ts_pg_added, imp_tsname);
lcbdata->sc_err = EBUSY;
return (UU_WALK_ERROR);
}
r = UU_WALK_ERROR;
goto deltemp;
}
if (uu_list_walk(s->sc_dependents, entity_pgroup_import, &cbdata,
UU_DEFAULT) != 0) {
if (uu_error() != UU_ERROR_CALLBACK_FAILED)
bad_error("uu_list_walk", uu_error());
lcbdata->sc_err = cbdata.sc_err;
switch (cbdata.sc_err) {
case ECONNABORTED:
goto connaborted;
case ECANCELED:
warn(ts_deleted, imp_tsname);
lcbdata->sc_err = EBUSY;
return (UU_WALK_ERROR);
case EEXIST:
warn(ts_pg_added, imp_tsname);
lcbdata->sc_err = EBUSY;
return (UU_WALK_ERROR);
}
r = UU_WALK_ERROR;
goto deltemp;
}
if (scf_scope_get_service(scope, s->sc_name, imp_svc) != 0) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_CONNECTION_BROKEN:
goto connaborted;
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_scope_get_service", scf_error());
}
if (scf_scope_add_service(scope, s->sc_name, imp_svc) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
goto connaborted;
case SCF_ERROR_NO_RESOURCES:
case SCF_ERROR_BACKEND_READONLY:
case SCF_ERROR_BACKEND_ACCESS:
r = stash_scferror(lcbdata);
goto deltemp;
case SCF_ERROR_EXISTS:
warn(gettext("Scope \"%s\" changed unexpectedly"
" (service \"%s\" added).\n"),
SCF_SCOPE_LOCAL, s->sc_name);
lcbdata->sc_err = EBUSY;
goto deltemp;
case SCF_ERROR_PERMISSION_DENIED:
warn(gettext("Could not create service \"%s\" "
"(permission denied).\n"), s->sc_name);
goto deltemp;
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_scope_add_service", scf_error());
}
}
s->sc_import_state = IMPORT_PROP_BEGUN;
/* import service properties */
cbdata.sc_handle = lcbdata->sc_handle;
cbdata.sc_parent = imp_svc;
cbdata.sc_service = 1;
cbdata.sc_flags = lcbdata->sc_flags;
cbdata.sc_source_fmri = s->sc_fmri;
cbdata.sc_target_fmri = s->sc_fmri;
if (uu_list_walk(s->sc_pgroups, entity_pgroup_import,
&cbdata, UU_DEFAULT) != 0) {
if (uu_error() != UU_ERROR_CALLBACK_FAILED)
bad_error("uu_list_walk", uu_error());
lcbdata->sc_err = cbdata.sc_err;
switch (cbdata.sc_err) {
case ECONNABORTED:
goto connaborted;
case ECANCELED:
warn(s_deleted, s->sc_fmri);
lcbdata->sc_err = EBUSY;
return (UU_WALK_ERROR);
case EEXIST:
warn(gettext("%s changed unexpectedly "
"(property group added).\n"), s->sc_fmri);
lcbdata->sc_err = EBUSY;
return (UU_WALK_ERROR);
case EINVAL:
/* caught above */
bad_error("entity_pgroup_import",
cbdata.sc_err);
}
r = UU_WALK_ERROR;
goto deltemp;
}
cbdata.sc_trans = NULL;
cbdata.sc_flags = 0;
if (uu_list_walk(s->sc_dependents, lscf_dependent_import,
&cbdata, UU_DEFAULT) != 0) {
if (uu_error() != UU_ERROR_CALLBACK_FAILED)
bad_error("uu_list_walk", uu_error());
lcbdata->sc_err = cbdata.sc_err;
if (cbdata.sc_err == ECONNABORTED)
goto connaborted;
r = UU_WALK_ERROR;
goto deltemp;
}
s->sc_import_state = IMPORT_PROP_DONE;
/*
* This is a new service, so we can't take previous snapshots
* or upgrade service properties.
*/
fresh = 1;
goto instances;
}
/* Clear sc_seen for the instances. */
if (uu_list_walk(s->sc_u.sc_service.sc_service_instances, clear_int,
(void *)offsetof(entity_t, sc_seen), UU_DEFAULT) != 0)
bad_error("uu_list_walk", uu_error());
/*
* Take previous snapshots for all instances. Even for ones not
* mentioned in the bundle, since we might change their service
* properties.
*/
if (scf_iter_service_instances(imp_iter, imp_svc) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
goto connaborted;
case SCF_ERROR_DELETED:
warn(s_deleted, s->sc_fmri);
lcbdata->sc_err = EBUSY;
r = UU_WALK_ERROR;
goto deltemp;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_iter_service_instances", scf_error());
}
}
for (;;) {
r = scf_iter_next_instance(imp_iter, imp_inst);
if (r == 0)
break;
if (r != 1) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
warn(s_deleted, s->sc_fmri);
lcbdata->sc_err = EBUSY;
r = UU_WALK_ERROR;
goto deltemp;
case SCF_ERROR_CONNECTION_BROKEN:
goto connaborted;
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_iter_next_instance",
scf_error());
}
}
if (scf_instance_get_name(imp_inst, imp_str, imp_str_sz) < 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
continue;
case SCF_ERROR_CONNECTION_BROKEN:
goto connaborted;
case SCF_ERROR_NOT_SET:
case SCF_ERROR_NOT_BOUND:
default:
bad_error("scf_instance_get_name", scf_error());
}
}
if (g_verbose)
warn(gettext(
"Taking \"%s\" snapshot for svc:/%s:%s.\n"),
snap_previous, s->sc_name, imp_str);
r = take_snap(imp_inst, snap_previous, imp_snap);
switch (r) {
case 0:
break;
case ECANCELED:
continue;
case ECONNABORTED:
goto connaborted;
case EPERM:
warn(gettext("Could not take \"%s\" snapshot of "
"svc:/%s:%s (permission denied).\n"),
snap_previous, s->sc_name, imp_str);
lcbdata->sc_err = r;
return (UU_WALK_ERROR);
case ENOSPC:
case -1:
lcbdata->sc_err = r;
r = UU_WALK_ERROR;
goto deltemp;
default:
bad_error("take_snap", r);
}
linst.sc_name = imp_str;
inst = uu_list_find(s->sc_u.sc_service.sc_service_instances,
&linst, NULL, NULL);
if (inst != NULL) {
inst->sc_import_state = IMPORT_PREVIOUS;
inst->sc_seen = 1;
}
}
/*
* Create the new instances and take previous snapshots of
* them. This is not necessary, but it maximizes data preservation.
*/
for (inst = uu_list_first(s->sc_u.sc_service.sc_service_instances);
inst != NULL;
inst = uu_list_next(s->sc_u.sc_service.sc_service_instances,
inst)) {
if (inst->sc_seen)
continue;
if (scf_service_add_instance(imp_svc, inst->sc_name,
imp_inst) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
goto connaborted;
case SCF_ERROR_BACKEND_READONLY:
case SCF_ERROR_BACKEND_ACCESS:
case SCF_ERROR_NO_RESOURCES:
r = stash_scferror(lcbdata);
goto deltemp;
case SCF_ERROR_EXISTS:
warn(gettext("%s changed unexpectedly "
"(instance \"%s\" added).\n"), s->sc_fmri,
inst->sc_name);
lcbdata->sc_err = EBUSY;
r = UU_WALK_ERROR;
goto deltemp;
case SCF_ERROR_INVALID_ARGUMENT:
warn(gettext("Service \"%s\" has instance with "
"invalid name \"%s\".\n"), s->sc_name,
inst->sc_name);
r = stash_scferror(lcbdata);
goto deltemp;
case SCF_ERROR_PERMISSION_DENIED:
warn(gettext("Could not create instance \"%s\" "
"in %s (permission denied).\n"),
inst->sc_name, s->sc_fmri);
r = stash_scferror(lcbdata);
goto deltemp;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_service_add_instance",
scf_error());
}
}
if (g_verbose)
warn(gettext("Taking \"%s\" snapshot for "
"new service %s.\n"), snap_previous, inst->sc_fmri);
r = take_snap(imp_inst, snap_previous, imp_snap);
switch (r) {
case 0:
break;
case ECANCELED:
warn(i_deleted, s->sc_fmri, inst->sc_name);
lcbdata->sc_err = EBUSY;
r = UU_WALK_ERROR;
goto deltemp;
case ECONNABORTED:
goto connaborted;
case EPERM:
warn(emsg_snap_perm, snap_previous, inst->sc_fmri);
lcbdata->sc_err = r;
r = UU_WALK_ERROR;
goto deltemp;
case ENOSPC:
case -1:
r = UU_WALK_ERROR;
goto deltemp;
default:
bad_error("take_snap", r);
}
}
s->sc_import_state = IMPORT_PREVIOUS;
/*
* Upgrade service properties, if we can find a last-import snapshot.
* Any will do because we don't support different service properties
* in different manifests, so all snaplevels of the service in all of
* the last-import snapshots of the instances should be the same.
*/
if (scf_iter_service_instances(imp_iter, imp_svc) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
goto connaborted;
case SCF_ERROR_DELETED:
warn(s_deleted, s->sc_fmri);
lcbdata->sc_err = EBUSY;
r = UU_WALK_ERROR;
goto deltemp;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_iter_service_instances", scf_error());
}
}
for (;;) {
r = scf_iter_next_instance(imp_iter, imp_inst);
if (r == -1) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
warn(s_deleted, s->sc_fmri);
lcbdata->sc_err = EBUSY;
r = UU_WALK_ERROR;
goto deltemp;
case SCF_ERROR_CONNECTION_BROKEN:
goto connaborted;
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_iter_next_instance",
scf_error());
}
}
if (r == 0) {
/*
* Didn't find any last-import snapshots. Override-
* import the properties. Unless one of the instances
* has a general/enabled property, in which case we're
* probably running a last-import-capable svccfg for
* the first time, and we should only take the
* last-import snapshot.
*/
if (have_ge) {
pgroup_t *mfpg;
scf_callback_t mfcbdata;
li_only = 1;
no_refresh = 1;
/*
* Need to go ahead and import the manifestfiles
* pg if it exists. If the last-import snapshot
* upgrade code is ever removed this code can
* be removed as well.
*/
mfpg = internal_pgroup_find(s,
SCF_PG_MANIFESTFILES, SCF_GROUP_FRAMEWORK);
if (mfpg) {
mfcbdata.sc_handle = g_hndl;
mfcbdata.sc_parent = imp_svc;
mfcbdata.sc_service = 1;
mfcbdata.sc_flags = SCI_FORCE;
mfcbdata.sc_source_fmri = s->sc_fmri;
mfcbdata.sc_target_fmri = s->sc_fmri;
if (entity_pgroup_import(mfpg,
&mfcbdata) != UU_WALK_NEXT) {
warn(s_mfile_upd, s->sc_fmri);
r = UU_WALK_ERROR;
goto deltemp;
}
}
break;
}
s->sc_import_state = IMPORT_PROP_BEGUN;
cbdata.sc_handle = g_hndl;
cbdata.sc_parent = imp_svc;
cbdata.sc_service = 1;
cbdata.sc_flags = SCI_FORCE;
cbdata.sc_source_fmri = s->sc_fmri;
cbdata.sc_target_fmri = s->sc_fmri;
if (uu_list_walk(s->sc_pgroups, entity_pgroup_import,
&cbdata, UU_DEFAULT) != 0) {
if (uu_error() != UU_ERROR_CALLBACK_FAILED)
bad_error("uu_list_walk", uu_error());
lcbdata->sc_err = cbdata.sc_err;
switch (cbdata.sc_err) {
case ECONNABORTED:
goto connaborted;
case ECANCELED:
warn(s_deleted, s->sc_fmri);
lcbdata->sc_err = EBUSY;
break;
case EINVAL: /* caught above */
case EEXIST:
bad_error("entity_pgroup_import",
cbdata.sc_err);
}
r = UU_WALK_ERROR;
goto deltemp;
}
cbdata.sc_trans = NULL;
cbdata.sc_flags = 0;
if (uu_list_walk(s->sc_dependents,
lscf_dependent_import, &cbdata, UU_DEFAULT) != 0) {
if (uu_error() != UU_ERROR_CALLBACK_FAILED)
bad_error("uu_list_walk", uu_error());
lcbdata->sc_err = cbdata.sc_err;
if (cbdata.sc_err == ECONNABORTED)
goto connaborted;
r = UU_WALK_ERROR;
goto deltemp;
}
break;
}
if (scf_instance_get_snapshot(imp_inst, snap_lastimport,
imp_snap) != 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
continue;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_CONNECTION_BROKEN:
goto connaborted;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_instance_get_snapshot",
scf_error());
}
if (have_ge)
continue;
/*
* Check for a general/enabled property. This is how
* we tell whether to import if there turn out to be
* no last-import snapshots.
*/
if (scf_instance_get_pg(imp_inst, SCF_PG_GENERAL,
imp_pg) == 0) {
if (scf_pg_get_property(imp_pg,
SCF_PROPERTY_ENABLED, imp_prop) == 0) {
have_ge = 1;
} else {
switch (scf_error()) {
case SCF_ERROR_DELETED:
case SCF_ERROR_NOT_FOUND:
continue;
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_pg_get_property",
scf_error());
}
}
} else {
switch (scf_error()) {
case SCF_ERROR_DELETED:
case SCF_ERROR_NOT_FOUND:
continue;
case SCF_ERROR_CONNECTION_BROKEN:
goto connaborted;
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
default:
bad_error("scf_instance_get_pg",
scf_error());
}
}
continue;
}
/* find service snaplevel */
r = get_snaplevel(imp_snap, 1, imp_snpl);
switch (r) {
case 0:
break;
case ECONNABORTED:
goto connaborted;
case ECANCELED:
continue;
case ENOENT:
if (scf_instance_get_name(imp_inst, imp_str,
imp_str_sz) < 0)
(void) strcpy(imp_str, "?");
warn(badsnap, snap_lastimport, s->sc_name, imp_str);
lcbdata->sc_err = EBADF;
r = UU_WALK_ERROR;
goto deltemp;
default:
bad_error("get_snaplevel", r);
}
if (scf_instance_get_snapshot(imp_inst, snap_running,
imp_rsnap) != 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
continue;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_CONNECTION_BROKEN:
goto connaborted;
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_instance_get_snapshot",
scf_error());
}
running = NULL;
} else {
r = get_snaplevel(imp_rsnap, 1, imp_rsnpl);
switch (r) {
case 0:
running = imp_rsnpl;
break;
case ECONNABORTED:
goto connaborted;
case ECANCELED:
continue;
case ENOENT:
if (scf_instance_get_name(imp_inst, imp_str,
imp_str_sz) < 0)
(void) strcpy(imp_str, "?");
warn(badsnap, snap_running, s->sc_name,
imp_str);
lcbdata->sc_err = EBADF;
r = UU_WALK_ERROR;
goto deltemp;
default:
bad_error("get_snaplevel", r);
}
}
if (g_verbose) {
if (scf_instance_get_name(imp_inst, imp_str,
imp_str_sz) < 0)
(void) strcpy(imp_str, "?");
warn(gettext("Upgrading properties of %s according to "
"instance \"%s\".\n"), s->sc_fmri, imp_str);
}
/* upgrade service properties */
r = upgrade_props(imp_svc, running, imp_snpl, s);
if (r == 0)
break;
switch (r) {
case ECONNABORTED:
goto connaborted;
case ECANCELED:
warn(s_deleted, s->sc_fmri);
lcbdata->sc_err = EBUSY;
break;
case ENODEV:
if (scf_instance_get_name(imp_inst, imp_str,
imp_str_sz) < 0)
(void) strcpy(imp_str, "?");
warn(i_deleted, s->sc_fmri, imp_str);
lcbdata->sc_err = EBUSY;
break;
default:
lcbdata->sc_err = r;
}
r = UU_WALK_ERROR;
goto deltemp;
}
s->sc_import_state = IMPORT_PROP_DONE;
instances:
/* import instances */
cbdata.sc_handle = lcbdata->sc_handle;
cbdata.sc_parent = imp_svc;
cbdata.sc_service = 1;
cbdata.sc_flags = lcbdata->sc_flags | (fresh ? SCI_FRESH : 0);
cbdata.sc_general = NULL;
if (uu_list_walk(s->sc_u.sc_service.sc_service_instances,
lscf_instance_import, &cbdata, UU_DEFAULT) != 0) {
if (uu_error() != UU_ERROR_CALLBACK_FAILED)
bad_error("uu_list_walk", uu_error());
lcbdata->sc_err = cbdata.sc_err;
if (cbdata.sc_err == ECONNABORTED)
goto connaborted;
r = UU_WALK_ERROR;
goto deltemp;
}
s->sc_import_state = IMPORT_COMPLETE;
r = UU_WALK_NEXT;
deltemp:
/* delete temporary service */
if (scf_service_delete(imp_tsvc) != 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
break;
case SCF_ERROR_CONNECTION_BROKEN:
goto connaborted;
case SCF_ERROR_EXISTS:
warn(gettext(
"Could not delete svc:/%s (instances exist).\n"),
imp_tsname);
break;
case SCF_ERROR_NOT_SET:
case SCF_ERROR_NOT_BOUND:
default:
bad_error("scf_service_delete", scf_error());
}
}
return (r);
connaborted:
warn(gettext("Could not delete svc:/%s "
"(repository connection broken).\n"), imp_tsname);
lcbdata->sc_err = ECONNABORTED;
return (UU_WALK_ERROR);
}
static const char *
import_progress(int st)
{
switch (st) {
case 0:
return (gettext("not reached."));
case IMPORT_PREVIOUS:
return (gettext("previous snapshot taken."));
case IMPORT_PROP_BEGUN:
return (gettext("some properties imported."));
case IMPORT_PROP_DONE:
return (gettext("properties imported."));
case IMPORT_COMPLETE:
return (gettext("imported."));
case IMPORT_REFRESHED:
return (gettext("refresh requested."));
default:
#ifndef NDEBUG
(void) fprintf(stderr, "%s:%d: Unknown entity state %d.\n",
__FILE__, __LINE__, st);
#endif
abort();
/* NOTREACHED */
}
}
/*
* Returns
* 0 - success
* - fmri wasn't found (error printed)
* - entity was deleted (error printed)
* - backend denied access (error printed)
* ENOMEM - out of memory (error printed)
* ECONNABORTED - repository connection broken (error printed)
* EPERM - permission denied (error printed)
* -1 - unknown libscf error (error printed)
*/
static int
imp_refresh_fmri(const char *fmri, const char *name, const char *d_fmri)
{
scf_error_t serr;
void *ent;
int issvc;
int r;
const char *deleted = gettext("Could not refresh %s (deleted).\n");
const char *dpt_deleted = gettext("Could not refresh %s "
"(dependent \"%s\" of %s) (deleted).\n");
serr = fmri_to_entity(g_hndl, fmri, &ent, &issvc);
switch (serr) {
case SCF_ERROR_NONE:
break;
case SCF_ERROR_NO_MEMORY:
if (name == NULL)
warn(gettext("Could not refresh %s (out of memory).\n"),
fmri);
else
warn(gettext("Could not refresh %s "
"(dependent \"%s\" of %s) (out of memory).\n"),
fmri, name, d_fmri);
return (ENOMEM);
case SCF_ERROR_NOT_FOUND:
if (name == NULL)
warn(deleted, fmri);
else
warn(dpt_deleted, fmri, name, d_fmri);
return (0);
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_CONSTRAINT_VIOLATED:
default:
bad_error("fmri_to_entity", serr);
}
r = refresh_entity(issvc, ent, fmri, imp_inst, imp_iter, imp_str);
switch (r) {
case 0:
break;
case ECONNABORTED:
if (name != NULL)
warn(gettext("Could not refresh %s "
"(dependent \"%s\" of %s) "
"(repository connection broken).\n"), fmri, name,
d_fmri);
return (r);
case ECANCELED:
if (name == NULL)
warn(deleted, fmri);
else
warn(dpt_deleted, fmri, name, d_fmri);
return (0);
case EACCES:
if (!g_verbose)
return (0);
if (name == NULL)
warn(gettext("Could not refresh %s "
"(backend access denied).\n"), fmri);
else
warn(gettext("Could not refresh %s "
"(dependent \"%s\" of %s) "
"(backend access denied).\n"), fmri, name, d_fmri);
return (0);
case EPERM:
if (name == NULL)
warn(gettext("Could not refresh %s "
"(permission denied).\n"), fmri);
else
warn(gettext("Could not refresh %s "
"(dependent \"%s\" of %s) "
"(permission denied).\n"), fmri, name, d_fmri);
return (r);
case ENOSPC:
if (name == NULL)
warn(gettext("Could not refresh %s "
"(repository server out of resources).\n"),
fmri);
else
warn(gettext("Could not refresh %s "
"(dependent \"%s\" of %s) "
"(repository server out of resources).\n"),
fmri, name, d_fmri);
return (r);
case -1:
scfwarn();
return (r);
default:
bad_error("refresh_entity", r);
}
if (issvc)
scf_service_destroy(ent);
else
scf_instance_destroy(ent);
return (0);
}
static int
alloc_imp_globals()
{
int r;
const char * const emsg_nomem = gettext("Out of memory.\n");
const char * const emsg_nores =
gettext("svc.configd is out of resources.\n");
imp_str_sz = ((max_scf_name_len > max_scf_fmri_len) ?
max_scf_name_len : max_scf_fmri_len) + 1;
if ((imp_scope = scf_scope_create(g_hndl)) == NULL ||
(imp_svc = scf_service_create(g_hndl)) == NULL ||
(imp_tsvc = scf_service_create(g_hndl)) == NULL ||
(imp_inst = scf_instance_create(g_hndl)) == NULL ||
(imp_tinst = scf_instance_create(g_hndl)) == NULL ||
(imp_snap = scf_snapshot_create(g_hndl)) == NULL ||
(imp_lisnap = scf_snapshot_create(g_hndl)) == NULL ||
(imp_tlisnap = scf_snapshot_create(g_hndl)) == NULL ||
(imp_rsnap = scf_snapshot_create(g_hndl)) == NULL ||
(imp_snpl = scf_snaplevel_create(g_hndl)) == NULL ||
(imp_rsnpl = scf_snaplevel_create(g_hndl)) == NULL ||
(imp_pg = scf_pg_create(g_hndl)) == NULL ||
(imp_pg2 = scf_pg_create(g_hndl)) == NULL ||
(imp_prop = scf_property_create(g_hndl)) == NULL ||
(imp_iter = scf_iter_create(g_hndl)) == NULL ||
(imp_rpg_iter = scf_iter_create(g_hndl)) == NULL ||
(imp_up_iter = scf_iter_create(g_hndl)) == NULL ||
(imp_tx = scf_transaction_create(g_hndl)) == NULL ||
(imp_str = malloc(imp_str_sz)) == NULL ||
(imp_tsname = malloc(max_scf_name_len + 1)) == NULL ||
(imp_fe1 = malloc(max_scf_fmri_len + 1)) == NULL ||
(imp_fe2 = malloc(max_scf_fmri_len + 1)) == NULL ||
(imp_deleted_dpts = uu_list_create(string_pool, NULL, 0)) == NULL ||
(ud_inst = scf_instance_create(g_hndl)) == NULL ||
(ud_snpl = scf_snaplevel_create(g_hndl)) == NULL ||
(ud_pg = scf_pg_create(g_hndl)) == NULL ||
(ud_cur_depts_pg = scf_pg_create(g_hndl)) == NULL ||
(ud_run_dpts_pg = scf_pg_create(g_hndl)) == NULL ||
(ud_prop = scf_property_create(g_hndl)) == NULL ||
(ud_dpt_prop = scf_property_create(g_hndl)) == NULL ||
(ud_val = scf_value_create(g_hndl)) == NULL ||
(ud_iter = scf_iter_create(g_hndl)) == NULL ||
(ud_iter2 = scf_iter_create(g_hndl)) == NULL ||
(ud_tx = scf_transaction_create(g_hndl)) == NULL ||
(ud_ctarg = malloc(max_scf_value_len + 1)) == NULL ||
(ud_oldtarg = malloc(max_scf_value_len + 1)) == NULL ||
(ud_name = malloc(max_scf_name_len + 1)) == NULL) {
if (scf_error() == SCF_ERROR_NO_RESOURCES)
warn(emsg_nores);
else
warn(emsg_nomem);
return (-1);
}
r = load_init();
switch (r) {
case 0:
break;
case ENOMEM:
warn(emsg_nomem);
return (-1);
default:
bad_error("load_init", r);
}
return (0);
}
static void
free_imp_globals()
{
pgroup_t *old_dpt;
void *cookie;
load_fini();
free(ud_ctarg);
free(ud_oldtarg);
free(ud_name);
ud_ctarg = ud_oldtarg = ud_name = NULL;
scf_transaction_destroy(ud_tx);
ud_tx = NULL;
scf_iter_destroy(ud_iter);
scf_iter_destroy(ud_iter2);
ud_iter = ud_iter2 = NULL;
scf_value_destroy(ud_val);
ud_val = NULL;
scf_property_destroy(ud_prop);
scf_property_destroy(ud_dpt_prop);
ud_prop = ud_dpt_prop = NULL;
scf_pg_destroy(ud_pg);
scf_pg_destroy(ud_cur_depts_pg);
scf_pg_destroy(ud_run_dpts_pg);
ud_pg = ud_cur_depts_pg = ud_run_dpts_pg = NULL;
scf_snaplevel_destroy(ud_snpl);
ud_snpl = NULL;
scf_instance_destroy(ud_inst);
ud_inst = NULL;
free(imp_str);
free(imp_tsname);
free(imp_fe1);
free(imp_fe2);
imp_str = imp_tsname = imp_fe1 = imp_fe2 = NULL;
cookie = NULL;
while ((old_dpt = uu_list_teardown(imp_deleted_dpts, &cookie)) !=
NULL) {
free((char *)old_dpt->sc_pgroup_name);
free((char *)old_dpt->sc_pgroup_fmri);
internal_pgroup_free(old_dpt);
}
uu_list_destroy(imp_deleted_dpts);
scf_transaction_destroy(imp_tx);
imp_tx = NULL;
scf_iter_destroy(imp_iter);
scf_iter_destroy(imp_rpg_iter);
scf_iter_destroy(imp_up_iter);
imp_iter = imp_rpg_iter = imp_up_iter = NULL;
scf_property_destroy(imp_prop);
imp_prop = NULL;
scf_pg_destroy(imp_pg);
scf_pg_destroy(imp_pg2);
imp_pg = imp_pg2 = NULL;
scf_snaplevel_destroy(imp_snpl);
scf_snaplevel_destroy(imp_rsnpl);
imp_snpl = imp_rsnpl = NULL;
scf_snapshot_destroy(imp_snap);
scf_snapshot_destroy(imp_lisnap);
scf_snapshot_destroy(imp_tlisnap);
scf_snapshot_destroy(imp_rsnap);
imp_snap = imp_lisnap = imp_tlisnap = imp_rsnap = NULL;
scf_instance_destroy(imp_inst);
scf_instance_destroy(imp_tinst);
imp_inst = imp_tinst = NULL;
scf_service_destroy(imp_svc);
scf_service_destroy(imp_tsvc);
imp_svc = imp_tsvc = NULL;
scf_scope_destroy(imp_scope);
imp_scope = NULL;
load_fini();
}
int
lscf_bundle_import(bundle_t *bndl, const char *filename, uint_t flags)
{
scf_callback_t cbdata;
int result = 0;
entity_t *svc, *inst;
uu_list_t *insts;
int r;
pgroup_t *old_dpt;
int annotation_set = 0;
const char * const emsg_nomem = gettext("Out of memory.\n");
const char * const emsg_nores =
gettext("svc.configd is out of resources.\n");
lscf_prep_hndl();
if (alloc_imp_globals())
goto out;
if (scf_handle_get_scope(g_hndl, SCF_SCOPE_LOCAL, imp_scope) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
warn(gettext("Repository connection broken.\n"));
repository_teardown();
result = -1;
goto out;
case SCF_ERROR_NOT_FOUND:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
default:
bad_error("scf_handle_get_scope", scf_error());
}
}
/* Set up the auditing annotation. */
if (_scf_set_annotation(g_hndl, "svccfg import", filename) == 0) {
annotation_set = 1;
} else {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
warn(gettext("Repository connection broken.\n"));
repository_teardown();
result = -1;
goto out;
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NO_RESOURCES:
case SCF_ERROR_INTERNAL:
bad_error("_scf_set_annotation", scf_error());
/* NOTREACHED */
default:
/*
* Do not terminate import because of inability to
* generate annotation audit event.
*/
warn(gettext("_scf_set_annotation() unexpectedly "
"failed with return code of %d\n"), scf_error());
break;
}
}
/*
* Clear the sc_import_state's of all services & instances so we can
* report how far we got if we fail.
*/
for (svc = uu_list_first(bndl->sc_bundle_services);
svc != NULL;
svc = uu_list_next(bndl->sc_bundle_services, svc)) {
svc->sc_import_state = 0;
if (uu_list_walk(svc->sc_u.sc_service.sc_service_instances,
clear_int, (void *)offsetof(entity_t, sc_import_state),
UU_DEFAULT) != 0)
bad_error("uu_list_walk", uu_error());
}
cbdata.sc_handle = g_hndl;
cbdata.sc_parent = imp_scope;
cbdata.sc_flags = flags;
cbdata.sc_general = NULL;
if (uu_list_walk(bndl->sc_bundle_services, lscf_service_import,
&cbdata, UU_DEFAULT) == 0) {
char *eptr;
/* Success. Refresh everything. */
if (flags & SCI_NOREFRESH || no_refresh) {
no_refresh = 0;
result = 0;
goto out;
}
for (svc = uu_list_first(bndl->sc_bundle_services);
svc != NULL;
svc = uu_list_next(bndl->sc_bundle_services, svc)) {
pgroup_t *dpt;
insts = svc->sc_u.sc_service.sc_service_instances;
for (inst = uu_list_first(insts);
inst != NULL;
inst = uu_list_next(insts, inst)) {
r = imp_refresh_fmri(inst->sc_fmri, NULL, NULL);
switch (r) {
case 0:
break;
case ENOMEM:
case ECONNABORTED:
case EPERM:
case -1:
goto progress;
default:
bad_error("imp_refresh_fmri", r);
}
inst->sc_import_state = IMPORT_REFRESHED;
for (dpt = uu_list_first(inst->sc_dependents);
dpt != NULL;
dpt = uu_list_next(inst->sc_dependents,
dpt))
if (imp_refresh_fmri(
dpt->sc_pgroup_fmri,
dpt->sc_pgroup_name,
inst->sc_fmri) != 0)
goto progress;
}
for (dpt = uu_list_first(svc->sc_dependents);
dpt != NULL;
dpt = uu_list_next(svc->sc_dependents, dpt))
if (imp_refresh_fmri(dpt->sc_pgroup_fmri,
dpt->sc_pgroup_name, svc->sc_fmri) != 0)
goto progress;
}
for (old_dpt = uu_list_first(imp_deleted_dpts);
old_dpt != NULL;
old_dpt = uu_list_next(imp_deleted_dpts, old_dpt))
if (imp_refresh_fmri(old_dpt->sc_pgroup_fmri,
old_dpt->sc_pgroup_name,
old_dpt->sc_parent->sc_fmri) != 0)
goto progress;
result = 0;
/*
* This snippet of code assumes that we are running svccfg as we
* normally do -- witih svc.startd running. Of course, that is
* not actually the case all the time because we also use a
* varient of svc.configd and svccfg which are only meant to
* run during the build process. During this time we have no
* svc.startd, so this check would hang the build process.
*
* However, we've also given other consolidations, a bit of a
* means to tie themselves into a knot. They're not properly
* using the native build equivalents, but they've been getting
* away with it anyways. Therefore, if we've found that
* SVCCFG_REPOSITORY is set indicating that a separate configd
* should be spun up, then we have to assume it's not using a
* startd and we should not do this check.
*/
#ifndef NATIVE_BUILD
/*
* Verify that the restarter group is preset
*/
eptr = getenv("SVCCFG_REPOSITORY");
for (svc = uu_list_first(bndl->sc_bundle_services);
svc != NULL && eptr == NULL;
svc = uu_list_next(bndl->sc_bundle_services, svc)) {
insts = svc->sc_u.sc_service.sc_service_instances;
for (inst = uu_list_first(insts);
inst != NULL;
inst = uu_list_next(insts, inst)) {
if (lscf_instance_verify(imp_scope, svc,
inst) != 0)
goto progress;
}
}
#endif
goto out;
}
if (uu_error() != UU_ERROR_CALLBACK_FAILED)
bad_error("uu_list_walk", uu_error());
printerr:
/* If the error hasn't been printed yet, do so here. */
switch (cbdata.sc_err) {
case ECONNABORTED:
warn(gettext("Repository connection broken.\n"));
break;
case ENOMEM:
warn(emsg_nomem);
break;
case ENOSPC:
warn(emsg_nores);
break;
case EROFS:
warn(gettext("Repository is read-only.\n"));
break;
case EACCES:
warn(gettext("Repository backend denied access.\n"));
break;
case EPERM:
case EINVAL:
case EEXIST:
case EBUSY:
case EBADF:
case -1:
break;
default:
bad_error("lscf_service_import", cbdata.sc_err);
}
progress:
warn(gettext("Import of %s failed. Progress:\n"), filename);
for (svc = uu_list_first(bndl->sc_bundle_services);
svc != NULL;
svc = uu_list_next(bndl->sc_bundle_services, svc)) {
insts = svc->sc_u.sc_service.sc_service_instances;
warn(gettext(" Service \"%s\": %s\n"), svc->sc_name,
import_progress(svc->sc_import_state));
for (inst = uu_list_first(insts);
inst != NULL;
inst = uu_list_next(insts, inst))
warn(gettext(" Instance \"%s\": %s\n"),
inst->sc_name,
import_progress(inst->sc_import_state));
}
if (cbdata.sc_err == ECONNABORTED)
repository_teardown();
result = -1;
out:
if (annotation_set != 0) {
/* Turn off annotation. It is no longer needed. */
(void) _scf_set_annotation(g_hndl, NULL, NULL);
}
free_imp_globals();
return (result);
}
/*
* _lscf_import_err() summarize the error handling returned by
* lscf_import_{instance | service}_pgs
* Return values are:
* IMPORT_NEXT
* IMPORT_OUT
* IMPORT_BAD
*/
#define IMPORT_BAD -1
#define IMPORT_NEXT 0
#define IMPORT_OUT 1
static int
_lscf_import_err(int err, const char *fmri)
{
switch (err) {
case 0:
if (g_verbose)
warn(gettext("%s updated.\n"), fmri);
return (IMPORT_NEXT);
case ECONNABORTED:
warn(gettext("Could not update %s "
"(repository connection broken).\n"), fmri);
return (IMPORT_OUT);
case ENOMEM:
warn(gettext("Could not update %s (out of memory).\n"), fmri);
return (IMPORT_OUT);
case ENOSPC:
warn(gettext("Could not update %s "
"(repository server out of resources).\n"), fmri);
return (IMPORT_OUT);
case ECANCELED:
warn(gettext(
"Could not update %s (deleted).\n"), fmri);
return (IMPORT_NEXT);
case EPERM:
case EINVAL:
case EBUSY:
return (IMPORT_NEXT);
case EROFS:
warn(gettext("Could not update %s (repository read-only).\n"),
fmri);
return (IMPORT_OUT);
case EACCES:
warn(gettext("Could not update %s "
"(backend access denied).\n"), fmri);
return (IMPORT_NEXT);
case EEXIST:
default:
return (IMPORT_BAD);
}
/*NOTREACHED*/
}
/*
* The global imp_svc and imp_inst should be set by the caller in the
* check to make sure the service and instance exist that the apply is
* working on.
*/
static int
lscf_dependent_apply(void *dpg, void *e)
{
scf_callback_t cb;
pgroup_t *dpt_pgroup = dpg;
pgroup_t *deldpt;
entity_t *ent = e;
int tissvc;
void *sc_ent, *tent;
scf_error_t serr;
int r;
const char * const dependents = "dependents";
const int issvc = (ent->sc_etype == SVCCFG_SERVICE_OBJECT);
if (issvc)
sc_ent = imp_svc;
else
sc_ent = imp_inst;
if (entity_get_running_pg(sc_ent, issvc, dependents, imp_pg,
imp_iter, imp_tinst, imp_snap, imp_snpl) != 0 ||
scf_pg_get_property(imp_pg, dpt_pgroup->sc_pgroup_name,
imp_prop) != 0) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
case SCF_ERROR_DELETED:
break;
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_NOT_SET:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
default:
bad_error("entity_get_pg", scf_error());
}
} else {
/*
* Found the dependents/<wip dep> so check to
* see if the service is different. If so
* store the service for later refresh, and
* delete the wip dependency from the service
*/
if (scf_property_get_value(imp_prop, ud_val) != 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
break;
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_NOT_SET:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
default:
bad_error("scf_property_get_value",
scf_error());
}
}
if (scf_value_get_as_string(ud_val, ud_oldtarg,
max_scf_value_len + 1) < 0)
bad_error("scf_value_get_as_string", scf_error());
r = fmri_equal(dpt_pgroup->sc_pgroup_fmri, ud_oldtarg);
switch (r) {
case 1:
break;
case 0:
if ((serr = fmri_to_entity(g_hndl, ud_oldtarg, &tent,
&tissvc)) != SCF_ERROR_NONE) {
if (serr == SCF_ERROR_NOT_FOUND) {
break;
} else {
bad_error("fmri_to_entity", serr);
}
}
if (entity_get_pg(tent, tissvc,
dpt_pgroup->sc_pgroup_name, imp_pg) != 0) {
serr = scf_error();
if (serr == SCF_ERROR_NOT_FOUND ||
serr == SCF_ERROR_DELETED) {
break;
} else {
bad_error("entity_get_pg", scf_error());
}
}
if (scf_pg_delete(imp_pg) != 0) {
serr = scf_error();
if (serr == SCF_ERROR_NOT_FOUND ||
serr == SCF_ERROR_DELETED) {
break;
} else {
bad_error("scf_pg_delete", scf_error());
}
}
deldpt = internal_pgroup_new();
if (deldpt == NULL)
return (ENOMEM);
deldpt->sc_pgroup_name =
strdup(dpt_pgroup->sc_pgroup_name);
deldpt->sc_pgroup_fmri = strdup(ud_oldtarg);
if (deldpt->sc_pgroup_name == NULL ||
deldpt->sc_pgroup_fmri == NULL)
return (ENOMEM);
deldpt->sc_parent = (entity_t *)ent;
if (uu_list_insert_after(imp_deleted_dpts, NULL,
deldpt) != 0)
uu_die(gettext("libuutil error: %s\n"),
uu_strerror(uu_error()));
break;
default:
bad_error("fmri_equal", r);
}
}
cb.sc_handle = g_hndl;
cb.sc_parent = ent;
cb.sc_service = ent->sc_etype == SVCCFG_SERVICE_OBJECT;
cb.sc_source_fmri = ent->sc_fmri;
cb.sc_target_fmri = ent->sc_fmri;
cb.sc_trans = NULL;
cb.sc_flags = SCI_FORCE;
if (lscf_dependent_import(dpt_pgroup, &cb) != UU_WALK_NEXT)
return (UU_WALK_ERROR);
r = imp_refresh_fmri(dpt_pgroup->sc_pgroup_fmri, NULL, NULL);
switch (r) {
case 0:
break;
case ENOMEM:
case ECONNABORTED:
case EPERM:
case -1:
warn(gettext("Unable to refresh \"%s\"\n"),
dpt_pgroup->sc_pgroup_fmri);
return (UU_WALK_ERROR);
default:
bad_error("imp_refresh_fmri", r);
}
return (UU_WALK_NEXT);
}
/*
* Returns
* 0 - success
* -1 - lscf_import_instance_pgs() failed.
*/
int
lscf_bundle_apply(bundle_t *bndl, const char *file)
{
pgroup_t *old_dpt;
entity_t *svc, *inst;
int annotation_set = 0;
int ret = 0;
int r = 0;
lscf_prep_hndl();
if ((ret = alloc_imp_globals()))
goto out;
if (scf_handle_get_scope(g_hndl, SCF_SCOPE_LOCAL, imp_scope) != 0)
scfdie();
/*
* Set the strings to be used for the security audit annotation
* event.
*/
if (_scf_set_annotation(g_hndl, "svccfg apply", file) == 0) {
annotation_set = 1;
} else {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
warn(gettext("Repository connection broken.\n"));
goto out;
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NO_RESOURCES:
case SCF_ERROR_INTERNAL:
bad_error("_scf_set_annotation", scf_error());
/* NOTREACHED */
default:
/*
* Do not abort apply operation because of
* inability to create annotation audit event.
*/
warn(gettext("_scf_set_annotation() unexpectedly "
"failed with return code of %d\n"), scf_error());
break;
}
}
for (svc = uu_list_first(bndl->sc_bundle_services);
svc != NULL;
svc = uu_list_next(bndl->sc_bundle_services, svc)) {
int refresh = 0;
if (scf_scope_get_service(imp_scope, svc->sc_name,
imp_svc) != 0) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
if (g_verbose)
warn(gettext("Ignoring nonexistent "
"service %s.\n"), svc->sc_name);
continue;
default:
scfdie();
}
}
/*
* If there were missing types in the profile, then need to
* attempt to find the types.
*/
if (svc->sc_miss_type) {
if (uu_list_numnodes(svc->sc_pgroups) &&
uu_list_walk(svc->sc_pgroups, find_current_pg_type,
svc, UU_DEFAULT) != 0) {
if (uu_error() != UU_ERROR_CALLBACK_FAILED)
bad_error("uu_list_walk", uu_error());
ret = -1;
continue;
}
for (inst = uu_list_first(
svc->sc_u.sc_service.sc_service_instances);
inst != NULL;
inst = uu_list_next(
svc->sc_u.sc_service.sc_service_instances, inst)) {
/*
* If the instance doesn't exist just
* skip to the next instance and let the
* import note the missing instance.
*/
if (scf_service_get_instance(imp_svc,
inst->sc_name, imp_inst) != 0)
continue;
if (uu_list_walk(inst->sc_pgroups,
find_current_pg_type, inst,
UU_DEFAULT) != 0) {
if (uu_error() !=
UU_ERROR_CALLBACK_FAILED)
bad_error("uu_list_walk",
uu_error());
ret = -1;
inst->sc_miss_type = B_TRUE;
}
}
}
/*
* if we have pgs in the profile, we need to refresh ALL
* instances of the service
*/
if (uu_list_numnodes(svc->sc_pgroups) != 0) {
refresh = 1;
r = lscf_import_service_pgs(imp_svc, svc->sc_fmri, svc,
SCI_FORCE | SCI_KEEP);
switch (_lscf_import_err(r, svc->sc_fmri)) {
case IMPORT_NEXT:
break;
case IMPORT_OUT:
goto out;
case IMPORT_BAD:
default:
bad_error("lscf_import_service_pgs", r);
}
}
if (uu_list_numnodes(svc->sc_dependents) != 0) {
uu_list_walk(svc->sc_dependents,
lscf_dependent_apply, svc, UU_DEFAULT);
}
for (inst = uu_list_first(
svc->sc_u.sc_service.sc_service_instances);
inst != NULL;
inst = uu_list_next(
svc->sc_u.sc_service.sc_service_instances, inst)) {
/*
* This instance still has missing types
* so skip it.
*/
if (inst->sc_miss_type) {
if (g_verbose)
warn(gettext("Ignoring instance "
"%s:%s with missing types\n"),
inst->sc_parent->sc_name,
inst->sc_name);
continue;
}
if (scf_service_get_instance(imp_svc, inst->sc_name,
imp_inst) != 0) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
if (g_verbose)
warn(gettext("Ignoring "
"nonexistant instance "
"%s:%s.\n"),
inst->sc_parent->sc_name,
inst->sc_name);
continue;
default:
scfdie();
}
}
/*
* If the instance does not have a general/enabled
* property and no last-import snapshot then the
* instance is not a fully installed instance and
* should not have a profile applied to it.
*
* This could happen if a service/instance declares
* a dependent on behalf of another service/instance.
*
*/
if (scf_instance_get_snapshot(imp_inst, snap_lastimport,
imp_snap) != 0) {
if (scf_instance_get_pg(imp_inst,
SCF_PG_GENERAL, imp_pg) != 0 ||
scf_pg_get_property(imp_pg,
SCF_PROPERTY_ENABLED, imp_prop) != 0) {
if (g_verbose)
warn(gettext("Ignoreing "
"partial instance "
"%s:%s.\n"),
inst->sc_parent->sc_name,
inst->sc_name);
continue;
}
}
r = lscf_import_instance_pgs(imp_inst, inst->sc_fmri,
inst, SCI_FORCE | SCI_KEEP);
switch (_lscf_import_err(r, inst->sc_fmri)) {
case IMPORT_NEXT:
break;
case IMPORT_OUT:
goto out;
case IMPORT_BAD:
default:
bad_error("lscf_import_instance_pgs", r);
}
if (uu_list_numnodes(inst->sc_dependents) != 0) {
uu_list_walk(inst->sc_dependents,
lscf_dependent_apply, inst, UU_DEFAULT);
}
/* refresh only if there is no pgs in the service */
if (refresh == 0)
(void) refresh_entity(0, imp_inst,
inst->sc_fmri, NULL, NULL, NULL);
}
if (refresh == 1) {
char *name_buf = safe_malloc(max_scf_name_len + 1);
(void) refresh_entity(1, imp_svc, svc->sc_name,
imp_inst, imp_iter, name_buf);
free(name_buf);
}
for (old_dpt = uu_list_first(imp_deleted_dpts);
old_dpt != NULL;
old_dpt = uu_list_next(imp_deleted_dpts, old_dpt)) {
if (imp_refresh_fmri(old_dpt->sc_pgroup_fmri,
old_dpt->sc_pgroup_name,
old_dpt->sc_parent->sc_fmri) != 0) {
warn(gettext("Unable to refresh \"%s\"\n"),
old_dpt->sc_pgroup_fmri);
}
}
}
out:
if (annotation_set) {
/* Remove security audit annotation strings. */
(void) _scf_set_annotation(g_hndl, NULL, NULL);
}
free_imp_globals();
return (ret);
}
/*
* Export. These functions create and output an XML tree of a service
* description from the repository. This is largely the inverse of
* lxml_get_bundle() in svccfg_xml.c, but with some kickers:
*
* - We must include any properties which are not represented specifically by
* a service manifest, e.g., properties created by an admin post-import. To
* do so we'll iterate through all properties and deal with each
* apropriately.
*
* - Children of services and instances must must be in the order set by the
* DTD, but we iterate over the properties in undefined order. The elements
* are not easily (or efficiently) sortable by name. Since there's a fixed
* number of classes of them, however, we'll keep the classes separate and
* assemble them in order.
*/
/*
* Convenience function to handle xmlSetProp errors (and type casting).
*/
static void
safe_setprop(xmlNodePtr n, const char *name, const char *val)
{
if (xmlSetProp(n, (const xmlChar *)name, (const xmlChar *)val) == NULL)
uu_die(gettext("Could not set XML property.\n"));
}
/*
* Convenience function to set an XML attribute to the single value of an
* astring property. If the value happens to be the default, don't set the
* attribute. "dval" should be the default value supplied by the DTD, or
* NULL for no default.
*/
static int
set_attr_from_prop_default(scf_property_t *prop, xmlNodePtr n,
const char *name, const char *dval)
{
scf_value_t *val;
ssize_t len;
char *str;
val = scf_value_create(g_hndl);
if (val == NULL)
scfdie();
if (prop_get_val(prop, val) != 0) {
scf_value_destroy(val);
return (-1);
}
len = scf_value_get_as_string(val, NULL, 0);
if (len < 0)
scfdie();
str = safe_malloc(len + 1);
if (scf_value_get_as_string(val, str, len + 1) < 0)
scfdie();
scf_value_destroy(val);
if (dval == NULL || strcmp(str, dval) != 0)
safe_setprop(n, name, str);
free(str);
return (0);
}
/*
* As above, but the attribute is always set.
*/
static int
set_attr_from_prop(scf_property_t *prop, xmlNodePtr n, const char *name)
{
return (set_attr_from_prop_default(prop, n, name, NULL));
}
/*
* Dump the given document onto f, with "'s replaced by ''s.
*/
static int
write_service_bundle(xmlDocPtr doc, FILE *f)
{
xmlChar *mem;
int sz, i;
mem = NULL;
xmlDocDumpFormatMemory(doc, &mem, &sz, 1);
if (mem == NULL) {
semerr(gettext("Could not dump XML tree.\n"));
return (-1);
}
/*
* Fortunately libxml produces &quot; instead of ", so we can blindly
* replace all " with '. Cursed libxml2! Why must you #ifdef out the
* &apos; code?!
*/
for (i = 0; i < sz; ++i) {
char c = (char)mem[i];
if (c == '"')
(void) fputc('\'', f);
else if (c == '\'')
(void) fwrite("&apos;", sizeof ("&apos;") - 1, 1, f);
else
(void) fputc(c, f);
}
return (0);
}
/*
* Create the DOM elements in elts necessary to (generically) represent prop
* (i.e., a property or propval element). If the name of the property is
* known, it should be passed as name_arg. Otherwise, pass NULL.
*/
static void
export_property(scf_property_t *prop, const char *name_arg,
struct pg_elts *elts, int flags)
{
const char *type;
scf_error_t err = 0;
xmlNodePtr pnode, lnode;
char *lnname;
int ret;
/* name */
if (name_arg != NULL) {
(void) strcpy(exp_str, name_arg);
} else {
if (scf_property_get_name(prop, exp_str, exp_str_sz) < 0)
scfdie();
}
/* type */
type = prop_to_typestr(prop);
if (type == NULL)
uu_die(gettext("Can't export property %s: unknown type.\n"),
exp_str);
/* If we're exporting values, and there's just one, export it here. */
if (!(flags & SCE_ALL_VALUES))
goto empty;
if (scf_property_get_value(prop, exp_val) == SCF_SUCCESS) {
xmlNodePtr n;
/* Single value, so use propval */
n = xmlNewNode(NULL, (xmlChar *)"propval");
if (n == NULL)
uu_die(emsg_create_xml);
safe_setprop(n, name_attr, exp_str);
safe_setprop(n, type_attr, type);
if (scf_value_get_as_string(exp_val, exp_str, exp_str_sz) < 0)
scfdie();
safe_setprop(n, value_attr, exp_str);
if (elts->propvals == NULL)
elts->propvals = n;
else
(void) xmlAddSibling(elts->propvals, n);
return;
}
err = scf_error();
if (err == SCF_ERROR_PERMISSION_DENIED) {
semerr(emsg_permission_denied);
return;
}
if (err != SCF_ERROR_CONSTRAINT_VIOLATED &&
err != SCF_ERROR_NOT_FOUND &&
err != SCF_ERROR_PERMISSION_DENIED)
scfdie();
empty:
/* Multiple (or no) values, so use property */
pnode = xmlNewNode(NULL, (xmlChar *)"property");
if (pnode == NULL)
uu_die(emsg_create_xml);
safe_setprop(pnode, name_attr, exp_str);
safe_setprop(pnode, type_attr, type);
if (err == SCF_ERROR_CONSTRAINT_VIOLATED) {
lnname = uu_msprintf("%s_list", type);
if (lnname == NULL)
uu_die(gettext("Could not create string"));
lnode = xmlNewChild(pnode, NULL, (xmlChar *)lnname, NULL);
if (lnode == NULL)
uu_die(emsg_create_xml);
uu_free(lnname);
if (scf_iter_property_values(exp_val_iter, prop) != SCF_SUCCESS)
scfdie();
while ((ret = scf_iter_next_value(exp_val_iter, exp_val)) ==
1) {
xmlNodePtr vn;
vn = xmlNewChild(lnode, NULL, (xmlChar *)"value_node",
NULL);
if (vn == NULL)
uu_die(emsg_create_xml);
if (scf_value_get_as_string(exp_val, exp_str,
exp_str_sz) < 0)
scfdie();
safe_setprop(vn, value_attr, exp_str);
}
if (ret != 0)
scfdie();
}
if (elts->properties == NULL)
elts->properties = pnode;
else
(void) xmlAddSibling(elts->properties, pnode);
}
/*
* Add a property_group element for this property group to elts.
*/
static void
export_pg(scf_propertygroup_t *pg, struct entity_elts *eelts, int flags)
{
xmlNodePtr n;
struct pg_elts elts;
int ret;
boolean_t read_protected;
n = xmlNewNode(NULL, (xmlChar *)"property_group");
/* name */
if (scf_pg_get_name(pg, exp_str, max_scf_name_len + 1) < 0)
scfdie();
safe_setprop(n, name_attr, exp_str);
/* type */
if (scf_pg_get_type(pg, exp_str, exp_str_sz) < 0)
scfdie();
safe_setprop(n, type_attr, exp_str);
/* properties */
if (scf_iter_pg_properties(exp_prop_iter, pg) != SCF_SUCCESS)
scfdie();
(void) memset(&elts, 0, sizeof (elts));
/*
* If this property group is not read protected, we always want to
* output all the values. Otherwise, we only output the values if the
* caller set SCE_ALL_VALUES (i.e., the user gave us export/archive -a).
*/
if (_scf_pg_is_read_protected(pg, &read_protected) != SCF_SUCCESS)
scfdie();
if (!read_protected)
flags |= SCE_ALL_VALUES;
while ((ret = scf_iter_next_property(exp_prop_iter, exp_prop)) == 1) {
if (scf_property_get_name(exp_prop, exp_str, exp_str_sz) < 0)
scfdie();
if (strcmp(exp_str, SCF_PROPERTY_STABILITY) == 0) {
xmlNodePtr m;
m = xmlNewNode(NULL, (xmlChar *)"stability");
if (m == NULL)
uu_die(emsg_create_xml);
if (set_attr_from_prop(exp_prop, m, value_attr) == 0) {
elts.stability = m;
continue;
}
xmlFreeNode(m);
}
export_property(exp_prop, NULL, &elts, flags);
}
if (ret == -1)
scfdie();
(void) xmlAddChild(n, elts.stability);
(void) xmlAddChildList(n, elts.propvals);
(void) xmlAddChildList(n, elts.properties);
if (eelts->property_groups == NULL)
eelts->property_groups = n;
else
(void) xmlAddSibling(eelts->property_groups, n);
}
/*
* Create an XML node representing the dependency described by the given
* property group and put it in eelts. Unless the dependency is not valid, in
* which case create a generic property_group element which represents it and
* put it in eelts.
*/
static void
export_dependency(scf_propertygroup_t *pg, struct entity_elts *eelts)
{
xmlNodePtr n;
int err = 0, ret;
struct pg_elts elts;
n = xmlNewNode(NULL, (xmlChar *)"dependency");
if (n == NULL)
uu_die(emsg_create_xml);
/*
* If the external flag is present, skip this dependency because it
* should have been created by another manifest.
*/
if (scf_pg_get_property(pg, scf_property_external, exp_prop) == 0) {
if (prop_check_type(exp_prop, SCF_TYPE_BOOLEAN) == 0 &&
prop_get_val(exp_prop, exp_val) == 0) {
uint8_t b;
if (scf_value_get_boolean(exp_val, &b) != SCF_SUCCESS)
scfdie();
if (b)
return;
}
} else if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
/* Get the required attributes. */
/* name */
if (scf_pg_get_name(pg, exp_str, max_scf_name_len + 1) < 0)
scfdie();
safe_setprop(n, name_attr, exp_str);
/* grouping */
if (pg_get_prop(pg, SCF_PROPERTY_GROUPING, exp_prop) != 0 ||
set_attr_from_prop(exp_prop, n, "grouping") != 0)
err = 1;
/* restart_on */
if (pg_get_prop(pg, SCF_PROPERTY_RESTART_ON, exp_prop) != 0 ||
set_attr_from_prop(exp_prop, n, "restart_on") != 0)
err = 1;
/* type */
if (pg_get_prop(pg, SCF_PROPERTY_TYPE, exp_prop) != 0 ||
set_attr_from_prop(exp_prop, n, type_attr) != 0)
err = 1;
/*
* entities: Not required, but if we create no children, it will be
* created as empty on import, so fail if it's missing.
*/
if (pg_get_prop(pg, SCF_PROPERTY_ENTITIES, exp_prop) == 0 &&
prop_check_type(exp_prop, SCF_TYPE_FMRI) == 0) {
scf_iter_t *eiter;
int ret2;
eiter = scf_iter_create(g_hndl);
if (eiter == NULL)
scfdie();
if (scf_iter_property_values(eiter, exp_prop) != SCF_SUCCESS)
scfdie();
while ((ret2 = scf_iter_next_value(eiter, exp_val)) == 1) {
xmlNodePtr ch;
if (scf_value_get_astring(exp_val, exp_str,
exp_str_sz) < 0)
scfdie();
/*
* service_fmri's must be first, so we can add them
* here.
*/
ch = xmlNewChild(n, NULL, (xmlChar *)"service_fmri",
NULL);
if (ch == NULL)
uu_die(emsg_create_xml);
safe_setprop(ch, value_attr, exp_str);
}
if (ret2 == -1)
scfdie();
scf_iter_destroy(eiter);
} else
err = 1;
if (err) {
xmlFreeNode(n);
export_pg(pg, eelts, SCE_ALL_VALUES);
return;
}
/* Iterate through the properties & handle each. */
if (scf_iter_pg_properties(exp_prop_iter, pg) != SCF_SUCCESS)
scfdie();
(void) memset(&elts, 0, sizeof (elts));
while ((ret = scf_iter_next_property(exp_prop_iter, exp_prop)) == 1) {
if (scf_property_get_name(exp_prop, exp_str, exp_str_sz) < 0)
scfdie();
if (strcmp(exp_str, SCF_PROPERTY_GROUPING) == 0 ||
strcmp(exp_str, SCF_PROPERTY_RESTART_ON) == 0 ||
strcmp(exp_str, SCF_PROPERTY_TYPE) == 0 ||
strcmp(exp_str, SCF_PROPERTY_ENTITIES) == 0) {
continue;
} else if (strcmp(exp_str, SCF_PROPERTY_STABILITY) == 0) {
xmlNodePtr m;
m = xmlNewNode(NULL, (xmlChar *)"stability");
if (m == NULL)
uu_die(emsg_create_xml);
if (set_attr_from_prop(exp_prop, m, value_attr) == 0) {
elts.stability = m;
continue;
}
xmlFreeNode(m);
}
export_property(exp_prop, exp_str, &elts, SCE_ALL_VALUES);
}
if (ret == -1)
scfdie();
(void) xmlAddChild(n, elts.stability);
(void) xmlAddChildList(n, elts.propvals);
(void) xmlAddChildList(n, elts.properties);
if (eelts->dependencies == NULL)
eelts->dependencies = n;
else
(void) xmlAddSibling(eelts->dependencies, n);
}
static xmlNodePtr
export_method_environment(scf_propertygroup_t *pg)
{
xmlNodePtr env;
int ret;
int children = 0;
if (scf_pg_get_property(pg, SCF_PROPERTY_ENVIRONMENT, NULL) != 0)
return (NULL);
env = xmlNewNode(NULL, (xmlChar *)"method_environment");
if (env == NULL)
uu_die(emsg_create_xml);
if (pg_get_prop(pg, SCF_PROPERTY_ENVIRONMENT, exp_prop) != 0)
scfdie();
if (scf_iter_property_values(exp_val_iter, exp_prop) != SCF_SUCCESS)
scfdie();
while ((ret = scf_iter_next_value(exp_val_iter, exp_val)) == 1) {
xmlNodePtr ev;
char *cp;
if (scf_value_get_as_string(exp_val, exp_str, exp_str_sz) < 0)
scfdie();
if ((cp = strchr(exp_str, '=')) == NULL || cp == exp_str) {
warn(gettext("Invalid environment variable \"%s\".\n"),
exp_str);
continue;
} else if (strncmp(exp_str, "SMF_", 4) == 0) {
warn(gettext("Invalid environment variable \"%s\"; "
"\"SMF_\" prefix is reserved.\n"), exp_str);
continue;
}
*cp = '\0';
cp++;
ev = xmlNewChild(env, NULL, (xmlChar *)"envvar", NULL);
if (ev == NULL)
uu_die(emsg_create_xml);
safe_setprop(ev, name_attr, exp_str);
safe_setprop(ev, value_attr, cp);
children++;
}
if (ret != 0)
scfdie();
if (children == 0) {
xmlFreeNode(env);
return (NULL);
}
return (env);
}
/*
* As above, but for a method property group.
*/
static void
export_method(scf_propertygroup_t *pg, struct entity_elts *eelts)
{
xmlNodePtr n, env;
char *str;
int err = 0, nonenv, ret;
uint8_t use_profile;
struct pg_elts elts;
xmlNodePtr ctxt = NULL;
n = xmlNewNode(NULL, (xmlChar *)"exec_method");
/* Get the required attributes. */
/* name */
if (scf_pg_get_name(pg, exp_str, max_scf_name_len + 1) < 0)
scfdie();
safe_setprop(n, name_attr, exp_str);
/* type */
if (pg_get_prop(pg, SCF_PROPERTY_TYPE, exp_prop) != 0 ||
set_attr_from_prop(exp_prop, n, type_attr) != 0)
err = 1;
/* exec */
if (pg_get_prop(pg, SCF_PROPERTY_EXEC, exp_prop) != 0 ||
set_attr_from_prop(exp_prop, n, "exec") != 0)
err = 1;
/* timeout */
if (pg_get_prop(pg, SCF_PROPERTY_TIMEOUT, exp_prop) == 0 &&
prop_check_type(exp_prop, SCF_TYPE_COUNT) == 0 &&
prop_get_val(exp_prop, exp_val) == 0) {
uint64_t c;
if (scf_value_get_count(exp_val, &c) != SCF_SUCCESS)
scfdie();
str = uu_msprintf("%llu", c);
if (str == NULL)
uu_die(gettext("Could not create string"));
safe_setprop(n, "timeout_seconds", str);
free(str);
} else
err = 1;
if (err) {
xmlFreeNode(n);
export_pg(pg, eelts, SCE_ALL_VALUES);
return;
}
/*
* If we're going to have a method_context child, we need to know
* before we iterate through the properties. Since method_context's
* are optional, we don't want to complain about any properties
* missing if none of them are there. Thus we can't use the
* convenience functions.
*/
nonenv =
scf_pg_get_property(pg, SCF_PROPERTY_WORKING_DIRECTORY, NULL) ==
SCF_SUCCESS ||
scf_pg_get_property(pg, SCF_PROPERTY_PROJECT, NULL) ==
SCF_SUCCESS ||
scf_pg_get_property(pg, SCF_PROPERTY_RESOURCE_POOL, NULL) ==
SCF_SUCCESS ||
scf_pg_get_property(pg, SCF_PROPERTY_SECFLAGS, NULL) ==
SCF_SUCCESS ||
scf_pg_get_property(pg, SCF_PROPERTY_USE_PROFILE, NULL) ==
SCF_SUCCESS;
if (nonenv) {
ctxt = xmlNewNode(NULL, (xmlChar *)"method_context");
if (ctxt == NULL)
uu_die(emsg_create_xml);
if (pg_get_prop(pg, SCF_PROPERTY_WORKING_DIRECTORY, exp_prop) ==
0 &&
set_attr_from_prop_default(exp_prop, ctxt,
"working_directory", ":default") != 0)
err = 1;
if (pg_get_prop(pg, SCF_PROPERTY_PROJECT, exp_prop) == 0 &&
set_attr_from_prop_default(exp_prop, ctxt, "project",
":default") != 0)
err = 1;
if (pg_get_prop(pg, SCF_PROPERTY_RESOURCE_POOL, exp_prop) ==
0 &&
set_attr_from_prop_default(exp_prop, ctxt,
"resource_pool", ":default") != 0)
err = 1;
if (pg_get_prop(pg, SCF_PROPERTY_SECFLAGS, exp_prop) == 0 &&
set_attr_from_prop_default(exp_prop, ctxt,
"security_flags", ":default") != 0)
err = 1;
/*
* We only want to complain about profile or credential
* properties if we will use them. To determine that we must
* examine USE_PROFILE.
*/
if (pg_get_prop(pg, SCF_PROPERTY_USE_PROFILE, exp_prop) == 0 &&
prop_check_type(exp_prop, SCF_TYPE_BOOLEAN) == 0 &&
prop_get_val(exp_prop, exp_val) == 0) {
if (scf_value_get_boolean(exp_val, &use_profile) !=
SCF_SUCCESS) {
scfdie();
}
if (use_profile) {
xmlNodePtr prof;
prof = xmlNewChild(ctxt, NULL,
(xmlChar *)"method_profile", NULL);
if (prof == NULL)
uu_die(emsg_create_xml);
if (pg_get_prop(pg, SCF_PROPERTY_PROFILE,
exp_prop) != 0 ||
set_attr_from_prop(exp_prop, prof,
name_attr) != 0)
err = 1;
} else {
xmlNodePtr cred;
cred = xmlNewChild(ctxt, NULL,
(xmlChar *)"method_credential", NULL);
if (cred == NULL)
uu_die(emsg_create_xml);
if (pg_get_prop(pg, SCF_PROPERTY_USER,
exp_prop) != 0 ||
set_attr_from_prop(exp_prop, cred,
"user") != 0) {
err = 1;
}
if (pg_get_prop(pg, SCF_PROPERTY_GROUP,
exp_prop) == 0 &&
set_attr_from_prop_default(exp_prop, cred,
"group", ":default") != 0)
err = 1;
if (pg_get_prop(pg, SCF_PROPERTY_SUPP_GROUPS,
exp_prop) == 0 &&
set_attr_from_prop_default(exp_prop, cred,
"supp_groups", ":default") != 0)
err = 1;
if (pg_get_prop(pg, SCF_PROPERTY_PRIVILEGES,
exp_prop) == 0 &&
set_attr_from_prop_default(exp_prop, cred,
"privileges", ":default") != 0)
err = 1;
if (pg_get_prop(pg,
SCF_PROPERTY_LIMIT_PRIVILEGES,
exp_prop) == 0 &&
set_attr_from_prop_default(exp_prop, cred,
"limit_privileges", ":default") != 0)
err = 1;
}
}
}
if ((env = export_method_environment(pg)) != NULL) {
if (ctxt == NULL) {
ctxt = xmlNewNode(NULL, (xmlChar *)"method_context");
if (ctxt == NULL)
uu_die(emsg_create_xml);
}
(void) xmlAddChild(ctxt, env);
}
if (env != NULL || (nonenv && err == 0))
(void) xmlAddChild(n, ctxt);
else
xmlFreeNode(ctxt);
nonenv = (err == 0);
if (scf_iter_pg_properties(exp_prop_iter, pg) != SCF_SUCCESS)
scfdie();
(void) memset(&elts, 0, sizeof (elts));
while ((ret = scf_iter_next_property(exp_prop_iter, exp_prop)) == 1) {
if (scf_property_get_name(exp_prop, exp_str, exp_str_sz) < 0)
scfdie();
if (strcmp(exp_str, SCF_PROPERTY_TYPE) == 0 ||
strcmp(exp_str, SCF_PROPERTY_EXEC) == 0 ||
strcmp(exp_str, SCF_PROPERTY_TIMEOUT) == 0) {
continue;
} else if (strcmp(exp_str, SCF_PROPERTY_STABILITY) == 0) {
xmlNodePtr m;
m = xmlNewNode(NULL, (xmlChar *)"stability");
if (m == NULL)
uu_die(emsg_create_xml);
if (set_attr_from_prop(exp_prop, m, value_attr) == 0) {
elts.stability = m;
continue;
}
xmlFreeNode(m);
} else if (strcmp(exp_str, SCF_PROPERTY_WORKING_DIRECTORY) ==
0 ||
strcmp(exp_str, SCF_PROPERTY_PROJECT) == 0 ||
strcmp(exp_str, SCF_PROPERTY_RESOURCE_POOL) == 0 ||
strcmp(exp_str, SCF_PROPERTY_USE_PROFILE) == 0) {
if (nonenv)
continue;
} else if (strcmp(exp_str, SCF_PROPERTY_USER) == 0 ||
strcmp(exp_str, SCF_PROPERTY_GROUP) == 0 ||
strcmp(exp_str, SCF_PROPERTY_SUPP_GROUPS) == 0 ||
strcmp(exp_str, SCF_PROPERTY_PRIVILEGES) == 0 ||
strcmp(exp_str, SCF_PROPERTY_LIMIT_PRIVILEGES) == 0 ||
strcmp(exp_str, SCF_PROPERTY_SECFLAGS) == 0) {
if (nonenv && !use_profile)
continue;
} else if (strcmp(exp_str, SCF_PROPERTY_PROFILE) == 0) {
if (nonenv && use_profile)
continue;
} else if (strcmp(exp_str, SCF_PROPERTY_ENVIRONMENT) == 0) {
if (env != NULL)
continue;
}
export_property(exp_prop, exp_str, &elts, SCE_ALL_VALUES);
}
if (ret == -1)
scfdie();
(void) xmlAddChild(n, elts.stability);
(void) xmlAddChildList(n, elts.propvals);
(void) xmlAddChildList(n, elts.properties);
if (eelts->exec_methods == NULL)
eelts->exec_methods = n;
else
(void) xmlAddSibling(eelts->exec_methods, n);
}
static void
export_pg_elts(struct pg_elts *elts, const char *name, const char *type,
struct entity_elts *eelts)
{
xmlNodePtr pgnode;
pgnode = xmlNewNode(NULL, (xmlChar *)"property_group");
if (pgnode == NULL)
uu_die(emsg_create_xml);
safe_setprop(pgnode, name_attr, name);
safe_setprop(pgnode, type_attr, type);
(void) xmlAddChildList(pgnode, elts->propvals);
(void) xmlAddChildList(pgnode, elts->properties);
if (eelts->property_groups == NULL)
eelts->property_groups = pgnode;
else
(void) xmlAddSibling(eelts->property_groups, pgnode);
}
/*
* Process the general property group for a service. This is the one with the
* goodies.
*/
static void
export_svc_general(scf_propertygroup_t *pg, struct entity_elts *selts)
{
struct pg_elts elts;
int ret;
/*
* In case there are properties which don't correspond to child
* entities of the service entity, we'll set up a pg_elts structure to
* put them in.
*/
(void) memset(&elts, 0, sizeof (elts));
/* Walk the properties, looking for special ones. */
if (scf_iter_pg_properties(exp_prop_iter, pg) != SCF_SUCCESS)
scfdie();
while ((ret = scf_iter_next_property(exp_prop_iter, exp_prop)) == 1) {
if (scf_property_get_name(exp_prop, exp_str, exp_str_sz) < 0)
scfdie();
if (strcmp(exp_str, SCF_PROPERTY_SINGLE_INSTANCE) == 0) {
if (prop_check_type(exp_prop, SCF_TYPE_BOOLEAN) == 0 &&
prop_get_val(exp_prop, exp_val) == 0) {
uint8_t b;
if (scf_value_get_boolean(exp_val, &b) !=
SCF_SUCCESS)
scfdie();
if (b) {
selts->single_instance =
xmlNewNode(NULL,
(xmlChar *)"single_instance");
if (selts->single_instance == NULL)
uu_die(emsg_create_xml);
}
continue;
}
} else if (strcmp(exp_str, SCF_PROPERTY_RESTARTER) == 0) {
xmlNodePtr rnode, sfnode;
rnode = xmlNewNode(NULL, (xmlChar *)"restarter");
if (rnode == NULL)
uu_die(emsg_create_xml);
sfnode = xmlNewChild(rnode, NULL,
(xmlChar *)"service_fmri", NULL);
if (sfnode == NULL)
uu_die(emsg_create_xml);
if (set_attr_from_prop(exp_prop, sfnode,
value_attr) == 0) {
selts->restarter = rnode;
continue;
}
xmlFreeNode(rnode);
} else if (strcmp(exp_str, SCF_PROPERTY_ENTITY_STABILITY) ==
0) {
xmlNodePtr s;
s = xmlNewNode(NULL, (xmlChar *)"stability");
if (s == NULL)
uu_die(emsg_create_xml);
if (set_attr_from_prop(exp_prop, s, value_attr) == 0) {
selts->stability = s;
continue;
}
xmlFreeNode(s);
}
export_property(exp_prop, exp_str, &elts, SCE_ALL_VALUES);
}
if (ret == -1)
scfdie();
if (elts.propvals != NULL || elts.properties != NULL)
export_pg_elts(&elts, scf_pg_general, scf_group_framework,
selts);
}
static void
export_method_context(scf_propertygroup_t *pg, struct entity_elts *elts)
{
xmlNodePtr n, prof, cred, env;
uint8_t use_profile;
int ret, err = 0;
n = xmlNewNode(NULL, (xmlChar *)"method_context");
env = export_method_environment(pg);
/* Need to know whether we'll use a profile or not. */
if (pg_get_prop(pg, SCF_PROPERTY_USE_PROFILE, exp_prop) == 0 &&
prop_check_type(exp_prop, SCF_TYPE_BOOLEAN) == 0 &&
prop_get_val(exp_prop, exp_val) == 0) {
if (scf_value_get_boolean(exp_val, &use_profile) != SCF_SUCCESS)
scfdie();
if (use_profile)
prof =
xmlNewChild(n, NULL, (xmlChar *)"method_profile",
NULL);
else
cred =
xmlNewChild(n, NULL, (xmlChar *)"method_credential",
NULL);
}
if (env != NULL)
(void) xmlAddChild(n, env);
if (scf_iter_pg_properties(exp_prop_iter, pg) != SCF_SUCCESS)
scfdie();
while ((ret = scf_iter_next_property(exp_prop_iter, exp_prop)) == 1) {
if (scf_property_get_name(exp_prop, exp_str, exp_str_sz) < 0)
scfdie();
if (strcmp(exp_str, SCF_PROPERTY_WORKING_DIRECTORY) == 0) {
if (set_attr_from_prop(exp_prop, n,
"working_directory") != 0)
err = 1;
} else if (strcmp(exp_str, SCF_PROPERTY_PROJECT) == 0) {
if (set_attr_from_prop(exp_prop, n, "project") != 0)
err = 1;
} else if (strcmp(exp_str, SCF_PROPERTY_RESOURCE_POOL) == 0) {
if (set_attr_from_prop(exp_prop, n,
"resource_pool") != 0)
err = 1;
} else if (strcmp(exp_str, SCF_PROPERTY_SECFLAGS) == 0) {
if (set_attr_from_prop(exp_prop, n,
"security_flags") != 0)
err = 1;
} else if (strcmp(exp_str, SCF_PROPERTY_USE_PROFILE) == 0) {
/* EMPTY */
} else if (strcmp(exp_str, SCF_PROPERTY_USER) == 0) {
if (use_profile ||
set_attr_from_prop(exp_prop, cred, "user") != 0)
err = 1;
} else if (strcmp(exp_str, SCF_PROPERTY_GROUP) == 0) {
if (use_profile ||
set_attr_from_prop(exp_prop, cred, "group") != 0)
err = 1;
} else if (strcmp(exp_str, SCF_PROPERTY_SUPP_GROUPS) == 0) {
if (use_profile || set_attr_from_prop(exp_prop, cred,
"supp_groups") != 0)
err = 1;
} else if (strcmp(exp_str, SCF_PROPERTY_PRIVILEGES) == 0) {
if (use_profile || set_attr_from_prop(exp_prop, cred,
"privileges") != 0)
err = 1;
} else if (strcmp(exp_str, SCF_PROPERTY_LIMIT_PRIVILEGES) ==
0) {
if (use_profile || set_attr_from_prop(exp_prop, cred,
"limit_privileges") != 0)
err = 1;
} else if (strcmp(exp_str, SCF_PROPERTY_PROFILE) == 0) {
if (!use_profile || set_attr_from_prop(exp_prop,
prof, name_attr) != 0)
err = 1;
} else {
/* Can't have generic properties in method_context's */
err = 1;
}
}
if (ret == -1)
scfdie();
if (err && env == NULL) {
xmlFreeNode(n);
export_pg(pg, elts, SCE_ALL_VALUES);
return;
}
elts->method_context = n;
}
/*
* Given a dependency property group in the tfmri entity (target fmri), return
* a dependent element which represents it.
*/
static xmlNodePtr
export_dependent(scf_propertygroup_t *pg, const char *name, const char *tfmri)
{
uint8_t b;
xmlNodePtr n, sf;
int err = 0, ret;
struct pg_elts pgelts;
/*
* If external isn't set to true then exporting the service will
* export this as a normal dependency, so we should stop to avoid
* duplication.
*/
if (scf_pg_get_property(pg, scf_property_external, exp_prop) != 0 ||
scf_property_get_value(exp_prop, exp_val) != 0 ||
scf_value_get_boolean(exp_val, &b) != 0 || !b) {
if (g_verbose) {
warn(gettext("Dependent \"%s\" cannot be exported "
"properly because the \"%s\" property of the "
"\"%s\" dependency of %s is not set to true.\n"),
name, scf_property_external, name, tfmri);
}
return (NULL);
}
n = xmlNewNode(NULL, (xmlChar *)"dependent");
if (n == NULL)
uu_die(emsg_create_xml);
safe_setprop(n, name_attr, name);
/* Get the required attributes */
if (pg_get_prop(pg, SCF_PROPERTY_RESTART_ON, exp_prop) != 0 ||
set_attr_from_prop(exp_prop, n, "restart_on") != 0)
err = 1;
if (pg_get_prop(pg, SCF_PROPERTY_GROUPING, exp_prop) != 0 ||
set_attr_from_prop(exp_prop, n, "grouping") != 0)
err = 1;
if (pg_get_prop(pg, SCF_PROPERTY_ENTITIES, exp_prop) == 0 &&
prop_check_type(exp_prop, SCF_TYPE_FMRI) == 0 &&
prop_get_val(exp_prop, exp_val) == 0) {
/* EMPTY */
} else
err = 1;
if (err) {
xmlFreeNode(n);
return (NULL);
}
sf = xmlNewChild(n, NULL, (xmlChar *)"service_fmri", NULL);
if (sf == NULL)
uu_die(emsg_create_xml);
safe_setprop(sf, value_attr, tfmri);
/*
* Now add elements for the other properties.
*/
if (scf_iter_pg_properties(exp_prop_iter, pg) != SCF_SUCCESS)
scfdie();
(void) memset(&pgelts, 0, sizeof (pgelts));
while ((ret = scf_iter_next_property(exp_prop_iter, exp_prop)) == 1) {
if (scf_property_get_name(exp_prop, exp_str, exp_str_sz) < 0)
scfdie();
if (strcmp(exp_str, scf_property_external) == 0 ||
strcmp(exp_str, SCF_PROPERTY_RESTART_ON) == 0 ||
strcmp(exp_str, SCF_PROPERTY_GROUPING) == 0 ||
strcmp(exp_str, SCF_PROPERTY_ENTITIES) == 0) {
continue;
} else if (strcmp(exp_str, SCF_PROPERTY_TYPE) == 0) {
if (prop_check_type(exp_prop, SCF_TYPE_ASTRING) == 0 &&
prop_get_val(exp_prop, exp_val) == 0) {
char type[sizeof ("service") + 1];
if (scf_value_get_astring(exp_val, type,
sizeof (type)) < 0)
scfdie();
if (strcmp(type, "service") == 0)
continue;
}
} else if (strcmp(exp_str, SCF_PROPERTY_STABILITY) == 0) {
xmlNodePtr s;
s = xmlNewNode(NULL, (xmlChar *)"stability");
if (s == NULL)
uu_die(emsg_create_xml);
if (set_attr_from_prop(exp_prop, s, value_attr) == 0) {
pgelts.stability = s;
continue;
}
xmlFreeNode(s);
}
export_property(exp_prop, exp_str, &pgelts, SCE_ALL_VALUES);
}
if (ret == -1)
scfdie();
(void) xmlAddChild(n, pgelts.stability);
(void) xmlAddChildList(n, pgelts.propvals);
(void) xmlAddChildList(n, pgelts.properties);
return (n);
}
static void
export_dependents(scf_propertygroup_t *pg, struct entity_elts *eelts)
{
scf_propertygroup_t *opg;
scf_iter_t *iter;
char *type, *fmri;
int ret;
struct pg_elts pgelts;
xmlNodePtr n;
scf_error_t serr;
if ((opg = scf_pg_create(g_hndl)) == NULL ||
(iter = scf_iter_create(g_hndl)) == NULL)
scfdie();
/* Can't use exp_prop_iter due to export_dependent(). */
if (scf_iter_pg_properties(iter, pg) != SCF_SUCCESS)
scfdie();
type = safe_malloc(max_scf_pg_type_len + 1);
/* Get an extra byte so we can tell if values are too long. */
fmri = safe_malloc(max_scf_fmri_len + 2);
(void) memset(&pgelts, 0, sizeof (pgelts));
while ((ret = scf_iter_next_property(iter, exp_prop)) == 1) {
void *entity;
int isservice;
scf_type_t ty;
if (scf_property_type(exp_prop, &ty) != SCF_SUCCESS)
scfdie();
if ((ty != SCF_TYPE_ASTRING &&
prop_check_type(exp_prop, SCF_TYPE_FMRI) != 0) ||
prop_get_val(exp_prop, exp_val) != 0) {
export_property(exp_prop, NULL, &pgelts,
SCE_ALL_VALUES);
continue;
}
if (scf_property_get_name(exp_prop, exp_str, exp_str_sz) < 0)
scfdie();
if (scf_value_get_astring(exp_val, fmri,
max_scf_fmri_len + 2) < 0)
scfdie();
/* Look for a dependency group in the target fmri. */
serr = fmri_to_entity(g_hndl, fmri, &entity, &isservice);
switch (serr) {
case SCF_ERROR_NONE:
break;
case SCF_ERROR_NO_MEMORY:
uu_die(gettext("Out of memory.\n"));
/* NOTREACHED */
case SCF_ERROR_INVALID_ARGUMENT:
if (g_verbose) {
if (scf_property_to_fmri(exp_prop, fmri,
max_scf_fmri_len + 2) < 0)
scfdie();
warn(gettext("The value of %s is not a valid "
"FMRI.\n"), fmri);
}
export_property(exp_prop, exp_str, &pgelts,
SCE_ALL_VALUES);
continue;
case SCF_ERROR_CONSTRAINT_VIOLATED:
if (g_verbose) {
if (scf_property_to_fmri(exp_prop, fmri,
max_scf_fmri_len + 2) < 0)
scfdie();
warn(gettext("The value of %s does not specify "
"a service or an instance.\n"), fmri);
}
export_property(exp_prop, exp_str, &pgelts,
SCE_ALL_VALUES);
continue;
case SCF_ERROR_NOT_FOUND:
if (g_verbose) {
if (scf_property_to_fmri(exp_prop, fmri,
max_scf_fmri_len + 2) < 0)
scfdie();
warn(gettext("The entity specified by %s does "
"not exist.\n"), fmri);
}
export_property(exp_prop, exp_str, &pgelts,
SCE_ALL_VALUES);
continue;
default:
#ifndef NDEBUG
(void) fprintf(stderr, "%s:%d: %s() failed with "
"unexpected error %d.\n", __FILE__, __LINE__,
"fmri_to_entity", serr);
#endif
abort();
}
if (entity_get_pg(entity, isservice, exp_str, opg) != 0) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
warn(gettext("Entity %s is missing dependency property "
"group %s.\n"), fmri, exp_str);
export_property(exp_prop, NULL, &pgelts,
SCE_ALL_VALUES);
continue;
}
if (scf_pg_get_type(opg, type, max_scf_pg_type_len + 1) < 0)
scfdie();
if (strcmp(type, SCF_GROUP_DEPENDENCY) != 0) {
if (scf_pg_to_fmri(opg, fmri, max_scf_fmri_len + 2) < 0)
scfdie();
warn(gettext("Property group %s is not of "
"expected type %s.\n"), fmri, SCF_GROUP_DEPENDENCY);
export_property(exp_prop, NULL, &pgelts,
SCE_ALL_VALUES);
continue;
}
n = export_dependent(opg, exp_str, fmri);
if (n == NULL) {
export_property(exp_prop, exp_str, &pgelts,
SCE_ALL_VALUES);
} else {
if (eelts->dependents == NULL)
eelts->dependents = n;
else
(void) xmlAddSibling(eelts->dependents,
n);
}
}
if (ret == -1)
scfdie();
free(fmri);
free(type);
scf_iter_destroy(iter);
scf_pg_destroy(opg);
if (pgelts.propvals != NULL || pgelts.properties != NULL)
export_pg_elts(&pgelts, SCF_PG_DEPENDENTS, scf_group_framework,
eelts);
}
static void
make_node(xmlNodePtr *nodep, const char *name)
{
if (*nodep == NULL) {
*nodep = xmlNewNode(NULL, (xmlChar *)name);
if (*nodep == NULL)
uu_die(emsg_create_xml);
}
}
static xmlNodePtr
export_tm_loctext(scf_propertygroup_t *pg, const char *parname)
{
int ret;
xmlNodePtr parent = NULL;
xmlNodePtr loctext = NULL;
if (scf_iter_pg_properties(exp_prop_iter, pg) != SCF_SUCCESS)
scfdie();
while ((ret = scf_iter_next_property(exp_prop_iter, exp_prop)) == 1) {
if (prop_check_type(exp_prop, SCF_TYPE_USTRING) != 0 ||
prop_get_val(exp_prop, exp_val) != 0)
continue;
if (scf_value_get_ustring(exp_val, exp_str, exp_str_sz) < 0)
scfdie();
make_node(&parent, parname);
loctext = xmlNewTextChild(parent, NULL, (xmlChar *)"loctext",
(xmlChar *)exp_str);
if (loctext == NULL)
uu_die(emsg_create_xml);
if (scf_property_get_name(exp_prop, exp_str, exp_str_sz) < 0)
scfdie();
safe_setprop(loctext, "xml:lang", exp_str);
}
if (ret == -1)
scfdie();
return (parent);
}
static xmlNodePtr
export_tm_manpage(scf_propertygroup_t *pg)
{
xmlNodePtr manpage = xmlNewNode(NULL, (xmlChar *)"manpage");
if (manpage == NULL)
uu_die(emsg_create_xml);
if (pg_get_prop(pg, SCF_PROPERTY_TM_TITLE, exp_prop) != 0 ||
set_attr_from_prop(exp_prop, manpage, "title") != 0 ||
pg_get_prop(pg, SCF_PROPERTY_TM_SECTION, exp_prop) != 0 ||
set_attr_from_prop(exp_prop, manpage, "section") != 0) {
xmlFreeNode(manpage);
return (NULL);
}
if (pg_get_prop(pg, SCF_PROPERTY_TM_MANPATH, exp_prop) == 0)
(void) set_attr_from_prop_default(exp_prop,
manpage, "manpath", ":default");
return (manpage);
}
static xmlNodePtr
export_tm_doc_link(scf_propertygroup_t *pg)
{
xmlNodePtr doc_link = xmlNewNode(NULL, (xmlChar *)"doc_link");
if (doc_link == NULL)
uu_die(emsg_create_xml);
if (pg_get_prop(pg, SCF_PROPERTY_TM_NAME, exp_prop) != 0 ||
set_attr_from_prop(exp_prop, doc_link, "name") != 0 ||
pg_get_prop(pg, SCF_PROPERTY_TM_URI, exp_prop) != 0 ||
set_attr_from_prop(exp_prop, doc_link, "uri") != 0) {
xmlFreeNode(doc_link);
return (NULL);
}
return (doc_link);
}
/*
* Process template information for a service or instances.
*/
static void
export_template(scf_propertygroup_t *pg, struct entity_elts *elts,
struct template_elts *telts)
{
size_t mansz = strlen(SCF_PG_TM_MAN_PREFIX);
size_t docsz = strlen(SCF_PG_TM_DOC_PREFIX);
xmlNodePtr child = NULL;
if (scf_pg_get_name(pg, exp_str, exp_str_sz) < 0)
scfdie();
if (strcmp(exp_str, SCF_PG_TM_COMMON_NAME) == 0) {
telts->common_name = export_tm_loctext(pg, "common_name");
if (telts->common_name == NULL)
export_pg(pg, elts, SCE_ALL_VALUES);
return;
} else if (strcmp(exp_str, SCF_PG_TM_DESCRIPTION) == 0) {
telts->description = export_tm_loctext(pg, "description");
if (telts->description == NULL)
export_pg(pg, elts, SCE_ALL_VALUES);
return;
}
if (strncmp(exp_str, SCF_PG_TM_MAN_PREFIX, mansz) == 0) {
child = export_tm_manpage(pg);
} else if (strncmp(exp_str, SCF_PG_TM_DOC_PREFIX, docsz) == 0) {
child = export_tm_doc_link(pg);
}
if (child != NULL) {
make_node(&telts->documentation, "documentation");
(void) xmlAddChild(telts->documentation, child);
} else {
export_pg(pg, elts, SCE_ALL_VALUES);
}
}
/*
* Process parameter and paramval elements
*/
static void
export_parameter(scf_property_t *prop, const char *name,
struct params_elts *elts)
{
xmlNodePtr param;
scf_error_t err = 0;
int ret;
if (scf_property_get_value(prop, exp_val) == SCF_SUCCESS) {
if ((param = xmlNewNode(NULL, (xmlChar *)"paramval")) == NULL)
uu_die(emsg_create_xml);
safe_setprop(param, name_attr, name);
if (scf_value_get_as_string(exp_val, exp_str, exp_str_sz) < 0)
scfdie();
safe_setprop(param, value_attr, exp_str);
if (elts->paramval == NULL)
elts->paramval = param;
else
(void) xmlAddSibling(elts->paramval, param);
return;
}
err = scf_error();
if (err != SCF_ERROR_CONSTRAINT_VIOLATED &&
err != SCF_ERROR_NOT_FOUND)
scfdie();
if ((param = xmlNewNode(NULL, (xmlChar *)"parameter")) == NULL)
uu_die(emsg_create_xml);
safe_setprop(param, name_attr, name);
if (err == SCF_ERROR_CONSTRAINT_VIOLATED) {
if (scf_iter_property_values(exp_val_iter, prop) != SCF_SUCCESS)
scfdie();
while ((ret = scf_iter_next_value(exp_val_iter, exp_val)) ==
1) {
xmlNodePtr vn;
if ((vn = xmlNewChild(param, NULL,
(xmlChar *)"value_node", NULL)) == NULL)
uu_die(emsg_create_xml);
if (scf_value_get_as_string(exp_val, exp_str,
exp_str_sz) < 0)
scfdie();
safe_setprop(vn, value_attr, exp_str);
}
if (ret != 0)
scfdie();
}
if (elts->parameter == NULL)
elts->parameter = param;
else
(void) xmlAddSibling(elts->parameter, param);
}
/*
* Process notification parameters for a service or instance
*/
static void
export_notify_params(scf_propertygroup_t *pg, struct entity_elts *elts)
{
xmlNodePtr n, event, *type;
struct params_elts *eelts;
int ret, err, i;
n = xmlNewNode(NULL, (xmlChar *)"notification_parameters");
event = xmlNewNode(NULL, (xmlChar *)"event");
if (n == NULL || event == NULL)
uu_die(emsg_create_xml);
/* event value */
if (scf_pg_get_name(pg, exp_str, max_scf_name_len + 1) < 0)
scfdie();
safe_setprop(event, value_attr, exp_str);
(void) xmlAddChild(n, event);
if ((type = calloc(URI_SCHEME_NUM, sizeof (xmlNodePtr))) == NULL ||
(eelts = calloc(URI_SCHEME_NUM,
sizeof (struct params_elts))) == NULL)
uu_die(gettext("Out of memory.\n"));
err = 0;
if (scf_iter_pg_properties(exp_prop_iter, pg) != SCF_SUCCESS)
scfdie();
while ((ret = scf_iter_next_property(exp_prop_iter, exp_prop)) == 1) {
char *t, *p;
if (scf_property_get_name(exp_prop, exp_str, exp_str_sz) < 0)
scfdie();
if ((t = strtok_r(exp_str, ",", &p)) == NULL || p == NULL) {
/*
* this is not a well formed notification parameters
* element, we should export as regular pg
*/
err = 1;
break;
}
if ((i = check_uri_protocol(t)) < 0) {
err = 1;
break;
}
if (type[i] == NULL) {
if ((type[i] = xmlNewNode(NULL, (xmlChar *)"type")) ==
NULL)
uu_die(emsg_create_xml);
safe_setprop(type[i], name_attr, t);
}
if (strcmp(p, active_attr) == 0) {
if (set_attr_from_prop(exp_prop, type[i],
active_attr) != 0) {
err = 1;
break;
}
continue;
}
/*
* We export the parameter
*/
export_parameter(exp_prop, p, &eelts[i]);
}
if (ret == -1)
scfdie();
if (err == 1) {
for (i = 0; i < URI_SCHEME_NUM; ++i)
xmlFree(type[i]);
free(type);
export_pg(pg, elts, SCE_ALL_VALUES);
return;
} else {
for (i = 0; i < URI_SCHEME_NUM; ++i)
if (type[i] != NULL) {
(void) xmlAddChildList(type[i],
eelts[i].paramval);
(void) xmlAddChildList(type[i],
eelts[i].parameter);
(void) xmlAddSibling(event, type[i]);
}
}
free(type);
if (elts->notify_params == NULL)
elts->notify_params = n;
else
(void) xmlAddSibling(elts->notify_params, n);
}
/*
* Process the general property group for an instance.
*/
static void
export_inst_general(scf_propertygroup_t *pg, xmlNodePtr inode,
struct entity_elts *elts)
{
uint8_t enabled;
struct pg_elts pgelts;
int ret;
/* enabled */
if (pg_get_prop(pg, scf_property_enabled, exp_prop) == 0 &&
prop_check_type(exp_prop, SCF_TYPE_BOOLEAN) == 0 &&
prop_get_val(exp_prop, exp_val) == 0) {
if (scf_value_get_boolean(exp_val, &enabled) != SCF_SUCCESS)
scfdie();
} else {
enabled = 0;
}
safe_setprop(inode, enabled_attr, enabled ? true : false);
if (scf_iter_pg_properties(exp_prop_iter, pg) != SCF_SUCCESS)
scfdie();
(void) memset(&pgelts, 0, sizeof (pgelts));
while ((ret = scf_iter_next_property(exp_prop_iter, exp_prop)) == 1) {
if (scf_property_get_name(exp_prop, exp_str, exp_str_sz) < 0)
scfdie();
if (strcmp(exp_str, scf_property_enabled) == 0) {
continue;
} else if (strcmp(exp_str, SCF_PROPERTY_RESTARTER) == 0) {
xmlNodePtr rnode, sfnode;
rnode = xmlNewNode(NULL, (xmlChar *)"restarter");
if (rnode == NULL)
uu_die(emsg_create_xml);
sfnode = xmlNewChild(rnode, NULL,
(xmlChar *)"service_fmri", NULL);
if (sfnode == NULL)
uu_die(emsg_create_xml);
if (set_attr_from_prop(exp_prop, sfnode,
value_attr) == 0) {
elts->restarter = rnode;
continue;
}
xmlFreeNode(rnode);
}
export_property(exp_prop, exp_str, &pgelts, SCE_ALL_VALUES);
}
if (ret == -1)
scfdie();
if (pgelts.propvals != NULL || pgelts.properties != NULL)
export_pg_elts(&pgelts, scf_pg_general, scf_group_framework,
elts);
}
/*
* Put an instance element for the given instance into selts.
*/
static void
export_instance(scf_instance_t *inst, struct entity_elts *selts, int flags)
{
xmlNodePtr n;
boolean_t isdefault;
struct entity_elts elts;
struct template_elts template_elts;
int ret;
n = xmlNewNode(NULL, (xmlChar *)"instance");
if (n == NULL)
uu_die(emsg_create_xml);
/* name */
if (scf_instance_get_name(inst, exp_str, exp_str_sz) < 0)
scfdie();
safe_setprop(n, name_attr, exp_str);
isdefault = strcmp(exp_str, "default") == 0;
/* check existance of general pg (since general/enabled is required) */
if (scf_instance_get_pg(inst, scf_pg_general, exp_pg) != SCF_SUCCESS) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
if (g_verbose) {
if (scf_instance_to_fmri(inst, exp_str, exp_str_sz) < 0)
scfdie();
warn(gettext("Instance %s has no general property "
"group; it will be marked disabled.\n"), exp_str);
}
safe_setprop(n, enabled_attr, false);
} else if (scf_pg_get_type(exp_pg, exp_str, exp_str_sz) < 0 ||
strcmp(exp_str, scf_group_framework) != 0) {
if (g_verbose) {
if (scf_pg_to_fmri(exp_pg, exp_str, exp_str_sz) < 0)
scfdie();
warn(gettext("Property group %s is not of type "
"framework; the instance will be marked "
"disabled.\n"), exp_str);
}
safe_setprop(n, enabled_attr, false);
}
/* property groups */
if (scf_iter_instance_pgs(exp_pg_iter, inst) < 0)
scfdie();
(void) memset(&elts, 0, sizeof (elts));
(void) memset(&template_elts, 0, sizeof (template_elts));
while ((ret = scf_iter_next_pg(exp_pg_iter, exp_pg)) == 1) {
uint32_t pgflags;
if (scf_pg_get_flags(exp_pg, &pgflags) != 0)
scfdie();
if (pgflags & SCF_PG_FLAG_NONPERSISTENT)
continue;
if (scf_pg_get_type(exp_pg, exp_str, exp_str_sz) < 0)
scfdie();
if (strcmp(exp_str, SCF_GROUP_DEPENDENCY) == 0) {
export_dependency(exp_pg, &elts);
continue;
} else if (strcmp(exp_str, SCF_GROUP_METHOD) == 0) {
export_method(exp_pg, &elts);
continue;
} else if (strcmp(exp_str, scf_group_framework) == 0) {
if (scf_pg_get_name(exp_pg, exp_str,
max_scf_name_len + 1) < 0)
scfdie();
if (strcmp(exp_str, scf_pg_general) == 0) {
export_inst_general(exp_pg, n, &elts);
continue;
} else if (strcmp(exp_str, SCF_PG_METHOD_CONTEXT) ==
0) {
export_method_context(exp_pg, &elts);
continue;
} else if (strcmp(exp_str, SCF_PG_DEPENDENTS) == 0) {
export_dependents(exp_pg, &elts);
continue;
}
} else if (strcmp(exp_str, SCF_GROUP_TEMPLATE) == 0) {
export_template(exp_pg, &elts, &template_elts);
continue;
} else if (strcmp(exp_str, SCF_NOTIFY_PARAMS_PG_TYPE) == 0) {
export_notify_params(exp_pg, &elts);
continue;
}
/* Ordinary pg. */
export_pg(exp_pg, &elts, flags);
}
if (ret == -1)
scfdie();
if (template_elts.common_name != NULL) {
elts.template = xmlNewNode(NULL, (xmlChar *)"template");
(void) xmlAddChild(elts.template, template_elts.common_name);
(void) xmlAddChild(elts.template, template_elts.description);
(void) xmlAddChild(elts.template, template_elts.documentation);
} else {
xmlFreeNode(template_elts.description);
xmlFreeNode(template_elts.documentation);
}
if (isdefault && elts.restarter == NULL &&
elts.dependencies == NULL && elts.method_context == NULL &&
elts.exec_methods == NULL && elts.notify_params == NULL &&
elts.property_groups == NULL && elts.template == NULL) {
xmlChar *eval;
/* This is a default instance */
eval = xmlGetProp(n, (xmlChar *)enabled_attr);
xmlFreeNode(n);
n = xmlNewNode(NULL, (xmlChar *)"create_default_instance");
if (n == NULL)
uu_die(emsg_create_xml);
safe_setprop(n, enabled_attr, (char *)eval);
xmlFree(eval);
selts->create_default_instance = n;
} else {
/* Assemble the children in order. */
(void) xmlAddChild(n, elts.restarter);
(void) xmlAddChildList(n, elts.dependencies);
(void) xmlAddChildList(n, elts.dependents);
(void) xmlAddChild(n, elts.method_context);
(void) xmlAddChildList(n, elts.exec_methods);
(void) xmlAddChildList(n, elts.notify_params);
(void) xmlAddChildList(n, elts.property_groups);
(void) xmlAddChild(n, elts.template);
if (selts->instances == NULL)
selts->instances = n;
else
(void) xmlAddSibling(selts->instances, n);
}
}
/*
* Return a service element for the given service.
*/
static xmlNodePtr
export_service(scf_service_t *svc, int flags)
{
xmlNodePtr snode;
struct entity_elts elts;
struct template_elts template_elts;
int ret;
snode = xmlNewNode(NULL, (xmlChar *)"service");
if (snode == NULL)
uu_die(emsg_create_xml);
/* Get & set name attribute */
if (scf_service_get_name(svc, exp_str, max_scf_name_len + 1) < 0)
scfdie();
safe_setprop(snode, name_attr, exp_str);
safe_setprop(snode, type_attr, "service");
safe_setprop(snode, "version", "0");
/* Acquire child elements. */
if (scf_iter_service_pgs(exp_pg_iter, svc) != SCF_SUCCESS)
scfdie();
(void) memset(&elts, 0, sizeof (elts));
(void) memset(&template_elts, 0, sizeof (template_elts));
while ((ret = scf_iter_next_pg(exp_pg_iter, exp_pg)) == 1) {
uint32_t pgflags;
if (scf_pg_get_flags(exp_pg, &pgflags) != 0)
scfdie();
if (pgflags & SCF_PG_FLAG_NONPERSISTENT)
continue;
if (scf_pg_get_type(exp_pg, exp_str, exp_str_sz) < 0)
scfdie();
if (strcmp(exp_str, SCF_GROUP_DEPENDENCY) == 0) {
export_dependency(exp_pg, &elts);
continue;
} else if (strcmp(exp_str, SCF_GROUP_METHOD) == 0) {
export_method(exp_pg, &elts);
continue;
} else if (strcmp(exp_str, scf_group_framework) == 0) {
if (scf_pg_get_name(exp_pg, exp_str,
max_scf_name_len + 1) < 0)
scfdie();
if (strcmp(exp_str, scf_pg_general) == 0) {
export_svc_general(exp_pg, &elts);
continue;
} else if (strcmp(exp_str, SCF_PG_METHOD_CONTEXT) ==
0) {
export_method_context(exp_pg, &elts);
continue;
} else if (strcmp(exp_str, SCF_PG_DEPENDENTS) == 0) {
export_dependents(exp_pg, &elts);
continue;
} else if (strcmp(exp_str, SCF_PG_MANIFESTFILES) == 0) {
continue;
}
} else if (strcmp(exp_str, SCF_GROUP_TEMPLATE) == 0) {
export_template(exp_pg, &elts, &template_elts);
continue;
} else if (strcmp(exp_str, SCF_NOTIFY_PARAMS_PG_TYPE) == 0) {
export_notify_params(exp_pg, &elts);
continue;
}
export_pg(exp_pg, &elts, flags);
}
if (ret == -1)
scfdie();
if (template_elts.common_name != NULL) {
elts.template = xmlNewNode(NULL, (xmlChar *)"template");
(void) xmlAddChild(elts.template, template_elts.common_name);
(void) xmlAddChild(elts.template, template_elts.description);
(void) xmlAddChild(elts.template, template_elts.documentation);
} else {
xmlFreeNode(template_elts.description);
xmlFreeNode(template_elts.documentation);
}
/* Iterate instances */
if (scf_iter_service_instances(exp_inst_iter, svc) != SCF_SUCCESS)
scfdie();
while ((ret = scf_iter_next_instance(exp_inst_iter, exp_inst)) == 1)
export_instance(exp_inst, &elts, flags);
if (ret == -1)
scfdie();
/* Now add all of the accumulated elements in order. */
(void) xmlAddChild(snode, elts.create_default_instance);
(void) xmlAddChild(snode, elts.single_instance);
(void) xmlAddChild(snode, elts.restarter);
(void) xmlAddChildList(snode, elts.dependencies);
(void) xmlAddChildList(snode, elts.dependents);
(void) xmlAddChild(snode, elts.method_context);
(void) xmlAddChildList(snode, elts.exec_methods);
(void) xmlAddChildList(snode, elts.notify_params);
(void) xmlAddChildList(snode, elts.property_groups);
(void) xmlAddChildList(snode, elts.instances);
(void) xmlAddChild(snode, elts.stability);
(void) xmlAddChild(snode, elts.template);
return (snode);
}
static int
export_callback(void *data, scf_walkinfo_t *wip)
{
FILE *f;
xmlDocPtr doc;
xmlNodePtr sb;
int result;
struct export_args *argsp = (struct export_args *)data;
if ((exp_inst = scf_instance_create(g_hndl)) == NULL ||
(exp_pg = scf_pg_create(g_hndl)) == NULL ||
(exp_prop = scf_property_create(g_hndl)) == NULL ||
(exp_val = scf_value_create(g_hndl)) == NULL ||
(exp_inst_iter = scf_iter_create(g_hndl)) == NULL ||
(exp_pg_iter = scf_iter_create(g_hndl)) == NULL ||
(exp_prop_iter = scf_iter_create(g_hndl)) == NULL ||
(exp_val_iter = scf_iter_create(g_hndl)) == NULL)
scfdie();
exp_str_sz = max_scf_len + 1;
exp_str = safe_malloc(exp_str_sz);
if (argsp->filename != NULL) {
errno = 0;
f = fopen(argsp->filename, "wb");
if (f == NULL) {
if (errno == 0)
uu_die(gettext("Could not open \"%s\": no free "
"stdio streams.\n"), argsp->filename);
else
uu_die(gettext("Could not open \"%s\""),
argsp->filename);
}
} else
f = stdout;
doc = xmlNewDoc((xmlChar *)"1.0");
if (doc == NULL)
uu_die(gettext("Could not create XML document.\n"));
if (xmlCreateIntSubset(doc, (xmlChar *)"service_bundle", NULL,
(xmlChar *)MANIFEST_DTD_PATH) == NULL)
uu_die(emsg_create_xml);
sb = xmlNewNode(NULL, (xmlChar *)"service_bundle");
if (sb == NULL)
uu_die(emsg_create_xml);
safe_setprop(sb, type_attr, "manifest");
safe_setprop(sb, name_attr, "export");
(void) xmlAddSibling(doc->children, sb);
(void) xmlAddChild(sb, export_service(wip->svc, argsp->flags));
result = write_service_bundle(doc, f);
free(exp_str);
scf_iter_destroy(exp_val_iter);
scf_iter_destroy(exp_prop_iter);
scf_iter_destroy(exp_pg_iter);
scf_iter_destroy(exp_inst_iter);
scf_value_destroy(exp_val);
scf_property_destroy(exp_prop);
scf_pg_destroy(exp_pg);
scf_instance_destroy(exp_inst);
xmlFreeDoc(doc);
if (f != stdout)
(void) fclose(f);
return (result);
}
/*
* Get the service named by fmri, build an XML tree which represents it, and
* dump it into filename (or stdout if filename is NULL).
*/
int
lscf_service_export(char *fmri, const char *filename, int flags)
{
struct export_args args;
char *fmridup;
const char *scope, *svc, *inst;
size_t cblen = 3 * max_scf_name_len;
char *canonbuf = alloca(cblen);
int ret, err;
lscf_prep_hndl();
bzero(&args, sizeof (args));
args.filename = filename;
args.flags = flags;
/*
* If some poor user has passed an exact instance FMRI, of the sort
* one might cut and paste from svcs(1) or an error message, warn
* and chop off the instance instead of failing.
*/
fmridup = alloca(strlen(fmri) + 1);
(void) strcpy(fmridup, fmri);
if (strncmp(fmridup, SCF_FMRI_SVC_PREFIX,
sizeof (SCF_FMRI_SVC_PREFIX) -1) == 0 &&
scf_parse_svc_fmri(fmridup, &scope, &svc, &inst, NULL, NULL) == 0 &&
inst != NULL) {
(void) strlcpy(canonbuf, "svc:/", cblen);
if (strcmp(scope, SCF_FMRI_LOCAL_SCOPE) != 0) {
(void) strlcat(canonbuf, "/", cblen);
(void) strlcat(canonbuf, scope, cblen);
}
(void) strlcat(canonbuf, svc, cblen);
fmri = canonbuf;
warn(gettext("Only services may be exported; ignoring "
"instance portion of argument.\n"));
}
err = 0;
if ((ret = scf_walk_fmri(g_hndl, 1, (char **)&fmri,
SCF_WALK_SERVICE | SCF_WALK_NOINSTANCE, export_callback,
&args, &err, semerr)) != 0) {
if (ret != -1)
semerr(gettext("Failed to walk instances: %s\n"),
scf_strerror(ret));
return (-1);
}
/*
* Error message has already been printed.
*/
if (err != 0)
return (-1);
return (0);
}
/*
* Archive
*/
static xmlNodePtr
make_archive(int flags)
{
xmlNodePtr sb;
scf_scope_t *scope;
scf_service_t *svc;
scf_iter_t *iter;
int r;
if ((scope = scf_scope_create(g_hndl)) == NULL ||
(svc = scf_service_create(g_hndl)) == NULL ||
(iter = scf_iter_create(g_hndl)) == NULL ||
(exp_inst = scf_instance_create(g_hndl)) == NULL ||
(exp_pg = scf_pg_create(g_hndl)) == NULL ||
(exp_prop = scf_property_create(g_hndl)) == NULL ||
(exp_val = scf_value_create(g_hndl)) == NULL ||
(exp_inst_iter = scf_iter_create(g_hndl)) == NULL ||
(exp_pg_iter = scf_iter_create(g_hndl)) == NULL ||
(exp_prop_iter = scf_iter_create(g_hndl)) == NULL ||
(exp_val_iter = scf_iter_create(g_hndl)) == NULL)
scfdie();
exp_str_sz = max_scf_len + 1;
exp_str = safe_malloc(exp_str_sz);
sb = xmlNewNode(NULL, (xmlChar *)"service_bundle");
if (sb == NULL)
uu_die(emsg_create_xml);
safe_setprop(sb, type_attr, "archive");
safe_setprop(sb, name_attr, "none");
if (scf_handle_get_scope(g_hndl, SCF_SCOPE_LOCAL, scope) != 0)
scfdie();
if (scf_iter_scope_services(iter, scope) != 0)
scfdie();
for (;;) {
r = scf_iter_next_service(iter, svc);
if (r == 0)
break;
if (r != 1)
scfdie();
if (scf_service_get_name(svc, exp_str,
max_scf_name_len + 1) < 0)
scfdie();
if (strcmp(exp_str, SCF_LEGACY_SERVICE) == 0)
continue;
(void) xmlAddChild(sb, export_service(svc, flags));
}
free(exp_str);
scf_iter_destroy(exp_val_iter);
scf_iter_destroy(exp_prop_iter);
scf_iter_destroy(exp_pg_iter);
scf_iter_destroy(exp_inst_iter);
scf_value_destroy(exp_val);
scf_property_destroy(exp_prop);
scf_pg_destroy(exp_pg);
scf_instance_destroy(exp_inst);
scf_iter_destroy(iter);
scf_service_destroy(svc);
scf_scope_destroy(scope);
return (sb);
}
int
lscf_archive(const char *filename, int flags)
{
FILE *f;
xmlDocPtr doc;
int result;
lscf_prep_hndl();
if (filename != NULL) {
errno = 0;
f = fopen(filename, "wb");
if (f == NULL) {
if (errno == 0)
uu_die(gettext("Could not open \"%s\": no free "
"stdio streams.\n"), filename);
else
uu_die(gettext("Could not open \"%s\""),
filename);
}
} else
f = stdout;
doc = xmlNewDoc((xmlChar *)"1.0");
if (doc == NULL)
uu_die(gettext("Could not create XML document.\n"));
if (xmlCreateIntSubset(doc, (xmlChar *)"service_bundle", NULL,
(xmlChar *)MANIFEST_DTD_PATH) == NULL)
uu_die(emsg_create_xml);
(void) xmlAddSibling(doc->children, make_archive(flags));
result = write_service_bundle(doc, f);
xmlFreeDoc(doc);
if (f != stdout)
(void) fclose(f);
return (result);
}
/*
* "Extract" a profile.
*/
int
lscf_profile_extract(const char *filename)
{
FILE *f;
xmlDocPtr doc;
xmlNodePtr sb, snode, inode;
scf_scope_t *scope;
scf_service_t *svc;
scf_instance_t *inst;
scf_propertygroup_t *pg;
scf_property_t *prop;
scf_value_t *val;
scf_iter_t *siter, *iiter;
int r, s;
char *namebuf;
uint8_t b;
int result;
lscf_prep_hndl();
if (filename != NULL) {
errno = 0;
f = fopen(filename, "wb");
if (f == NULL) {
if (errno == 0)
uu_die(gettext("Could not open \"%s\": no "
"free stdio streams.\n"), filename);
else
uu_die(gettext("Could not open \"%s\""),
filename);
}
} else
f = stdout;
doc = xmlNewDoc((xmlChar *)"1.0");
if (doc == NULL)
uu_die(gettext("Could not create XML document.\n"));
if (xmlCreateIntSubset(doc, (xmlChar *)"service_bundle", NULL,
(xmlChar *)MANIFEST_DTD_PATH) == NULL)
uu_die(emsg_create_xml);
sb = xmlNewNode(NULL, (xmlChar *)"service_bundle");
if (sb == NULL)
uu_die(emsg_create_xml);
safe_setprop(sb, type_attr, "profile");
safe_setprop(sb, name_attr, "extract");
(void) xmlAddSibling(doc->children, sb);
if ((scope = scf_scope_create(g_hndl)) == NULL ||
(svc = scf_service_create(g_hndl)) == NULL ||
(inst = scf_instance_create(g_hndl)) == NULL ||
(pg = scf_pg_create(g_hndl)) == NULL ||
(prop = scf_property_create(g_hndl)) == NULL ||
(val = scf_value_create(g_hndl)) == NULL ||
(siter = scf_iter_create(g_hndl)) == NULL ||
(iiter = scf_iter_create(g_hndl)) == NULL)
scfdie();
if (scf_handle_get_local_scope(g_hndl, scope) != SCF_SUCCESS)
scfdie();
if (scf_iter_scope_services(siter, scope) != SCF_SUCCESS)
scfdie();
namebuf = safe_malloc(max_scf_name_len + 1);
while ((r = scf_iter_next_service(siter, svc)) == 1) {
if (scf_iter_service_instances(iiter, svc) != SCF_SUCCESS)
scfdie();
snode = xmlNewNode(NULL, (xmlChar *)"service");
if (snode == NULL)
uu_die(emsg_create_xml);
if (scf_service_get_name(svc, namebuf, max_scf_name_len + 1) <
0)
scfdie();
safe_setprop(snode, name_attr, namebuf);
safe_setprop(snode, type_attr, "service");
safe_setprop(snode, "version", "0");
while ((s = scf_iter_next_instance(iiter, inst)) == 1) {
if (scf_instance_get_pg(inst, scf_pg_general, pg) !=
SCF_SUCCESS) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
if (g_verbose) {
ssize_t len;
char *fmri;
len =
scf_instance_to_fmri(inst, NULL, 0);
if (len < 0)
scfdie();
fmri = safe_malloc(len + 1);
if (scf_instance_to_fmri(inst, fmri,
len + 1) < 0)
scfdie();
warn("Instance %s has no \"%s\" "
"property group.\n", fmri,
scf_pg_general);
free(fmri);
}
continue;
}
if (pg_get_prop(pg, scf_property_enabled, prop) != 0 ||
prop_check_type(prop, SCF_TYPE_BOOLEAN) != 0 ||
prop_get_val(prop, val) != 0)
continue;
inode = xmlNewChild(snode, NULL, (xmlChar *)"instance",
NULL);
if (inode == NULL)
uu_die(emsg_create_xml);
if (scf_instance_get_name(inst, namebuf,
max_scf_name_len + 1) < 0)
scfdie();
safe_setprop(inode, name_attr, namebuf);
if (scf_value_get_boolean(val, &b) != SCF_SUCCESS)
scfdie();
safe_setprop(inode, enabled_attr, b ? true : false);
}
if (s < 0)
scfdie();
if (snode->children != NULL)
(void) xmlAddChild(sb, snode);
else
xmlFreeNode(snode);
}
if (r < 0)
scfdie();
free(namebuf);
result = write_service_bundle(doc, f);
xmlFreeDoc(doc);
if (f != stdout)
(void) fclose(f);
return (result);
}
/*
* Entity manipulation commands
*/
/*
* Entity selection. If no entity is selected, then the current scope is in
* cur_scope, and cur_svc and cur_inst are NULL. When a service is selected,
* only cur_inst is NULL, and when an instance is selected, none are NULL.
* When the snaplevel of a snapshot is selected, cur_level, cur_snap, and
* cur_inst will be non-NULL.
*/
/* Returns 1 if maybe absolute fmri, 0 on success (dies on failure) */
static int
select_inst(const char *name)
{
scf_instance_t *inst;
scf_error_t err;
assert(cur_svc != NULL);
inst = scf_instance_create(g_hndl);
if (inst == NULL)
scfdie();
if (scf_service_get_instance(cur_svc, name, inst) == SCF_SUCCESS) {
cur_inst = inst;
return (0);
}
err = scf_error();
if (err != SCF_ERROR_NOT_FOUND && err != SCF_ERROR_INVALID_ARGUMENT)
scfdie();
scf_instance_destroy(inst);
return (1);
}
/* Returns as above. */
static int
select_svc(const char *name)
{
scf_service_t *svc;
scf_error_t err;
assert(cur_scope != NULL);
svc = scf_service_create(g_hndl);
if (svc == NULL)
scfdie();
if (scf_scope_get_service(cur_scope, name, svc) == SCF_SUCCESS) {
cur_svc = svc;
return (0);
}
err = scf_error();
if (err != SCF_ERROR_NOT_FOUND && err != SCF_ERROR_INVALID_ARGUMENT)
scfdie();
scf_service_destroy(svc);
return (1);
}
/* ARGSUSED */
static int
select_callback(void *unused, scf_walkinfo_t *wip)
{
scf_instance_t *inst;
scf_service_t *svc;
scf_scope_t *scope;
if (wip->inst != NULL) {
if ((scope = scf_scope_create(g_hndl)) == NULL ||
(svc = scf_service_create(g_hndl)) == NULL ||
(inst = scf_instance_create(g_hndl)) == NULL)
scfdie();
if (scf_handle_decode_fmri(g_hndl, wip->fmri, scope, svc,
inst, NULL, NULL, SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS)
scfdie();
} else {
assert(wip->svc != NULL);
if ((scope = scf_scope_create(g_hndl)) == NULL ||
(svc = scf_service_create(g_hndl)) == NULL)
scfdie();
if (scf_handle_decode_fmri(g_hndl, wip->fmri, scope, svc,
NULL, NULL, NULL, SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS)
scfdie();
inst = NULL;
}
/* Clear out the current selection */
assert(cur_scope != NULL);
scf_scope_destroy(cur_scope);
scf_service_destroy(cur_svc);
scf_instance_destroy(cur_inst);
cur_scope = scope;
cur_svc = svc;
cur_inst = inst;
return (0);
}
static int
validate_callback(void *fmri_p, scf_walkinfo_t *wip)
{
char **fmri = fmri_p;
*fmri = strdup(wip->fmri);
if (*fmri == NULL)
uu_die(gettext("Out of memory.\n"));
return (0);
}
/*
* validate [fmri]
* Perform the validation of an FMRI instance.
*/
void
lscf_validate_fmri(const char *fmri)
{
int ret = 0;
size_t inst_sz;
char *inst_fmri = NULL;
scf_tmpl_errors_t *errs = NULL;
char *snapbuf = NULL;
lscf_prep_hndl();
if (fmri == NULL) {
inst_sz = max_scf_fmri_len + 1;
inst_fmri = safe_malloc(inst_sz);
if (cur_snap != NULL) {
snapbuf = safe_malloc(max_scf_name_len + 1);
if (scf_snapshot_get_name(cur_snap, snapbuf,
max_scf_name_len + 1) < 0)
scfdie();
}
if (cur_inst == NULL) {
semerr(gettext("No instance selected\n"));
goto cleanup;
} else if (scf_instance_to_fmri(cur_inst, inst_fmri,
inst_sz) >= inst_sz) {
/* sanity check. Should never get here */
uu_die(gettext("Unexpected error! file %s, line %d\n"),
__FILE__, __LINE__);
}
} else {
scf_error_t scf_err;
int err = 0;
if ((scf_err = scf_walk_fmri(g_hndl, 1, (char **)&fmri, 0,
validate_callback, &inst_fmri, &err, semerr)) != 0) {
uu_warn("Failed to walk instances: %s\n",
scf_strerror(scf_err));
goto cleanup;
}
if (err != 0) {
/* error message displayed by scf_walk_fmri */
goto cleanup;
}
}
ret = scf_tmpl_validate_fmri(g_hndl, inst_fmri, snapbuf, &errs,
SCF_TMPL_VALIDATE_FLAG_CURRENT);
if (ret == -1) {
if (scf_error() == SCF_ERROR_TEMPLATE_INVALID) {
warn(gettext("Template data for %s is invalid. "
"Consider reverting to a previous snapshot or "
"restoring original configuration.\n"), inst_fmri);
} else {
uu_warn("%s: %s\n",
gettext("Error validating the instance"),
scf_strerror(scf_error()));
}
} else if (ret == 1 && errs != NULL) {
scf_tmpl_error_t *err = NULL;
char *msg;
size_t len = 256; /* initial error buffer size */
int flag = (est->sc_cmd_flags & SC_CMD_IACTIVE) ?
SCF_TMPL_STRERROR_HUMAN : 0;
msg = safe_malloc(len);
while ((err = scf_tmpl_next_error(errs)) != NULL) {
int ret;
if ((ret = scf_tmpl_strerror(err, msg, len,
flag)) >= len) {
len = ret + 1;
msg = realloc(msg, len);
if (msg == NULL)
uu_die(gettext(
"Out of memory.\n"));
(void) scf_tmpl_strerror(err, msg, len,
flag);
}
(void) fprintf(stderr, "%s\n", msg);
}
if (msg != NULL)
free(msg);
}
if (errs != NULL)
scf_tmpl_errors_destroy(errs);
cleanup:
free(inst_fmri);
free(snapbuf);
}
static void
lscf_validate_file(const char *filename)
{
tmpl_errors_t *errs;
bundle_t *b = internal_bundle_new();
if (lxml_get_bundle_file(b, filename, SVCCFG_OP_IMPORT) == 0) {
if (tmpl_validate_bundle(b, &errs) != TVS_SUCCESS) {
tmpl_errors_print(stderr, errs, "");
semerr(gettext("Validation failed.\n"));
}
tmpl_errors_destroy(errs);
}
(void) internal_bundle_free(b);
}
/*
* validate [fmri|file]
*/
void
lscf_validate(const char *arg)
{
const char *str;
if (strncmp(arg, SCF_FMRI_FILE_PREFIX,
sizeof (SCF_FMRI_FILE_PREFIX) - 1) == 0) {
str = arg + sizeof (SCF_FMRI_FILE_PREFIX) - 1;
lscf_validate_file(str);
} else if (strncmp(arg, SCF_FMRI_SVC_PREFIX,
sizeof (SCF_FMRI_SVC_PREFIX) - 1) == 0) {
str = arg + sizeof (SCF_FMRI_SVC_PREFIX) - 1;
lscf_validate_fmri(str);
} else if (access(arg, R_OK | F_OK) == 0) {
lscf_validate_file(arg);
} else {
lscf_validate_fmri(arg);
}
}
void
lscf_select(const char *fmri)
{
int ret, err;
lscf_prep_hndl();
if (cur_snap != NULL) {
struct snaplevel *elt;
char *buf;
/* Error unless name is that of the next level. */
elt = uu_list_next(cur_levels, cur_elt);
if (elt == NULL) {
semerr(gettext("No children.\n"));
return;
}
buf = safe_malloc(max_scf_name_len + 1);
if (scf_snaplevel_get_instance_name(elt->sl, buf,
max_scf_name_len + 1) < 0)
scfdie();
if (strcmp(buf, fmri) != 0) {
semerr(gettext("No such child.\n"));
free(buf);
return;
}
free(buf);
cur_elt = elt;
cur_level = elt->sl;
return;
}
/*
* Special case for 'svc:', which takes the user to the scope level.
*/
if (strcmp(fmri, "svc:") == 0) {
scf_instance_destroy(cur_inst);
scf_service_destroy(cur_svc);
cur_inst = NULL;
cur_svc = NULL;
return;
}
/*
* Special case for ':properties'. This appears as part of 'list' but
* can't be selected. Give a more helpful error message in this case.
*/
if (strcmp(fmri, ":properties") == 0) {
semerr(gettext(":properties is not an entity. Try 'listprop' "
"to list properties.\n"));
return;
}
/*
* First try the argument as relative to the current selection.
*/
if (cur_inst != NULL) {
/* EMPTY */;
} else if (cur_svc != NULL) {
if (select_inst(fmri) != 1)
return;
} else {
if (select_svc(fmri) != 1)
return;
}
err = 0;
if ((ret = scf_walk_fmri(g_hndl, 1, (char **)&fmri, SCF_WALK_SERVICE,
select_callback, NULL, &err, semerr)) != 0) {
semerr(gettext("Failed to walk instances: %s\n"),
scf_strerror(ret));
}
}
void
lscf_unselect(void)
{
lscf_prep_hndl();
if (cur_snap != NULL) {
struct snaplevel *elt;
elt = uu_list_prev(cur_levels, cur_elt);
if (elt == NULL) {
semerr(gettext("No parent levels.\n"));
} else {
cur_elt = elt;
cur_level = elt->sl;
}
} else if (cur_inst != NULL) {
scf_instance_destroy(cur_inst);
cur_inst = NULL;
} else if (cur_svc != NULL) {
scf_service_destroy(cur_svc);
cur_svc = NULL;
} else {
semerr(gettext("Cannot unselect at scope level.\n"));
}
}
/*
* Return the FMRI of the current selection, for the prompt.
*/
void
lscf_get_selection_str(char *buf, size_t bufsz)
{
char *cp;
ssize_t fmrilen, szret;
boolean_t deleted = B_FALSE;
if (g_hndl == NULL) {
(void) strlcpy(buf, "svc:", bufsz);
return;
}
if (cur_level != NULL) {
assert(cur_snap != NULL);
/* [ snapshot ] FMRI [: instance ] */
assert(bufsz >= 1 + max_scf_name_len + 1 + max_scf_fmri_len
+ 2 + max_scf_name_len + 1 + 1);
buf[0] = '[';
szret = scf_snapshot_get_name(cur_snap, buf + 1,
max_scf_name_len + 1);
if (szret < 0) {
if (scf_error() != SCF_ERROR_DELETED)
scfdie();
goto snap_deleted;
}
(void) strcat(buf, "]svc:/");
cp = strchr(buf, '\0');
szret = scf_snaplevel_get_service_name(cur_level, cp,
max_scf_name_len + 1);
if (szret < 0) {
if (scf_error() != SCF_ERROR_DELETED)
scfdie();
goto snap_deleted;
}
cp = strchr(cp, '\0');
if (snaplevel_is_instance(cur_level)) {
*cp++ = ':';
if (scf_snaplevel_get_instance_name(cur_level, cp,
max_scf_name_len + 1) < 0) {
if (scf_error() != SCF_ERROR_DELETED)
scfdie();
goto snap_deleted;
}
} else {
*cp++ = '[';
*cp++ = ':';
if (scf_instance_get_name(cur_inst, cp,
max_scf_name_len + 1) < 0) {
if (scf_error() != SCF_ERROR_DELETED)
scfdie();
goto snap_deleted;
}
(void) strcat(buf, "]");
}
return;
snap_deleted:
deleted = B_TRUE;
free(buf);
unselect_cursnap();
}
assert(cur_snap == NULL);
if (cur_inst != NULL) {
assert(cur_svc != NULL);
assert(cur_scope != NULL);
fmrilen = scf_instance_to_fmri(cur_inst, buf, bufsz);
if (fmrilen >= 0) {
assert(fmrilen < bufsz);
if (deleted)
warn(emsg_deleted);
return;
}
if (scf_error() != SCF_ERROR_DELETED)
scfdie();
deleted = B_TRUE;
scf_instance_destroy(cur_inst);
cur_inst = NULL;
}
if (cur_svc != NULL) {
assert(cur_scope != NULL);
szret = scf_service_to_fmri(cur_svc, buf, bufsz);
if (szret >= 0) {
assert(szret < bufsz);
if (deleted)
warn(emsg_deleted);
return;
}
if (scf_error() != SCF_ERROR_DELETED)
scfdie();
deleted = B_TRUE;
scf_service_destroy(cur_svc);
cur_svc = NULL;
}
assert(cur_scope != NULL);
fmrilen = scf_scope_to_fmri(cur_scope, buf, bufsz);
if (fmrilen < 0)
scfdie();
assert(fmrilen < bufsz);
if (deleted)
warn(emsg_deleted);
}
/*
* Entity listing. Entities and colon namespaces (e.g., :properties and
* :statistics) are listed for the current selection.
*/
void
lscf_list(const char *pattern)
{
scf_iter_t *iter;
char *buf;
int ret;
lscf_prep_hndl();
if (cur_level != NULL) {
struct snaplevel *elt;
(void) fputs(COLON_NAMESPACES, stdout);
elt = uu_list_next(cur_levels, cur_elt);
if (elt == NULL)
return;
/*
* For now, we know that the next level is an instance. But
* if we ever have multiple scopes, this could be complicated.
*/
buf = safe_malloc(max_scf_name_len + 1);
if (scf_snaplevel_get_instance_name(elt->sl, buf,
max_scf_name_len + 1) >= 0) {
(void) puts(buf);
} else {
if (scf_error() != SCF_ERROR_DELETED)
scfdie();
}
free(buf);
return;
}
if (cur_inst != NULL) {
(void) fputs(COLON_NAMESPACES, stdout);
return;
}
iter = scf_iter_create(g_hndl);
if (iter == NULL)
scfdie();
buf = safe_malloc(max_scf_name_len + 1);
if (cur_svc != NULL) {
/* List the instances in this service. */
scf_instance_t *inst;
inst = scf_instance_create(g_hndl);
if (inst == NULL)
scfdie();
if (scf_iter_service_instances(iter, cur_svc) == 0) {
safe_printf(COLON_NAMESPACES);
for (;;) {
ret = scf_iter_next_instance(iter, inst);
if (ret == 0)
break;
if (ret != 1) {
if (scf_error() != SCF_ERROR_DELETED)
scfdie();
break;
}
if (scf_instance_get_name(inst, buf,
max_scf_name_len + 1) >= 0) {
if (pattern == NULL ||
fnmatch(pattern, buf, 0) == 0)
(void) puts(buf);
} else {
if (scf_error() != SCF_ERROR_DELETED)
scfdie();
}
}
} else {
if (scf_error() != SCF_ERROR_DELETED)
scfdie();
}
scf_instance_destroy(inst);
} else {
/* List the services in this scope. */
scf_service_t *svc;
assert(cur_scope != NULL);
svc = scf_service_create(g_hndl);
if (svc == NULL)
scfdie();
if (scf_iter_scope_services(iter, cur_scope) != SCF_SUCCESS)
scfdie();
for (;;) {
ret = scf_iter_next_service(iter, svc);
if (ret == 0)
break;
if (ret != 1)
scfdie();
if (scf_service_get_name(svc, buf,
max_scf_name_len + 1) >= 0) {
if (pattern == NULL ||
fnmatch(pattern, buf, 0) == 0)
safe_printf("%s\n", buf);
} else {
if (scf_error() != SCF_ERROR_DELETED)
scfdie();
}
}
scf_service_destroy(svc);
}
free(buf);
scf_iter_destroy(iter);
}
/*
* Entity addition. Creates an empty entity in the current selection.
*/
void
lscf_add(const char *name)
{
lscf_prep_hndl();
if (cur_snap != NULL) {
semerr(emsg_cant_modify_snapshots);
} else if (cur_inst != NULL) {
semerr(gettext("Cannot add entities to an instance.\n"));
} else if (cur_svc != NULL) {
if (scf_service_add_instance(cur_svc, name, NULL) !=
SCF_SUCCESS) {
switch (scf_error()) {
case SCF_ERROR_INVALID_ARGUMENT:
semerr(gettext("Invalid name.\n"));
break;
case SCF_ERROR_EXISTS:
semerr(gettext("Instance already exists.\n"));
break;
case SCF_ERROR_PERMISSION_DENIED:
semerr(emsg_permission_denied);
break;
default:
scfdie();
}
}
} else {
assert(cur_scope != NULL);
if (scf_scope_add_service(cur_scope, name, NULL) !=
SCF_SUCCESS) {
switch (scf_error()) {
case SCF_ERROR_INVALID_ARGUMENT:
semerr(gettext("Invalid name.\n"));
break;
case SCF_ERROR_EXISTS:
semerr(gettext("Service already exists.\n"));
break;
case SCF_ERROR_PERMISSION_DENIED:
semerr(emsg_permission_denied);
break;
case SCF_ERROR_BACKEND_READONLY:
semerr(emsg_read_only);
break;
default:
scfdie();
}
}
}
}
/* return 1 if the entity has no persistent pgs, else return 0 */
static int
entity_has_no_pgs(void *ent, int isservice)
{
scf_iter_t *iter = NULL;
scf_propertygroup_t *pg = NULL;
uint32_t flags;
int err;
int ret = 1;
if ((iter = scf_iter_create(g_hndl)) == NULL ||
(pg = scf_pg_create(g_hndl)) == NULL)
scfdie();
if (isservice) {
if (scf_iter_service_pgs(iter, (scf_service_t *)ent) < 0)
scfdie();
} else {
if (scf_iter_instance_pgs(iter, (scf_instance_t *)ent) < 0)
scfdie();
}
while ((err = scf_iter_next_pg(iter, pg)) == 1) {
if (scf_pg_get_flags(pg, &flags) != 0)
scfdie();
/* skip nonpersistent pgs */
if (flags & SCF_PG_FLAG_NONPERSISTENT)
continue;
ret = 0;
break;
}
if (err == -1)
scfdie();
scf_pg_destroy(pg);
scf_iter_destroy(iter);
return (ret);
}
/* return 1 if the service has no instances, else return 0 */
static int
svc_has_no_insts(scf_service_t *svc)
{
scf_instance_t *inst;
scf_iter_t *iter;
int r;
int ret = 1;
if ((inst = scf_instance_create(g_hndl)) == NULL ||
(iter = scf_iter_create(g_hndl)) == NULL)
scfdie();
if (scf_iter_service_instances(iter, svc) != 0)
scfdie();
r = scf_iter_next_instance(iter, inst);
if (r == 1) {
ret = 0;
} else if (r == 0) {
ret = 1;
} else if (r == -1) {
scfdie();
} else {
bad_error("scf_iter_next_instance", r);
}
scf_iter_destroy(iter);
scf_instance_destroy(inst);
return (ret);
}
/*
* Entity deletion.
*/
/*
* Delete the property group <fmri>/:properties/<name>. Returns
* SCF_ERROR_NONE on success (or if the entity is not found),
* SCF_ERROR_INVALID_ARGUMENT if the fmri is bad, SCF_ERROR_TYPE_MISMATCH if
* the pg is the wrong type, or SCF_ERROR_PERMISSION_DENIED if permission was
* denied.
*/
static scf_error_t
delete_dependency_pg(const char *fmri, const char *name)
{
void *entity = NULL;
int isservice;
scf_propertygroup_t *pg = NULL;
scf_error_t result;
char *pgty;
scf_service_t *svc = NULL;
scf_instance_t *inst = NULL;
scf_iter_t *iter = NULL;
char *name_buf = NULL;
result = fmri_to_entity(g_hndl, fmri, &entity, &isservice);
switch (result) {
case SCF_ERROR_NONE:
break;
case SCF_ERROR_NO_MEMORY:
uu_die(gettext("Out of memory.\n"));
/* NOTREACHED */
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_CONSTRAINT_VIOLATED:
return (SCF_ERROR_INVALID_ARGUMENT);
case SCF_ERROR_NOT_FOUND:
result = SCF_ERROR_NONE;
goto out;
default:
bad_error("fmri_to_entity", result);
}
pg = scf_pg_create(g_hndl);
if (pg == NULL)
scfdie();
if (entity_get_pg(entity, isservice, name, pg) != 0) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
result = SCF_ERROR_NONE;
goto out;
}
pgty = safe_malloc(max_scf_pg_type_len + 1);
if (scf_pg_get_type(pg, pgty, max_scf_pg_type_len + 1) < 0)
scfdie();
if (strcmp(pgty, SCF_GROUP_DEPENDENCY) != 0) {
result = SCF_ERROR_TYPE_MISMATCH;
free(pgty);
goto out;
}
free(pgty);
if (scf_pg_delete(pg) != 0) {
result = scf_error();
if (result != SCF_ERROR_PERMISSION_DENIED)
scfdie();
goto out;
}
/*
* We have to handle the case where we've just deleted the last
* property group of a "dummy" entity (instance or service).
* A "dummy" entity is an entity only present to hold an
* external dependency.
* So, in the case we deleted the last property group then we
* can also delete the entity. If the entity is an instance then
* we must verify if this was the last instance for the service
* and if it is, we can also delete the service if it doesn't
* have any property group either.
*/
result = SCF_ERROR_NONE;
if (isservice) {
svc = (scf_service_t *)entity;
if ((inst = scf_instance_create(g_hndl)) == NULL ||
(iter = scf_iter_create(g_hndl)) == NULL)
scfdie();
name_buf = safe_malloc(max_scf_name_len + 1);
} else {
inst = (scf_instance_t *)entity;
}
/*
* If the entity is an instance and we've just deleted its last
* property group then we should delete it.
*/
if (!isservice && entity_has_no_pgs(entity, isservice)) {
/* find the service before deleting the inst. - needed later */
if ((svc = scf_service_create(g_hndl)) == NULL)
scfdie();
if (scf_instance_get_parent(inst, svc) != 0)
scfdie();
/* delete the instance */
if (scf_instance_delete(inst) != 0) {
if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
result = SCF_ERROR_PERMISSION_DENIED;
goto out;
}
/* no need to refresh the instance */
inst = NULL;
}
/*
* If the service has no more instances and pgs or we just deleted the
* last instance and the service doesn't have anymore propery groups
* then the service should be deleted.
*/
if (svc != NULL &&
svc_has_no_insts(svc) &&
entity_has_no_pgs((void *)svc, 1)) {
if (scf_service_delete(svc) == 0) {
if (isservice) {
/* no need to refresh the service */
svc = NULL;
}
goto out;
}
if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
result = SCF_ERROR_PERMISSION_DENIED;
}
/* if the entity has not been deleted, refresh it */
if ((isservice && svc != NULL) || (!isservice && inst != NULL)) {
(void) refresh_entity(isservice, entity, fmri, inst, iter,
name_buf);
}
out:
if (isservice && (inst != NULL && iter != NULL)) {
free(name_buf);
scf_iter_destroy(iter);
scf_instance_destroy(inst);
}
if (!isservice && svc != NULL) {
scf_service_destroy(svc);
}
scf_pg_destroy(pg);
if (entity != NULL)
entity_destroy(entity, isservice);
return (result);
}
static int
delete_dependents(scf_propertygroup_t *pg)
{
char *pgty, *name, *fmri;
scf_property_t *prop;
scf_value_t *val;
scf_iter_t *iter;
int r;
scf_error_t err;
/* Verify that the pg has the correct type. */
pgty = safe_malloc(max_scf_pg_type_len + 1);
if (scf_pg_get_type(pg, pgty, max_scf_pg_type_len + 1) < 0)
scfdie();
if (strcmp(pgty, scf_group_framework) != 0) {
if (g_verbose) {
fmri = safe_malloc(max_scf_fmri_len + 1);
if (scf_pg_to_fmri(pg, fmri, max_scf_fmri_len + 1) < 0)
scfdie();
warn(gettext("Property group %s is not of expected "
"type %s.\n"), fmri, scf_group_framework);
free(fmri);
}
free(pgty);
return (-1);
}
free(pgty);
/* map delete_dependency_pg onto the properties. */
if ((prop = scf_property_create(g_hndl)) == NULL ||
(val = scf_value_create(g_hndl)) == NULL ||
(iter = scf_iter_create(g_hndl)) == NULL)
scfdie();
if (scf_iter_pg_properties(iter, pg) != SCF_SUCCESS)
scfdie();
name = safe_malloc(max_scf_name_len + 1);
fmri = safe_malloc(max_scf_fmri_len + 2);
while ((r = scf_iter_next_property(iter, prop)) == 1) {
scf_type_t ty;
if (scf_property_get_name(prop, name, max_scf_name_len + 1) < 0)
scfdie();
if (scf_property_type(prop, &ty) != SCF_SUCCESS)
scfdie();
if ((ty != SCF_TYPE_ASTRING &&
prop_check_type(prop, SCF_TYPE_FMRI) != 0) ||
prop_get_val(prop, val) != 0)
continue;
if (scf_value_get_astring(val, fmri, max_scf_fmri_len + 2) < 0)
scfdie();
err = delete_dependency_pg(fmri, name);
if (err == SCF_ERROR_INVALID_ARGUMENT && g_verbose) {
if (scf_property_to_fmri(prop, fmri,
max_scf_fmri_len + 2) < 0)
scfdie();
warn(gettext("Value of %s is not a valid FMRI.\n"),
fmri);
} else if (err == SCF_ERROR_TYPE_MISMATCH && g_verbose) {
warn(gettext("Property group \"%s\" of entity \"%s\" "
"does not have dependency type.\n"), name, fmri);
} else if (err == SCF_ERROR_PERMISSION_DENIED && g_verbose) {
warn(gettext("Could not delete property group \"%s\" "
"of entity \"%s\" (permission denied).\n"), name,
fmri);
}
}
if (r == -1)
scfdie();
scf_value_destroy(val);
scf_property_destroy(prop);
return (0);
}
/*
* Returns 1 if the instance may be running, and 0 otherwise.
*/
static int
inst_is_running(scf_instance_t *inst)
{
scf_propertygroup_t *pg;
scf_property_t *prop;
scf_value_t *val;
char buf[MAX_SCF_STATE_STRING_SZ];
int ret = 0;
ssize_t szret;
if ((pg = scf_pg_create(g_hndl)) == NULL ||
(prop = scf_property_create(g_hndl)) == NULL ||
(val = scf_value_create(g_hndl)) == NULL)
scfdie();
if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, pg) != SCF_SUCCESS) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
goto out;
}
if (pg_get_prop(pg, SCF_PROPERTY_STATE, prop) != 0 ||
prop_check_type(prop, SCF_TYPE_ASTRING) != 0 ||
prop_get_val(prop, val) != 0)
goto out;
szret = scf_value_get_astring(val, buf, sizeof (buf));
assert(szret >= 0);
ret = (strcmp(buf, SCF_STATE_STRING_ONLINE) == 0 ||
strcmp(buf, SCF_STATE_STRING_DEGRADED) == 0) ? 1 : 0;
out:
scf_value_destroy(val);
scf_property_destroy(prop);
scf_pg_destroy(pg);
return (ret);
}
static uint8_t
pg_is_external_dependency(scf_propertygroup_t *pg)
{
char *type;
scf_value_t *val;
scf_property_t *prop;
uint8_t b = B_FALSE;
type = safe_malloc(max_scf_pg_type_len + 1);
if (scf_pg_get_type(pg, type, max_scf_pg_type_len + 1) < 0)
scfdie();
if ((prop = scf_property_create(g_hndl)) == NULL ||
(val = scf_value_create(g_hndl)) == NULL)
scfdie();
if (strcmp(type, SCF_GROUP_DEPENDENCY) == 0) {
if (pg_get_prop(pg, scf_property_external, prop) == 0) {
if (scf_property_get_value(prop, val) != 0)
scfdie();
if (scf_value_get_boolean(val, &b) != 0)
scfdie();
}
}
free(type);
(void) scf_value_destroy(val);
(void) scf_property_destroy(prop);
return (b);
}
#define DELETE_FAILURE -1
#define DELETE_SUCCESS_NOEXTDEPS 0
#define DELETE_SUCCESS_EXTDEPS 1
/*
* lscf_instance_delete() deletes an instance. Before calling
* scf_instance_delete(), though, we make sure the instance isn't
* running and delete dependencies in other entities which the instance
* declared as "dependents". If there are dependencies which were
* created for other entities, then instead of deleting the instance we
* make it "empty" by deleting all other property groups and all
* snapshots.
*
* lscf_instance_delete() verifies that there is no external dependency pgs
* before suppressing the instance. If there is, then we must not remove them
* now in case the instance is re-created otherwise the dependencies would be
* lost. The external dependency pgs will be removed if the dependencies are
* removed.
*
* Returns:
* DELETE_FAILURE on failure
* DELETE_SUCCESS_NOEXTDEPS on success - no external dependencies
* DELETE_SUCCESS_EXTDEPS on success - external dependencies
*/
static int
lscf_instance_delete(scf_instance_t *inst, int force)
{
scf_propertygroup_t *pg;
scf_snapshot_t *snap;
scf_iter_t *iter;
int err;
int external = 0;
/* If we're not forcing and the instance is running, refuse. */
if (!force && inst_is_running(inst)) {
char *fmri;
fmri = safe_malloc(max_scf_fmri_len + 1);
if (scf_instance_to_fmri(inst, fmri, max_scf_fmri_len + 1) < 0)
scfdie();
semerr(gettext("Instance %s may be running. "
"Use delete -f if it is not.\n"), fmri);
free(fmri);
return (DELETE_FAILURE);
}
pg = scf_pg_create(g_hndl);
if (pg == NULL)
scfdie();
if (scf_instance_get_pg(inst, SCF_PG_DEPENDENTS, pg) == 0)
(void) delete_dependents(pg);
else if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
scf_pg_destroy(pg);
/*
* If the instance has some external dependencies then we must
* keep them in case the instance is reimported otherwise the
* dependencies would be lost on reimport.
*/
if ((iter = scf_iter_create(g_hndl)) == NULL ||
(pg = scf_pg_create(g_hndl)) == NULL)
scfdie();
if (scf_iter_instance_pgs(iter, inst) < 0)
scfdie();
while ((err = scf_iter_next_pg(iter, pg)) == 1) {
if (pg_is_external_dependency(pg)) {
external = 1;
continue;
}
if (scf_pg_delete(pg) != 0) {
if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
else {
semerr(emsg_permission_denied);
(void) scf_iter_destroy(iter);
(void) scf_pg_destroy(pg);
return (DELETE_FAILURE);
}
}
}
if (err == -1)
scfdie();
(void) scf_iter_destroy(iter);
(void) scf_pg_destroy(pg);
if (external) {
/*
* All the pgs have been deleted for the instance except
* the ones holding the external dependencies.
* For the job to be complete, we must also delete the
* snapshots associated with the instance.
*/
if ((snap = scf_snapshot_create((scf_handle_t *)g_hndl)) ==
NULL)
scfdie();
if ((iter = scf_iter_create((scf_handle_t *)g_hndl)) == NULL)
scfdie();
if (scf_iter_instance_snapshots(iter, inst) == -1)
scfdie();
while ((err = scf_iter_next_snapshot(iter, snap)) == 1) {
if (_scf_snapshot_delete(snap) != 0) {
if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
semerr(emsg_permission_denied);
(void) scf_iter_destroy(iter);
(void) scf_snapshot_destroy(snap);
return (DELETE_FAILURE);
}
}
if (err == -1)
scfdie();
(void) scf_iter_destroy(iter);
(void) scf_snapshot_destroy(snap);
return (DELETE_SUCCESS_EXTDEPS);
}
if (scf_instance_delete(inst) != 0) {
if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
semerr(emsg_permission_denied);
return (DELETE_FAILURE);
}
return (DELETE_SUCCESS_NOEXTDEPS);
}
/*
* lscf_service_delete() deletes a service. Before calling
* scf_service_delete(), though, we call lscf_instance_delete() for
* each of the instances and delete dependencies in other entities
* which were created as "dependents" of this service. If there are
* dependencies which were created for other entities, then we delete
* all other property groups in the service and leave it as "empty".
*
* lscf_service_delete() verifies that there is no external dependency
* pgs at the instance & service level before suppressing the service.
* If there is, then we must not remove them now in case the service
* is re-imported otherwise the dependencies would be lost. The external
* dependency pgs will be removed if the dependencies are removed.
*
* Returns:
* DELETE_FAILURE on failure
* DELETE_SUCCESS_NOEXTDEPS on success - no external dependencies
* DELETE_SUCCESS_EXTDEPS on success - external dependencies
*/
static int
lscf_service_delete(scf_service_t *svc, int force)
{
int r;
scf_instance_t *inst;
scf_propertygroup_t *pg;
scf_iter_t *iter;
int ret;
int external = 0;
if ((inst = scf_instance_create(g_hndl)) == NULL ||
(pg = scf_pg_create(g_hndl)) == NULL ||
(iter = scf_iter_create(g_hndl)) == NULL)
scfdie();
if (scf_iter_service_instances(iter, svc) != 0)
scfdie();
for (r = scf_iter_next_instance(iter, inst);
r == 1;
r = scf_iter_next_instance(iter, inst)) {
ret = lscf_instance_delete(inst, force);
if (ret == DELETE_FAILURE) {
scf_iter_destroy(iter);
scf_pg_destroy(pg);
scf_instance_destroy(inst);
return (DELETE_FAILURE);
}
/*
* Record the fact that there is some external dependencies
* at the instance level.
*/
if (ret == DELETE_SUCCESS_EXTDEPS)
external |= 1;
}
if (r != 0)
scfdie();
/* Delete dependency property groups in dependent services. */
if (scf_service_get_pg(svc, SCF_PG_DEPENDENTS, pg) == 0)
(void) delete_dependents(pg);
else if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
scf_iter_destroy(iter);
scf_pg_destroy(pg);
scf_instance_destroy(inst);
/*
* If the service has some external dependencies then we don't
* want to remove them in case the service is re-imported.
*/
if ((pg = scf_pg_create(g_hndl)) == NULL ||
(iter = scf_iter_create(g_hndl)) == NULL)
scfdie();
if (scf_iter_service_pgs(iter, svc) < 0)
scfdie();
while ((r = scf_iter_next_pg(iter, pg)) == 1) {
if (pg_is_external_dependency(pg)) {
external |= 2;
continue;
}
if (scf_pg_delete(pg) != 0) {
if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
else {
semerr(emsg_permission_denied);
(void) scf_iter_destroy(iter);
(void) scf_pg_destroy(pg);
return (DELETE_FAILURE);
}
}
}
if (r == -1)
scfdie();
(void) scf_iter_destroy(iter);
(void) scf_pg_destroy(pg);
if (external != 0)
return (DELETE_SUCCESS_EXTDEPS);
if (scf_service_delete(svc) == 0)
return (DELETE_SUCCESS_NOEXTDEPS);
if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
semerr(emsg_permission_denied);
return (DELETE_FAILURE);
}
static int
delete_callback(void *data, scf_walkinfo_t *wip)
{
int force = (int)data;
if (wip->inst != NULL)
(void) lscf_instance_delete(wip->inst, force);
else
(void) lscf_service_delete(wip->svc, force);
return (0);
}
void
lscf_delete(const char *fmri, int force)
{
scf_service_t *svc;
scf_instance_t *inst;
int ret;
lscf_prep_hndl();
if (cur_snap != NULL) {
if (!snaplevel_is_instance(cur_level)) {
char *buf;
buf = safe_malloc(max_scf_name_len + 1);
if (scf_instance_get_name(cur_inst, buf,
max_scf_name_len + 1) >= 0) {
if (strcmp(buf, fmri) == 0) {
semerr(emsg_cant_modify_snapshots);
free(buf);
return;
}
} else if (scf_error() != SCF_ERROR_DELETED) {
scfdie();
}
free(buf);
}
} else if (cur_inst != NULL) {
/* EMPTY */;
} else if (cur_svc != NULL) {
inst = scf_instance_create(g_hndl);
if (inst == NULL)
scfdie();
if (scf_service_get_instance(cur_svc, fmri, inst) ==
SCF_SUCCESS) {
(void) lscf_instance_delete(inst, force);
scf_instance_destroy(inst);
return;
}
if (scf_error() != SCF_ERROR_NOT_FOUND &&
scf_error() != SCF_ERROR_INVALID_ARGUMENT)
scfdie();
scf_instance_destroy(inst);
} else {
assert(cur_scope != NULL);
svc = scf_service_create(g_hndl);
if (svc == NULL)
scfdie();
if (scf_scope_get_service(cur_scope, fmri, svc) ==
SCF_SUCCESS) {
(void) lscf_service_delete(svc, force);
scf_service_destroy(svc);
return;
}
if (scf_error() != SCF_ERROR_NOT_FOUND &&
scf_error() != SCF_ERROR_INVALID_ARGUMENT)
scfdie();
scf_service_destroy(svc);
}
/*
* Match FMRI to entity.
*/
if ((ret = scf_walk_fmri(g_hndl, 1, (char **)&fmri, SCF_WALK_SERVICE,
delete_callback, (void *)force, NULL, semerr)) != 0) {
semerr(gettext("Failed to walk instances: %s\n"),
scf_strerror(ret));
}
}
/*
* :properties commands. These all end with "pg" or "prop" and generally
* operate on the currently selected entity.
*/
/*
* Property listing. List the property groups, properties, their types and
* their values for the currently selected entity.
*/
static void
list_pg_info(const scf_propertygroup_t *pg, const char *name, size_t namewidth)
{
char *buf;
uint32_t flags;
buf = safe_malloc(max_scf_pg_type_len + 1);
if (scf_pg_get_type(pg, buf, max_scf_pg_type_len + 1) < 0)
scfdie();
if (scf_pg_get_flags(pg, &flags) != SCF_SUCCESS)
scfdie();
safe_printf("%-*s %s", namewidth, name, buf);
if (flags & SCF_PG_FLAG_NONPERSISTENT)
safe_printf("\tNONPERSISTENT");
safe_printf("\n");
free(buf);
}
static boolean_t
prop_has_multiple_values(const scf_property_t *prop, scf_value_t *val)
{
if (scf_property_get_value(prop, val) == 0) {
return (B_FALSE);
} else {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
return (B_FALSE);
case SCF_ERROR_PERMISSION_DENIED:
case SCF_ERROR_CONSTRAINT_VIOLATED:
return (B_TRUE);
default:
scfdie();
/*NOTREACHED*/
}
}
}
static void
list_prop_info(const scf_property_t *prop, const char *name, size_t len)
{
scf_iter_t *iter;
scf_value_t *val;
const char *type;
int multiple_strings = 0;
int ret;
if ((iter = scf_iter_create(g_hndl)) == NULL ||
(val = scf_value_create(g_hndl)) == NULL)
scfdie();
type = prop_to_typestr(prop);
assert(type != NULL);
safe_printf("%-*s %-7s ", len, name, type);
if (prop_has_multiple_values(prop, val) &&
(scf_value_type(val) == SCF_TYPE_ASTRING ||
scf_value_type(val) == SCF_TYPE_USTRING))
multiple_strings = 1;
if (scf_iter_property_values(iter, prop) != SCF_SUCCESS)
scfdie();
while ((ret = scf_iter_next_value(iter, val)) == 1) {
char *buf;
ssize_t vlen, szret;
vlen = scf_value_get_as_string(val, NULL, 0);
if (vlen < 0)
scfdie();
buf = safe_malloc(vlen + 1);
szret = scf_value_get_as_string(val, buf, vlen + 1);
if (szret < 0)
scfdie();
assert(szret <= vlen);
/* This is to be human-readable, so don't use CHARS_TO_QUOTE */
if (multiple_strings || strpbrk(buf, " \t\n\"()") != NULL) {
safe_printf(" \"");
(void) quote_and_print(buf, stdout, 0);
(void) putchar('"');
if (ferror(stdout)) {
(void) putchar('\n');
uu_die(gettext("Error writing to stdout.\n"));
}
} else {
safe_printf(" %s", buf);
}
free(buf);
}
if (ret != 0 && scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
if (putchar('\n') != '\n')
uu_die(gettext("Could not output newline"));
}
/*
* Outputs template property group info for the describe subcommand.
* If 'templates' == 2, verbose output is printed in the format expected
* for describe -v, which includes all templates fields. If pg is
* not NULL, we're describing the template data, not an existing property
* group, and formatting should be appropriate for describe -t.
*/
static void
list_pg_tmpl(scf_pg_tmpl_t *pgt, scf_propertygroup_t *pg, int templates)
{
char *buf;
uint8_t required;
scf_property_t *stability_prop;
scf_value_t *stability_val;
if (templates == 0)
return;
if ((stability_prop = scf_property_create(g_hndl)) == NULL ||
(stability_val = scf_value_create(g_hndl)) == NULL)
scfdie();
if (templates == 2 && pg != NULL) {
if (scf_pg_get_property(pg, SCF_PROPERTY_STABILITY,
stability_prop) == 0) {
if (prop_check_type(stability_prop,
SCF_TYPE_ASTRING) == 0 &&
prop_get_val(stability_prop, stability_val) == 0) {
char *stability;
stability = safe_malloc(max_scf_value_len + 1);
if (scf_value_get_astring(stability_val,
stability, max_scf_value_len + 1) == -1 &&
scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
safe_printf("%s%s: %s\n", TMPL_INDENT,
gettext("stability"), stability);
free(stability);
}
} else if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
}
scf_property_destroy(stability_prop);
scf_value_destroy(stability_val);
if (pgt == NULL)
return;
if (pg == NULL || templates == 2) {
/* print type info only if scf_tmpl_pg_name succeeds */
if (scf_tmpl_pg_name(pgt, &buf) != -1) {
if (pg != NULL)
safe_printf("%s", TMPL_INDENT);
safe_printf("%s: ", gettext("name"));
safe_printf("%s\n", buf);
free(buf);
}
/* print type info only if scf_tmpl_pg_type succeeds */
if (scf_tmpl_pg_type(pgt, &buf) != -1) {
if (pg != NULL)
safe_printf("%s", TMPL_INDENT);
safe_printf("%s: ", gettext("type"));
safe_printf("%s\n", buf);
free(buf);
}
}
if (templates == 2 && scf_tmpl_pg_required(pgt, &required) == 0)
safe_printf("%s%s: %s\n", TMPL_INDENT, gettext("required"),
required ? "true" : "false");
if (templates == 2 && scf_tmpl_pg_target(pgt, &buf) > 0) {
safe_printf("%s%s: %s\n", TMPL_INDENT, gettext("target"),
buf);
free(buf);
}
if (templates == 2 && scf_tmpl_pg_common_name(pgt, NULL, &buf) > 0) {
safe_printf("%s%s: %s\n", TMPL_INDENT, gettext("common name"),
buf);
free(buf);
}
if (scf_tmpl_pg_description(pgt, NULL, &buf) > 0) {
if (templates == 2)
safe_printf("%s%s: %s\n", TMPL_INDENT,
gettext("description"), buf);
else
safe_printf("%s%s\n", TMPL_INDENT, buf);
free(buf);
}
}
/*
* With as_value set to true, indent as appropriate for the value level.
* If false, indent to appropriate level for inclusion in constraint
* or choice printout.
*/
static void
print_template_value_details(scf_prop_tmpl_t *prt, const char *val_buf,
int as_value)
{
char *buf;
if (scf_tmpl_value_common_name(prt, NULL, val_buf, &buf) > 0) {
if (as_value == 0)
safe_printf("%s", TMPL_CHOICE_INDENT);
else
safe_printf("%s", TMPL_INDENT);
safe_printf("%s: %s\n", gettext("value common name"), buf);
free(buf);
}
if (scf_tmpl_value_description(prt, NULL, val_buf, &buf) > 0) {
if (as_value == 0)
safe_printf("%s", TMPL_CHOICE_INDENT);
else
safe_printf("%s", TMPL_INDENT);
safe_printf("%s: %s\n", gettext("value description"), buf);
free(buf);
}
}
static void
print_template_value(scf_prop_tmpl_t *prt, const char *val_buf)
{
safe_printf("%s%s: ", TMPL_VALUE_INDENT, gettext("value"));
/* This is to be human-readable, so don't use CHARS_TO_QUOTE */
safe_printf("%s\n", val_buf);
print_template_value_details(prt, val_buf, 1);
}
static void
print_template_constraints(scf_prop_tmpl_t *prt, int verbose)
{
int i, printed = 0;
scf_values_t values;
scf_count_ranges_t c_ranges;
scf_int_ranges_t i_ranges;
printed = 0;
i = 0;
if (scf_tmpl_value_name_constraints(prt, &values) == 0) {
safe_printf("%s%s:\n", TMPL_VALUE_INDENT,
gettext("value constraints"));
printed++;
for (i = 0; i < values.value_count; ++i) {
safe_printf("%s%s: %s\n", TMPL_INDENT,
gettext("value name"), values.values_as_strings[i]);
if (verbose == 1)
print_template_value_details(prt,
values.values_as_strings[i], 0);
}
scf_values_destroy(&values);
}
if (scf_tmpl_value_count_range_constraints(prt, &c_ranges) == 0) {
if (printed++ == 0)
safe_printf("%s%s:\n", TMPL_VALUE_INDENT,
gettext("value constraints"));
for (i = 0; i < c_ranges.scr_num_ranges; ++i) {
safe_printf("%s%s: %llu to %llu\n", TMPL_INDENT,
gettext("range"), c_ranges.scr_min[i],
c_ranges.scr_max[i]);
}
scf_count_ranges_destroy(&c_ranges);
} else if (scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED &&
scf_tmpl_value_int_range_constraints(prt, &i_ranges) == 0) {
if (printed++ == 0)
safe_printf("%s%s:\n", TMPL_VALUE_INDENT,
gettext("value constraints"));
for (i = 0; i < i_ranges.sir_num_ranges; ++i) {
safe_printf("%s%s: %lld to %lld\n", TMPL_INDENT,
gettext("range"), i_ranges.sir_min[i],
i_ranges.sir_max[i]);
}
scf_int_ranges_destroy(&i_ranges);
}
}
static void
print_template_choices(scf_prop_tmpl_t *prt, int verbose)
{
int i = 0, printed = 0;
scf_values_t values;
scf_count_ranges_t c_ranges;
scf_int_ranges_t i_ranges;
printed = 0;
if (scf_tmpl_value_name_choices(prt, &values) == 0) {
safe_printf("%s%s:\n", TMPL_VALUE_INDENT,
gettext("value constraints"));
printed++;
for (i = 0; i < values.value_count; i++) {
safe_printf("%s%s: %s\n", TMPL_INDENT,
gettext("value name"), values.values_as_strings[i]);
if (verbose == 1)
print_template_value_details(prt,
values.values_as_strings[i], 0);
}
scf_values_destroy(&values);
}
if (scf_tmpl_value_count_range_choices(prt, &c_ranges) == 0) {
for (i = 0; i < c_ranges.scr_num_ranges; ++i) {
if (printed++ == 0)
safe_printf("%s%s:\n", TMPL_VALUE_INDENT,
gettext("value choices"));
safe_printf("%s%s: %llu to %llu\n", TMPL_INDENT,
gettext("range"), c_ranges.scr_min[i],
c_ranges.scr_max[i]);
}
scf_count_ranges_destroy(&c_ranges);
} else if (scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED &&
scf_tmpl_value_int_range_choices(prt, &i_ranges) == 0) {
for (i = 0; i < i_ranges.sir_num_ranges; ++i) {
if (printed++ == 0)
safe_printf("%s%s:\n", TMPL_VALUE_INDENT,
gettext("value choices"));
safe_printf("%s%s: %lld to %lld\n", TMPL_INDENT,
gettext("range"), i_ranges.sir_min[i],
i_ranges.sir_max[i]);
}
scf_int_ranges_destroy(&i_ranges);
}
}
static void
list_values_by_template(scf_prop_tmpl_t *prt)
{
print_template_constraints(prt, 1);
print_template_choices(prt, 1);
}
static void
list_values_tmpl(scf_prop_tmpl_t *prt, scf_property_t *prop)
{
char *val_buf;
scf_iter_t *iter;
scf_value_t *val;
int ret;
if ((iter = scf_iter_create(g_hndl)) == NULL ||
(val = scf_value_create(g_hndl)) == NULL)
scfdie();
if (scf_iter_property_values(iter, prop) != SCF_SUCCESS)
scfdie();
val_buf = safe_malloc(max_scf_value_len + 1);
while ((ret = scf_iter_next_value(iter, val)) == 1) {
if (scf_value_get_as_string(val, val_buf,
max_scf_value_len + 1) < 0)
scfdie();
print_template_value(prt, val_buf);
}
if (ret != 0 && scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
free(val_buf);
print_template_constraints(prt, 0);
print_template_choices(prt, 0);
}
/*
* Outputs property info for the describe subcommand
* Verbose output if templates == 2, -v option of svccfg describe
* Displays template data if prop is not NULL, -t option of svccfg describe
*/
static void
list_prop_tmpl(scf_prop_tmpl_t *prt, scf_property_t *prop, int templates)
{
char *buf;
uint8_t u_buf;
int i;
uint64_t min, max;
scf_values_t values;
if (prt == NULL || templates == 0)
return;
if (prop == NULL) {
safe_printf("%s%s: ", TMPL_VALUE_INDENT, gettext("name"));
if (scf_tmpl_prop_name(prt, &buf) > 0) {
safe_printf("%s\n", buf);
free(buf);
} else
safe_printf("(%s)\n", gettext("any"));
}
if (prop == NULL || templates == 2) {
if (prop != NULL)
safe_printf("%s", TMPL_INDENT);
else
safe_printf("%s", TMPL_VALUE_INDENT);
safe_printf("%s: ", gettext("type"));
if ((buf = _scf_read_tmpl_prop_type_as_string(prt)) != NULL) {
safe_printf("%s\n", buf);
free(buf);
} else
safe_printf("(%s)\n", gettext("any"));
}
if (templates == 2 && scf_tmpl_prop_required(prt, &u_buf) == 0)
safe_printf("%s%s: %s\n", TMPL_INDENT, gettext("required"),
u_buf ? "true" : "false");
if (templates == 2 && scf_tmpl_prop_common_name(prt, NULL, &buf) > 0) {
safe_printf("%s%s: %s\n", TMPL_INDENT, gettext("common name"),
buf);
free(buf);
}
if (templates == 2 && scf_tmpl_prop_units(prt, NULL, &buf) > 0) {
safe_printf("%s%s: %s\n", TMPL_INDENT, gettext("units"),
buf);
free(buf);
}
if (scf_tmpl_prop_description(prt, NULL, &buf) > 0) {
safe_printf("%s%s\n", TMPL_INDENT, buf);
free(buf);
}
if (templates == 2 && scf_tmpl_prop_visibility(prt, &u_buf) == 0)
safe_printf("%s%s: %s\n", TMPL_INDENT, gettext("visibility"),
scf_tmpl_visibility_to_string(u_buf));
if (templates == 2 && scf_tmpl_prop_cardinality(prt, &min, &max) == 0) {
safe_printf("%s%s: %" PRIu64 "\n", TMPL_INDENT,
gettext("minimum number of values"), min);
if (max == ULLONG_MAX) {
safe_printf("%s%s: %s\n", TMPL_INDENT,
gettext("maximum number of values"),
gettext("unlimited"));
} else {
safe_printf("%s%s: %" PRIu64 "\n", TMPL_INDENT,
gettext("maximum number of values"), max);
}
}
if (templates == 2 && scf_tmpl_prop_internal_seps(prt, &values) == 0) {
for (i = 0; i < values.value_count; i++) {
if (i == 0) {
safe_printf("%s%s:", TMPL_INDENT,
gettext("internal separators"));
}
safe_printf(" \"%s\"", values.values_as_strings[i]);
}
safe_printf("\n");
}
if (templates != 2)
return;
if (prop != NULL)
list_values_tmpl(prt, prop);
else
list_values_by_template(prt);
}
static char *
read_astring(scf_propertygroup_t *pg, const char *prop_name)
{
char *rv;
rv = _scf_read_single_astring_from_pg(pg, prop_name);
if (rv == NULL) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
break;
default:
scfdie();
}
}
return (rv);
}
static void
display_documentation(scf_iter_t *iter, scf_propertygroup_t *pg)
{
size_t doc_len;
size_t man_len;
char *pg_name;
char *text = NULL;
int rv;
doc_len = strlen(SCF_PG_TM_DOC_PREFIX);
man_len = strlen(SCF_PG_TM_MAN_PREFIX);
pg_name = safe_malloc(max_scf_name_len + 1);
while ((rv = scf_iter_next_pg(iter, pg)) == 1) {
if (scf_pg_get_name(pg, pg_name, max_scf_name_len + 1) == -1) {
scfdie();
}
if (strncmp(pg_name, SCF_PG_TM_DOC_PREFIX, doc_len) == 0) {
/* Display doc_link and and uri */
safe_printf("%s%s:\n", TMPL_INDENT,
gettext("doc_link"));
text = read_astring(pg, SCF_PROPERTY_TM_NAME);
if (text != NULL) {
safe_printf("%s%s%s: %s\n", TMPL_INDENT,
TMPL_INDENT, gettext("name"), text);
uu_free(text);
}
text = read_astring(pg, SCF_PROPERTY_TM_URI);
if (text != NULL) {
safe_printf("%s%s: %s\n", TMPL_INDENT_2X,
gettext("uri"), text);
uu_free(text);
}
} else if (strncmp(pg_name, SCF_PG_TM_MAN_PREFIX,
man_len) == 0) {
/* Display manpage title, section and path */
safe_printf("%s%s:\n", TMPL_INDENT,
gettext("manpage"));
text = read_astring(pg, SCF_PROPERTY_TM_TITLE);
if (text != NULL) {
safe_printf("%s%s%s: %s\n", TMPL_INDENT,
TMPL_INDENT, gettext("title"), text);
uu_free(text);
}
text = read_astring(pg, SCF_PROPERTY_TM_SECTION);
if (text != NULL) {
safe_printf("%s%s%s: %s\n", TMPL_INDENT,
TMPL_INDENT, gettext("section"), text);
uu_free(text);
}
text = read_astring(pg, SCF_PROPERTY_TM_MANPATH);
if (text != NULL) {
safe_printf("%s%s%s: %s\n", TMPL_INDENT,
TMPL_INDENT, gettext("manpath"), text);
uu_free(text);
}
}
}
if (rv == -1)
scfdie();
done:
free(pg_name);
}
static void
list_entity_tmpl(int templates)
{
char *common_name = NULL;
char *description = NULL;
char *locale = NULL;
scf_iter_t *iter;
scf_propertygroup_t *pg;
scf_property_t *prop;
int r;
scf_value_t *val;
if ((pg = scf_pg_create(g_hndl)) == NULL ||
(prop = scf_property_create(g_hndl)) == NULL ||
(val = scf_value_create(g_hndl)) == NULL ||
(iter = scf_iter_create(g_hndl)) == NULL)
scfdie();
locale = setlocale(LC_MESSAGES, NULL);
if (get_pg(SCF_PG_TM_COMMON_NAME, pg) == 0) {
common_name = safe_malloc(max_scf_value_len + 1);
/* Try both the current locale and the "C" locale. */
if (scf_pg_get_property(pg, locale, prop) == 0 ||
(scf_error() == SCF_ERROR_NOT_FOUND &&
scf_pg_get_property(pg, "C", prop) == 0)) {
if (prop_get_val(prop, val) == 0 &&
scf_value_get_ustring(val, common_name,
max_scf_value_len + 1) != -1) {
safe_printf("%s%s: %s\n", TMPL_INDENT,
gettext("common name"), common_name);
}
}
}
/*
* Do description, manpages, and doc links if templates == 2.
*/
if (templates == 2) {
/* Get the description. */
if (get_pg(SCF_PG_TM_DESCRIPTION, pg) == 0) {
description = safe_malloc(max_scf_value_len + 1);
/* Try both the current locale and the "C" locale. */
if (scf_pg_get_property(pg, locale, prop) == 0 ||
(scf_error() == SCF_ERROR_NOT_FOUND &&
scf_pg_get_property(pg, "C", prop) == 0)) {
if (prop_get_val(prop, val) == 0 &&
scf_value_get_ustring(val, description,
max_scf_value_len + 1) != -1) {
safe_printf("%s%s: %s\n", TMPL_INDENT,
gettext("description"),
description);
}
}
}
/* Process doc_link & manpage elements. */
if (cur_level != NULL) {
r = scf_iter_snaplevel_pgs_typed(iter, cur_level,
SCF_GROUP_TEMPLATE);
} else if (cur_inst != NULL) {
r = scf_iter_instance_pgs_typed(iter, cur_inst,
SCF_GROUP_TEMPLATE);
} else {
r = scf_iter_service_pgs_typed(iter, cur_svc,
SCF_GROUP_TEMPLATE);
}
if (r == 0) {
display_documentation(iter, pg);
}
}
free(common_name);
free(description);
scf_pg_destroy(pg);
scf_property_destroy(prop);
scf_value_destroy(val);
scf_iter_destroy(iter);
}
static void
listtmpl(const char *pattern, int templates)
{
scf_pg_tmpl_t *pgt;
scf_prop_tmpl_t *prt;
char *snapbuf = NULL;
char *fmribuf;
char *pg_name = NULL, *prop_name = NULL;
ssize_t prop_name_size;
char *qual_prop_name;
char *search_name;
int listed = 0;
if ((pgt = scf_tmpl_pg_create(g_hndl)) == NULL ||
(prt = scf_tmpl_prop_create(g_hndl)) == NULL)
scfdie();
fmribuf = safe_malloc(max_scf_name_len + 1);
qual_prop_name = safe_malloc(max_scf_name_len + 1);
if (cur_snap != NULL) {
snapbuf = safe_malloc(max_scf_name_len + 1);
if (scf_snapshot_get_name(cur_snap, snapbuf,
max_scf_name_len + 1) < 0)
scfdie();
}
if (cur_inst != NULL) {
if (scf_instance_to_fmri(cur_inst, fmribuf,
max_scf_name_len + 1) < 0)
scfdie();
} else if (cur_svc != NULL) {
if (scf_service_to_fmri(cur_svc, fmribuf,
max_scf_name_len + 1) < 0)
scfdie();
} else
abort();
/* If pattern is specified, we want to list only those items. */
while (scf_tmpl_iter_pgs(pgt, fmribuf, snapbuf, NULL, 0) == 1) {
listed = 0;
if (pattern == NULL || (scf_tmpl_pg_name(pgt, &pg_name) > 0 &&
fnmatch(pattern, pg_name, 0) == 0)) {
list_pg_tmpl(pgt, NULL, templates);
listed++;
}
scf_tmpl_prop_reset(prt);
while (scf_tmpl_iter_props(pgt, prt, 0) == 0) {
search_name = NULL;
prop_name_size = scf_tmpl_prop_name(prt, &prop_name);
if ((prop_name_size > 0) && (pg_name != NULL)) {
if (snprintf(qual_prop_name,
max_scf_name_len + 1, "%s/%s",
pg_name, prop_name) >=
max_scf_name_len + 1) {
prop_name_size = -1;
} else {
search_name = qual_prop_name;
}
}
if (listed > 0 || pattern == NULL ||
(prop_name_size > 0 &&
fnmatch(pattern, search_name,
FNM_PATHNAME) == 0))
list_prop_tmpl(prt, NULL, templates);
if (prop_name != NULL) {
free(prop_name);
prop_name = NULL;
}
}
if (pg_name != NULL) {
free(pg_name);
pg_name = NULL;
}
}
scf_tmpl_prop_destroy(prt);
scf_tmpl_pg_destroy(pgt);
free(snapbuf);
free(fmribuf);
free(qual_prop_name);
}
static void
listprop(const char *pattern, int only_pgs, int templates)
{
scf_propertygroup_t *pg;
scf_property_t *prop;
scf_iter_t *iter, *piter;
char *pgnbuf, *prnbuf, *ppnbuf;
scf_pg_tmpl_t *pgt, *pgtp;
scf_prop_tmpl_t *prt;
void **objects;
char **names;
void **tmpls;
int allocd, i;
int ret;
ssize_t pgnlen, prnlen, szret;
size_t max_len = 0;
if (cur_svc == NULL && cur_inst == NULL) {
semerr(emsg_entity_not_selected);
return;
}
if ((pg = scf_pg_create(g_hndl)) == NULL ||
(prop = scf_property_create(g_hndl)) == NULL ||
(iter = scf_iter_create(g_hndl)) == NULL ||
(piter = scf_iter_create(g_hndl)) == NULL ||
(prt = scf_tmpl_prop_create(g_hndl)) == NULL ||
(pgt = scf_tmpl_pg_create(g_hndl)) == NULL)
scfdie();
prnbuf = safe_malloc(max_scf_name_len + 1);
if (cur_level != NULL)
ret = scf_iter_snaplevel_pgs(iter, cur_level);
else if (cur_inst != NULL)
ret = scf_iter_instance_pgs(iter, cur_inst);
else
ret = scf_iter_service_pgs(iter, cur_svc);
if (ret != 0) {
return;
}
/*
* We want to only list items which match pattern, and we want the
* second column to line up, so during the first pass we'll save
* matching items, their names, and their templates in objects,
* names, and tmpls, computing the maximum name length as we go,
* and then we'll print them out.
*
* Note: We always keep an extra slot available so the array can be
* NULL-terminated.
*/
i = 0;
allocd = 1;
objects = safe_malloc(sizeof (*objects));
names = safe_malloc(sizeof (*names));
tmpls = safe_malloc(sizeof (*tmpls));
while ((ret = scf_iter_next_pg(iter, pg)) == 1) {
int new_pg = 0;
int print_props = 0;
pgtp = NULL;
pgnlen = scf_pg_get_name(pg, NULL, 0);
if (pgnlen < 0)
scfdie();
pgnbuf = safe_malloc(pgnlen + 1);
szret = scf_pg_get_name(pg, pgnbuf, pgnlen + 1);
if (szret < 0)
scfdie();
assert(szret <= pgnlen);
if (scf_tmpl_get_by_pg(pg, pgt, 0) == -1) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
pgtp = NULL;
} else {
pgtp = pgt;
}
if (pattern == NULL ||
fnmatch(pattern, pgnbuf, 0) == 0) {
if (i+1 >= allocd) {
allocd *= 2;
objects = realloc(objects,
sizeof (*objects) * allocd);
names =
realloc(names, sizeof (*names) * allocd);
tmpls = realloc(tmpls,
sizeof (*tmpls) * allocd);
if (objects == NULL || names == NULL ||
tmpls == NULL)
uu_die(gettext("Out of memory"));
}
objects[i] = pg;
names[i] = pgnbuf;
if (pgtp == NULL)
tmpls[i] = NULL;
else
tmpls[i] = pgt;
++i;
if (pgnlen > max_len)
max_len = pgnlen;
new_pg = 1;
print_props = 1;
}
if (only_pgs) {
if (new_pg) {
pg = scf_pg_create(g_hndl);
if (pg == NULL)
scfdie();
pgt = scf_tmpl_pg_create(g_hndl);
if (pgt == NULL)
scfdie();
} else
free(pgnbuf);
continue;
}
if (scf_iter_pg_properties(piter, pg) != SCF_SUCCESS)
scfdie();
while ((ret = scf_iter_next_property(piter, prop)) == 1) {
prnlen = scf_property_get_name(prop, prnbuf,
max_scf_name_len + 1);
if (prnlen < 0)
scfdie();
/* Will prepend the property group name and a slash. */
prnlen += pgnlen + 1;
ppnbuf = safe_malloc(prnlen + 1);
if (snprintf(ppnbuf, prnlen + 1, "%s/%s", pgnbuf,
prnbuf) < 0)
uu_die("snprintf");
if (pattern == NULL || print_props == 1 ||
fnmatch(pattern, ppnbuf, 0) == 0) {
if (i+1 >= allocd) {
allocd *= 2;
objects = realloc(objects,
sizeof (*objects) * allocd);
names = realloc(names,
sizeof (*names) * allocd);
tmpls = realloc(tmpls,
sizeof (*tmpls) * allocd);
if (objects == NULL || names == NULL ||
tmpls == NULL)
uu_die(gettext(
"Out of memory"));
}
objects[i] = prop;
names[i] = ppnbuf;
if (pgtp != NULL) {
if (scf_tmpl_get_by_prop(pgt, prnbuf,
prt, 0) < 0) {
if (scf_error() !=
SCF_ERROR_NOT_FOUND)
scfdie();
tmpls[i] = NULL;
} else {
tmpls[i] = prt;
}
} else {
tmpls[i] = NULL;
}
++i;
if (prnlen > max_len)
max_len = prnlen;
prop = scf_property_create(g_hndl);
prt = scf_tmpl_prop_create(g_hndl);
} else {
free(ppnbuf);
}
}
if (new_pg) {
pg = scf_pg_create(g_hndl);
if (pg == NULL)
scfdie();
pgt = scf_tmpl_pg_create(g_hndl);
if (pgt == NULL)
scfdie();
} else
free(pgnbuf);
}
if (ret != 0)
scfdie();
objects[i] = NULL;
scf_pg_destroy(pg);
scf_tmpl_pg_destroy(pgt);
scf_property_destroy(prop);
scf_tmpl_prop_destroy(prt);
for (i = 0; objects[i] != NULL; ++i) {
if (strchr(names[i], '/') == NULL) {
/* property group */
pg = (scf_propertygroup_t *)objects[i];
pgt = (scf_pg_tmpl_t *)tmpls[i];
list_pg_info(pg, names[i], max_len);
list_pg_tmpl(pgt, pg, templates);
free(names[i]);
scf_pg_destroy(pg);
if (pgt != NULL)
scf_tmpl_pg_destroy(pgt);
} else {
/* property */
prop = (scf_property_t *)objects[i];
prt = (scf_prop_tmpl_t *)tmpls[i];
list_prop_info(prop, names[i], max_len);
list_prop_tmpl(prt, prop, templates);
free(names[i]);
scf_property_destroy(prop);
if (prt != NULL)
scf_tmpl_prop_destroy(prt);
}
}
free(names);
free(objects);
free(tmpls);
}
void
lscf_listpg(const char *pattern)
{
lscf_prep_hndl();
listprop(pattern, 1, 0);
}
/*
* Property group and property creation, setting, and deletion. setprop (and
* its alias, addprop) can either create a property group of a given type, or
* it can create or set a property to a given type and list of values.
*/
void
lscf_addpg(const char *name, const char *type, const char *flags)
{
scf_propertygroup_t *pg;
int ret;
uint32_t flgs = 0;
const char *cp;
lscf_prep_hndl();
if (cur_snap != NULL) {
semerr(emsg_cant_modify_snapshots);
return;
}
if (cur_inst == NULL && cur_svc == NULL) {
semerr(emsg_entity_not_selected);
return;
}
if (flags != NULL) {
for (cp = flags; *cp != '\0'; ++cp) {
switch (*cp) {
case 'P':
flgs |= SCF_PG_FLAG_NONPERSISTENT;
break;
case 'p':
flgs &= ~SCF_PG_FLAG_NONPERSISTENT;
break;
default:
semerr(gettext("Invalid property group flag "
"%c."), *cp);
return;
}
}
}
pg = scf_pg_create(g_hndl);
if (pg == NULL)
scfdie();
if (cur_inst != NULL)
ret = scf_instance_add_pg(cur_inst, name, type, flgs, pg);
else
ret = scf_service_add_pg(cur_svc, name, type, flgs, pg);
if (ret != SCF_SUCCESS) {
switch (scf_error()) {
case SCF_ERROR_INVALID_ARGUMENT:
semerr(gettext("Name, type, or flags are invalid.\n"));
break;
case SCF_ERROR_EXISTS:
semerr(gettext("Property group already exists.\n"));
break;
case SCF_ERROR_PERMISSION_DENIED:
semerr(emsg_permission_denied);
break;
case SCF_ERROR_BACKEND_ACCESS:
semerr(gettext("Backend refused access.\n"));
break;
default:
scfdie();
}
}
scf_pg_destroy(pg);
private_refresh();
}
void
lscf_delpg(char *name)
{
lscf_prep_hndl();
if (cur_snap != NULL) {
semerr(emsg_cant_modify_snapshots);
return;
}
if (cur_inst == NULL && cur_svc == NULL) {
semerr(emsg_entity_not_selected);
return;
}
if (strchr(name, '/') != NULL) {
semerr(emsg_invalid_pg_name, name);
return;
}
lscf_delprop(name);
}
/*
* scf_delhash() is used to remove the property group related to the
* hash entry for a specific manifest in the repository. pgname will be
* constructed from the location of the manifest file. If deathrow isn't 0,
* manifest file doesn't need to exist (manifest string will be used as
* an absolute path).
*/
void
lscf_delhash(char *manifest, int deathrow)
{
char *pgname;
if (cur_snap != NULL ||
cur_inst != NULL || cur_svc != NULL) {
warn(gettext("error, an entity is selected\n"));
return;
}
/* select smf/manifest */
lscf_select(HASH_SVC);
/*
* Translate the manifest file name to property name. In the deathrow
* case, the manifest file does not need to exist.
*/
pgname = mhash_filename_to_propname(manifest,
deathrow ? B_TRUE : B_FALSE);
if (pgname == NULL) {
warn(gettext("cannot resolve pathname for %s\n"), manifest);
return;
}
/* delete the hash property name */
lscf_delpg(pgname);
}
void
lscf_listprop(const char *pattern)
{
lscf_prep_hndl();
listprop(pattern, 0, 0);
}
int
lscf_setprop(const char *pgname, const char *type, const char *value,
const uu_list_t *values)
{
scf_type_t ty, current_ty;
scf_service_t *svc;
scf_propertygroup_t *pg, *parent_pg;
scf_property_t *prop, *parent_prop;
scf_pg_tmpl_t *pgt;
scf_prop_tmpl_t *prt;
int ret, result = 0;
scf_transaction_t *tx;
scf_transaction_entry_t *e;
scf_value_t *v;
uu_list_walk_t *walk;
string_list_t *sp;
char *propname;
int req_quotes = 0;
lscf_prep_hndl();
if ((e = scf_entry_create(g_hndl)) == NULL ||
(svc = scf_service_create(g_hndl)) == NULL ||
(parent_pg = scf_pg_create(g_hndl)) == NULL ||
(pg = scf_pg_create(g_hndl)) == NULL ||
(parent_prop = scf_property_create(g_hndl)) == NULL ||
(prop = scf_property_create(g_hndl)) == NULL ||
(pgt = scf_tmpl_pg_create(g_hndl)) == NULL ||
(prt = scf_tmpl_prop_create(g_hndl)) == NULL ||
(tx = scf_transaction_create(g_hndl)) == NULL)
scfdie();
if (cur_snap != NULL) {
semerr(emsg_cant_modify_snapshots);
goto fail;
}
if (cur_inst == NULL && cur_svc == NULL) {
semerr(emsg_entity_not_selected);
goto fail;
}
propname = strchr(pgname, '/');
if (propname == NULL) {
semerr(gettext("Property names must contain a `/'.\n"));
goto fail;
}
*propname = '\0';
++propname;
if (type != NULL) {
ty = string_to_type(type);
if (ty == SCF_TYPE_INVALID) {
semerr(gettext("Unknown type \"%s\".\n"), type);
goto fail;
}
}
if (cur_inst != NULL)
ret = scf_instance_get_pg(cur_inst, pgname, pg);
else
ret = scf_service_get_pg(cur_svc, pgname, pg);
if (ret != SCF_SUCCESS) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
semerr(emsg_no_such_pg, pgname);
goto fail;
case SCF_ERROR_INVALID_ARGUMENT:
semerr(emsg_invalid_pg_name, pgname);
goto fail;
default:
scfdie();
break;
}
}
do {
if (scf_pg_update(pg) == -1)
scfdie();
if (scf_transaction_start(tx, pg) != SCF_SUCCESS) {
if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
semerr(emsg_permission_denied);
goto fail;
}
ret = scf_pg_get_property(pg, propname, prop);
if (ret == SCF_SUCCESS) {
if (scf_property_type(prop, &current_ty) != SCF_SUCCESS)
scfdie();
if (type == NULL)
ty = current_ty;
if (scf_transaction_property_change_type(tx, e,
propname, ty) == -1)
scfdie();
} else if (scf_error() == SCF_ERROR_NOT_FOUND) {
/* Infer the type, if possible. */
if (type == NULL) {
/*
* First check if we're an instance and the
* property is set on the service.
*/
if (cur_inst != NULL &&
scf_instance_get_parent(cur_inst,
svc) == 0 &&
scf_service_get_pg(cur_svc, pgname,
parent_pg) == 0 &&
scf_pg_get_property(parent_pg, propname,
parent_prop) == 0 &&
scf_property_type(parent_prop,
&current_ty) == 0) {
ty = current_ty;
/* Then check for a type set in a template. */
} else if (scf_tmpl_get_by_pg(pg, pgt,
0) == 0 &&
scf_tmpl_get_by_prop(pgt, propname, prt,
0) == 0 &&
scf_tmpl_prop_type(prt, &current_ty) == 0) {
ty = current_ty;
/* If type can't be inferred, fail. */
} else {
semerr(gettext("Type required for new "
"properties.\n"));
goto fail;
}
}
if (scf_transaction_property_new(tx, e, propname,
ty) == -1)
scfdie();
} else if (scf_error() == SCF_ERROR_INVALID_ARGUMENT) {
semerr(emsg_invalid_prop_name, propname);
goto fail;
} else {
scfdie();
}
if (ty == SCF_TYPE_ASTRING || ty == SCF_TYPE_USTRING)
req_quotes = 1;
if (value != NULL) {
v = string_to_value(value, ty, 0);
if (v == NULL)
goto fail;
ret = scf_entry_add_value(e, v);
assert(ret == SCF_SUCCESS);
} else {
assert(values != NULL);
walk = uu_list_walk_start((uu_list_t *)values,
UU_DEFAULT);
if (walk == NULL)
uu_die(gettext("Could not walk list"));
for (sp = uu_list_walk_next(walk); sp != NULL;
sp = uu_list_walk_next(walk)) {
v = string_to_value(sp->str, ty, req_quotes);
if (v == NULL) {
scf_entry_destroy_children(e);
goto fail;
}
ret = scf_entry_add_value(e, v);
assert(ret == SCF_SUCCESS);
}
uu_list_walk_end(walk);
}
result = scf_transaction_commit(tx);
scf_transaction_reset(tx);
scf_entry_destroy_children(e);
} while (result == 0);
if (result < 0) {
if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
semerr(emsg_permission_denied);
goto fail;
}
ret = 0;
private_refresh();
goto cleanup;
fail:
ret = -1;
cleanup:
scf_transaction_destroy(tx);
scf_entry_destroy(e);
scf_service_destroy(svc);
scf_pg_destroy(parent_pg);
scf_pg_destroy(pg);
scf_property_destroy(parent_prop);
scf_property_destroy(prop);
scf_tmpl_pg_destroy(pgt);
scf_tmpl_prop_destroy(prt);
return (ret);
}
void
lscf_delprop(char *pgn)
{
char *slash, *pn;
scf_propertygroup_t *pg;
scf_transaction_t *tx;
scf_transaction_entry_t *e;
int ret;
lscf_prep_hndl();
if (cur_snap != NULL) {
semerr(emsg_cant_modify_snapshots);
return;
}
if (cur_inst == NULL && cur_svc == NULL) {
semerr(emsg_entity_not_selected);
return;
}
pg = scf_pg_create(g_hndl);
if (pg == NULL)
scfdie();
slash = strchr(pgn, '/');
if (slash == NULL) {
pn = NULL;
} else {
*slash = '\0';
pn = slash + 1;
}
if (cur_inst != NULL)
ret = scf_instance_get_pg(cur_inst, pgn, pg);
else
ret = scf_service_get_pg(cur_svc, pgn, pg);
if (ret != SCF_SUCCESS) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
semerr(emsg_no_such_pg, pgn);
break;
case SCF_ERROR_INVALID_ARGUMENT:
semerr(emsg_invalid_pg_name, pgn);
break;
default:
scfdie();
}
scf_pg_destroy(pg);
return;
}
if (pn == NULL) {
/* Try to delete the property group. */
if (scf_pg_delete(pg) != SCF_SUCCESS) {
if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
semerr(emsg_permission_denied);
} else {
private_refresh();
}
scf_pg_destroy(pg);
return;
}
e = scf_entry_create(g_hndl);
tx = scf_transaction_create(g_hndl);
do {
if (scf_pg_update(pg) == -1)
scfdie();
if (scf_transaction_start(tx, pg) != SCF_SUCCESS) {
if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
semerr(emsg_permission_denied);
break;
}
if (scf_transaction_property_delete(tx, e, pn) != SCF_SUCCESS) {
if (scf_error() == SCF_ERROR_NOT_FOUND) {
semerr(gettext("No such property %s/%s.\n"),
pgn, pn);
break;
} else if (scf_error() == SCF_ERROR_INVALID_ARGUMENT) {
semerr(emsg_invalid_prop_name, pn);
break;
} else {
scfdie();
}
}
ret = scf_transaction_commit(tx);
if (ret == 0)
scf_transaction_reset(tx);
} while (ret == 0);
if (ret < 0) {
if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
semerr(emsg_permission_denied);
} else {
private_refresh();
}
scf_transaction_destroy(tx);
scf_entry_destroy(e);
scf_pg_destroy(pg);
}
/*
* Property editing.
*/
static int
write_edit_script(FILE *strm)
{
char *fmribuf;
ssize_t fmrilen;
scf_propertygroup_t *pg;
scf_property_t *prop;
scf_value_t *val;
scf_type_t ty;
int ret, result = 0;
scf_iter_t *iter, *piter, *viter;
char *buf, *tybuf, *pname;
const char *emsg_write_error;
emsg_write_error = gettext("Error writing temoprary file: %s.\n");
/* select fmri */
if (cur_inst != NULL) {
fmrilen = scf_instance_to_fmri(cur_inst, NULL, 0);
if (fmrilen < 0)
scfdie();
fmribuf = safe_malloc(fmrilen + 1);
if (scf_instance_to_fmri(cur_inst, fmribuf, fmrilen + 1) < 0)
scfdie();
} else {
assert(cur_svc != NULL);
fmrilen = scf_service_to_fmri(cur_svc, NULL, 0);
if (fmrilen < 0)
scfdie();
fmribuf = safe_malloc(fmrilen + 1);
if (scf_service_to_fmri(cur_svc, fmribuf, fmrilen + 1) < 0)
scfdie();
}
if (fprintf(strm, "select %s\n\n", fmribuf) < 0) {
warn(emsg_write_error, strerror(errno));
free(fmribuf);
return (-1);
}
free(fmribuf);
if ((pg = scf_pg_create(g_hndl)) == NULL ||
(prop = scf_property_create(g_hndl)) == NULL ||
(val = scf_value_create(g_hndl)) == NULL ||
(iter = scf_iter_create(g_hndl)) == NULL ||
(piter = scf_iter_create(g_hndl)) == NULL ||
(viter = scf_iter_create(g_hndl)) == NULL)
scfdie();
buf = safe_malloc(max_scf_name_len + 1);
tybuf = safe_malloc(max_scf_pg_type_len + 1);
pname = safe_malloc(max_scf_name_len + 1);
if (cur_inst != NULL)
ret = scf_iter_instance_pgs(iter, cur_inst);
else
ret = scf_iter_service_pgs(iter, cur_svc);
if (ret != SCF_SUCCESS)
scfdie();
while ((ret = scf_iter_next_pg(iter, pg)) == 1) {
int ret2;
/*
* # delprop pg
* # addpg pg type
*/
if (scf_pg_get_name(pg, buf, max_scf_name_len + 1) < 0)
scfdie();
if (scf_pg_get_type(pg, tybuf, max_scf_pg_type_len + 1) < 0)
scfdie();
if (fprintf(strm, "# Property group \"%s\"\n"
"# delprop %s\n"
"# addpg %s %s\n", buf, buf, buf, tybuf) < 0) {
warn(emsg_write_error, strerror(errno));
result = -1;
goto out;
}
/* # setprop pg/prop = (values) */
if (scf_iter_pg_properties(piter, pg) != SCF_SUCCESS)
scfdie();
while ((ret2 = scf_iter_next_property(piter, prop)) == 1) {
int first = 1;
int ret3;
int multiple;
int is_str;
scf_type_t bty;
if (scf_property_get_name(prop, pname,
max_scf_name_len + 1) < 0)
scfdie();
if (scf_property_type(prop, &ty) != 0)
scfdie();
multiple = prop_has_multiple_values(prop, val);
if (fprintf(strm, "# setprop %s/%s = %s: %s", buf,
pname, scf_type_to_string(ty), multiple ? "(" : "")
< 0) {
warn(emsg_write_error, strerror(errno));
result = -1;
goto out;
}
(void) scf_type_base_type(ty, &bty);
is_str = (bty == SCF_TYPE_ASTRING);
if (scf_iter_property_values(viter, prop) !=
SCF_SUCCESS)
scfdie();
while ((ret3 = scf_iter_next_value(viter, val)) == 1) {
char *buf;
ssize_t buflen;
buflen = scf_value_get_as_string(val, NULL, 0);
if (buflen < 0)
scfdie();
buf = safe_malloc(buflen + 1);
if (scf_value_get_as_string(val, buf,
buflen + 1) < 0)
scfdie();
if (first)
first = 0;
else {
if (putc(' ', strm) != ' ') {
warn(emsg_write_error,
strerror(errno));
result = -1;
goto out;
}
}
if ((is_str && multiple) ||
strpbrk(buf, CHARS_TO_QUOTE) != NULL) {
(void) putc('"', strm);
(void) quote_and_print(buf, strm, 1);
(void) putc('"', strm);
if (ferror(strm)) {
warn(emsg_write_error,
strerror(errno));
result = -1;
goto out;
}
} else {
if (fprintf(strm, "%s", buf) < 0) {
warn(emsg_write_error,
strerror(errno));
result = -1;
goto out;
}
}
free(buf);
}
if (ret3 < 0 &&
scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
/* Write closing paren if mult-value property */
if ((multiple && putc(')', strm) == EOF) ||
/* Write final newline */
fputc('\n', strm) == EOF) {
warn(emsg_write_error, strerror(errno));
result = -1;
goto out;
}
}
if (ret2 < 0)
scfdie();
if (fputc('\n', strm) == EOF) {
warn(emsg_write_error, strerror(errno));
result = -1;
goto out;
}
}
if (ret < 0)
scfdie();
out:
free(pname);
free(tybuf);
free(buf);
scf_iter_destroy(viter);
scf_iter_destroy(piter);
scf_iter_destroy(iter);
scf_value_destroy(val);
scf_property_destroy(prop);
scf_pg_destroy(pg);
if (result == 0) {
if (fflush(strm) != 0) {
warn(emsg_write_error, strerror(errno));
return (-1);
}
}
return (result);
}
int
lscf_editprop()
{
char *buf, *editor;
size_t bufsz;
int tmpfd;
char tempname[] = TEMP_FILE_PATTERN;
lscf_prep_hndl();
if (cur_snap != NULL) {
semerr(emsg_cant_modify_snapshots);
return (-1);
}
if (cur_svc == NULL && cur_inst == NULL) {
semerr(emsg_entity_not_selected);
return (-1);
}
tmpfd = mkstemp(tempname);
if (tmpfd == -1) {
semerr(gettext("Could not create temporary file.\n"));
return (-1);
}
(void) strcpy(tempfilename, tempname);
tempfile = fdopen(tmpfd, "r+");
if (tempfile == NULL) {
warn(gettext("Could not create temporary file.\n"));
if (close(tmpfd) == -1)
warn(gettext("Could not close temporary file: %s.\n"),
strerror(errno));
remove_tempfile();
return (-1);
}
if (write_edit_script(tempfile) == -1) {
remove_tempfile();
return (-1);
}
editor = getenv("EDITOR");
if (editor == NULL)
editor = "vi";
bufsz = strlen(editor) + 1 + strlen(tempname) + 1;
buf = safe_malloc(bufsz);
if (snprintf(buf, bufsz, "%s %s", editor, tempname) < 0)
uu_die(gettext("Error creating editor command"));
if (system(buf) == -1) {
semerr(gettext("Could not launch editor %s: %s\n"), editor,
strerror(errno));
free(buf);
remove_tempfile();
return (-1);
}
free(buf);
(void) engine_source(tempname, est->sc_cmd_flags & SC_CMD_IACTIVE);
remove_tempfile();
return (0);
}
static void
add_string(uu_list_t *strlist, const char *str)
{
string_list_t *elem;
elem = safe_malloc(sizeof (*elem));
uu_list_node_init(elem, &elem->node, string_pool);
elem->str = safe_strdup(str);
if (uu_list_append(strlist, elem) != 0)
uu_die(gettext("libuutil error: %s\n"),
uu_strerror(uu_error()));
}
static int
remove_string(uu_list_t *strlist, const char *str)
{
uu_list_walk_t *elems;
string_list_t *sp;
/*
* Find the element that needs to be removed.
*/
elems = uu_list_walk_start(strlist, UU_DEFAULT);
while ((sp = uu_list_walk_next(elems)) != NULL) {
if (strcmp(sp->str, str) == 0)
break;
}
uu_list_walk_end(elems);
/*
* Returning 1 here as the value was not found, this
* might not be an error. Leave it to the caller to
* decide.
*/
if (sp == NULL) {
return (1);
}
uu_list_remove(strlist, sp);
free(sp->str);
free(sp);
return (0);
}
/*
* Get all property values that don't match the given glob pattern,
* if a pattern is specified.
*/
static void
get_prop_values(scf_property_t *prop, uu_list_t *values,
const char *pattern)
{
scf_iter_t *iter;
scf_value_t *val;
int ret;
if ((iter = scf_iter_create(g_hndl)) == NULL ||
(val = scf_value_create(g_hndl)) == NULL)
scfdie();
if (scf_iter_property_values(iter, prop) != 0)
scfdie();
while ((ret = scf_iter_next_value(iter, val)) == 1) {
char *buf;
ssize_t vlen, szret;
vlen = scf_value_get_as_string(val, NULL, 0);
if (vlen < 0)
scfdie();
buf = safe_malloc(vlen + 1);
szret = scf_value_get_as_string(val, buf, vlen + 1);
if (szret < 0)
scfdie();
assert(szret <= vlen);
if (pattern == NULL || fnmatch(pattern, buf, 0) != 0)
add_string(values, buf);
free(buf);
}
if (ret == -1)
scfdie();
scf_value_destroy(val);
scf_iter_destroy(iter);
}
static int
lscf_setpropvalue(const char *pgname, const char *type,
const char *arg, int isadd, int isnotfoundok)
{
scf_type_t ty;
scf_propertygroup_t *pg;
scf_property_t *prop;
int ret, result = 0;
scf_transaction_t *tx;
scf_transaction_entry_t *e;
scf_value_t *v;
string_list_t *sp;
char *propname;
uu_list_t *values;
uu_list_walk_t *walk;
void *cookie = NULL;
char *pattern = NULL;
lscf_prep_hndl();
if ((values = uu_list_create(string_pool, NULL, 0)) == NULL)
uu_die(gettext("Could not create property list: %s\n"),
uu_strerror(uu_error()));
if (!isadd)
pattern = safe_strdup(arg);
if ((e = scf_entry_create(g_hndl)) == NULL ||
(pg = scf_pg_create(g_hndl)) == NULL ||
(prop = scf_property_create(g_hndl)) == NULL ||
(tx = scf_transaction_create(g_hndl)) == NULL)
scfdie();
if (cur_snap != NULL) {
semerr(emsg_cant_modify_snapshots);
goto fail;
}
if (cur_inst == NULL && cur_svc == NULL) {
semerr(emsg_entity_not_selected);
goto fail;
}
propname = strchr(pgname, '/');
if (propname == NULL) {
semerr(gettext("Property names must contain a `/'.\n"));
goto fail;
}
*propname = '\0';
++propname;
if (type != NULL) {
ty = string_to_type(type);
if (ty == SCF_TYPE_INVALID) {
semerr(gettext("Unknown type \"%s\".\n"), type);
goto fail;
}
}
if (cur_inst != NULL)
ret = scf_instance_get_pg(cur_inst, pgname, pg);
else
ret = scf_service_get_pg(cur_svc, pgname, pg);
if (ret != 0) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
if (isnotfoundok) {
result = 0;
} else {
semerr(emsg_no_such_pg, pgname);
result = -1;
}
goto out;
case SCF_ERROR_INVALID_ARGUMENT:
semerr(emsg_invalid_pg_name, pgname);
goto fail;
default:
scfdie();
}
}
do {
if (scf_pg_update(pg) == -1)
scfdie();
if (scf_transaction_start(tx, pg) != 0) {
if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
semerr(emsg_permission_denied);
goto fail;
}
ret = scf_pg_get_property(pg, propname, prop);
if (ret == 0) {
scf_type_t ptype;
char *pat = pattern;
if (scf_property_type(prop, &ptype) != 0)
scfdie();
if (isadd) {
if (type != NULL && ptype != ty) {
semerr(gettext("Property \"%s\" is not "
"of type \"%s\".\n"), propname,
type);
goto fail;
}
pat = NULL;
} else {
size_t len = strlen(pat);
if (len > 0 && pat[len - 1] == '\"')
pat[len - 1] = '\0';
if (len > 0 && pat[0] == '\"')
pat++;
}
ty = ptype;
get_prop_values(prop, values, pat);
if (isadd)
add_string(values, arg);
if (scf_transaction_property_change(tx, e,
propname, ty) == -1)
scfdie();
} else if (scf_error() == SCF_ERROR_NOT_FOUND) {
if (isadd) {
if (type == NULL) {
semerr(gettext("Type required "
"for new properties.\n"));
goto fail;
}
add_string(values, arg);
if (scf_transaction_property_new(tx, e,
propname, ty) == -1)
scfdie();
} else if (isnotfoundok) {
result = 0;
goto out;
} else {
semerr(gettext("No such property %s/%s.\n"),
pgname, propname);
result = -1;
goto out;
}
} else if (scf_error() == SCF_ERROR_INVALID_ARGUMENT) {
semerr(emsg_invalid_prop_name, propname);
goto fail;
} else {
scfdie();
}
walk = uu_list_walk_start(values, UU_DEFAULT);
if (walk == NULL)
uu_die(gettext("Could not walk property list.\n"));
for (sp = uu_list_walk_next(walk); sp != NULL;
sp = uu_list_walk_next(walk)) {
v = string_to_value(sp->str, ty, 0);
if (v == NULL) {
scf_entry_destroy_children(e);
goto fail;
}
ret = scf_entry_add_value(e, v);
assert(ret == 0);
}
uu_list_walk_end(walk);
result = scf_transaction_commit(tx);
scf_transaction_reset(tx);
scf_entry_destroy_children(e);
} while (result == 0);
if (result < 0) {
if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
semerr(emsg_permission_denied);
goto fail;
}
result = 0;
private_refresh();
out:
scf_transaction_destroy(tx);
scf_entry_destroy(e);
scf_pg_destroy(pg);
scf_property_destroy(prop);
free(pattern);
while ((sp = uu_list_teardown(values, &cookie)) != NULL) {
free(sp->str);
free(sp);
}
uu_list_destroy(values);
return (result);
fail:
result = -1;
goto out;
}
int
lscf_addpropvalue(const char *pgname, const char *type, const char *value)
{
return (lscf_setpropvalue(pgname, type, value, 1, 0));
}
int
lscf_delpropvalue(const char *pgname, const char *pattern, int isnotfoundok)
{
return (lscf_setpropvalue(pgname, NULL, pattern, 0, isnotfoundok));
}
/*
* Look for a standard start method, first in the instance (if any),
* then the service.
*/
static const char *
start_method_name(int *in_instance)
{
scf_propertygroup_t *pg;
char **p;
int ret;
scf_instance_t *inst = cur_inst;
if ((pg = scf_pg_create(g_hndl)) == NULL)
scfdie();
again:
for (p = start_method_names; *p != NULL; p++) {
if (inst != NULL)
ret = scf_instance_get_pg(inst, *p, pg);
else
ret = scf_service_get_pg(cur_svc, *p, pg);
if (ret == 0) {
size_t bufsz = strlen(SCF_GROUP_METHOD) + 1;
char *buf = safe_malloc(bufsz);
if ((ret = scf_pg_get_type(pg, buf, bufsz)) < 0) {
free(buf);
continue;
}
if (strcmp(buf, SCF_GROUP_METHOD) != 0) {
free(buf);
continue;
}
free(buf);
*in_instance = (inst != NULL);
scf_pg_destroy(pg);
return (*p);
}
if (scf_error() == SCF_ERROR_NOT_FOUND)
continue;
scfdie();
}
if (inst != NULL) {
inst = NULL;
goto again;
}
scf_pg_destroy(pg);
return (NULL);
}
static int
addpg(const char *name, const char *type)
{
scf_propertygroup_t *pg;
int ret;
pg = scf_pg_create(g_hndl);
if (pg == NULL)
scfdie();
if (cur_inst != NULL)
ret = scf_instance_add_pg(cur_inst, name, type, 0, pg);
else
ret = scf_service_add_pg(cur_svc, name, type, 0, pg);
if (ret != 0) {
switch (scf_error()) {
case SCF_ERROR_EXISTS:
ret = 0;
break;
case SCF_ERROR_PERMISSION_DENIED:
semerr(emsg_permission_denied);
break;
default:
scfdie();
}
}
scf_pg_destroy(pg);
return (ret);
}
int
lscf_setenv(uu_list_t *args, int isunset)
{
int ret = 0;
size_t i;
int argc;
char **argv = NULL;
string_list_t *slp;
char *pattern;
char *prop;
int do_service = 0;
int do_instance = 0;
const char *method = NULL;
const char *name = NULL;
const char *value = NULL;
scf_instance_t *saved_cur_inst = cur_inst;
lscf_prep_hndl();
argc = uu_list_numnodes(args);
if (argc < 1)
goto usage;
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;
for (;;) {
ret = getopt(argc, argv, "sim:");
if (ret == -1)
break;
switch (ret) {
case 's':
do_service = 1;
cur_inst = NULL;
break;
case 'i':
do_instance = 1;
break;
case 'm':
method = optarg;
break;
case '?':
goto usage;
default:
bad_error("getopt", ret);
}
}
argc -= optind;
if ((do_service && do_instance) ||
(isunset && argc != 1) ||
(!isunset && argc != 2))
goto usage;
name = argv[optind];
if (!isunset)
value = argv[optind + 1];
if (cur_snap != NULL) {
semerr(emsg_cant_modify_snapshots);
ret = -1;
goto out;
}
if (cur_inst == NULL && cur_svc == NULL) {
semerr(emsg_entity_not_selected);
ret = -1;
goto out;
}
if (do_instance && cur_inst == NULL) {
semerr(gettext("No instance is selected.\n"));
ret = -1;
goto out;
}
if (do_service && cur_svc == NULL) {
semerr(gettext("No service is selected.\n"));
ret = -1;
goto out;
}
if (method == NULL) {
if (do_instance || do_service) {
method = "method_context";
if (!isunset) {
ret = addpg("method_context",
SCF_GROUP_FRAMEWORK);
if (ret != 0)
goto out;
}
} else {
int in_instance;
method = start_method_name(&in_instance);
if (method == NULL) {
semerr(gettext(
"Couldn't find start method; please "
"specify a method with '-m'.\n"));
ret = -1;
goto out;
}
if (!in_instance)
cur_inst = NULL;
}
} else {
scf_propertygroup_t *pg;
size_t bufsz;
char *buf;
int ret;
if ((pg = scf_pg_create(g_hndl)) == NULL)
scfdie();
if (cur_inst != NULL)
ret = scf_instance_get_pg(cur_inst, method, pg);
else
ret = scf_service_get_pg(cur_svc, method, pg);
if (ret != 0) {
scf_pg_destroy(pg);
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
semerr(gettext("Couldn't find the method "
"\"%s\".\n"), method);
goto out;
case SCF_ERROR_INVALID_ARGUMENT:
semerr(gettext("Invalid method name \"%s\".\n"),
method);
goto out;
default:
scfdie();
}
}
bufsz = strlen(SCF_GROUP_METHOD) + 1;
buf = safe_malloc(bufsz);
if (scf_pg_get_type(pg, buf, bufsz) < 0 ||
strcmp(buf, SCF_GROUP_METHOD) != 0) {
semerr(gettext("Property group \"%s\" is not of type "
"\"method\".\n"), method);
ret = -1;
free(buf);
scf_pg_destroy(pg);
goto out;
}
free(buf);
scf_pg_destroy(pg);
}
prop = uu_msprintf("%s/environment", method);
pattern = uu_msprintf("%s=*", name);
if (prop == NULL || pattern == NULL)
uu_die(gettext("Out of memory.\n"));
ret = lscf_delpropvalue(prop, pattern, !isunset);
if (ret == 0 && !isunset) {
uu_free(pattern);
uu_free(prop);
prop = uu_msprintf("%s/environment", method);
pattern = uu_msprintf("%s=%s", name, value);
if (prop == NULL || pattern == NULL)
uu_die(gettext("Out of memory.\n"));
ret = lscf_addpropvalue(prop, "astring:", pattern);
}
uu_free(pattern);
uu_free(prop);
out:
cur_inst = saved_cur_inst;
free(argv);
return (ret);
usage:
ret = -2;
goto out;
}
/*
* Snapshot commands
*/
void
lscf_listsnap()
{
scf_snapshot_t *snap;
scf_iter_t *iter;
char *nb;
int r;
lscf_prep_hndl();
if (cur_inst == NULL) {
semerr(gettext("Instance not selected.\n"));
return;
}
if ((snap = scf_snapshot_create(g_hndl)) == NULL ||
(iter = scf_iter_create(g_hndl)) == NULL)
scfdie();
if (scf_iter_instance_snapshots(iter, cur_inst) != SCF_SUCCESS)
scfdie();
nb = safe_malloc(max_scf_name_len + 1);
while ((r = scf_iter_next_snapshot(iter, snap)) == 1) {
if (scf_snapshot_get_name(snap, nb, max_scf_name_len + 1) < 0)
scfdie();
(void) puts(nb);
}
if (r < 0)
scfdie();
free(nb);
scf_iter_destroy(iter);
scf_snapshot_destroy(snap);
}
void
lscf_selectsnap(const char *name)
{
scf_snapshot_t *snap;
scf_snaplevel_t *level;
lscf_prep_hndl();
if (cur_inst == NULL) {
semerr(gettext("Instance not selected.\n"));
return;
}
if (cur_snap != NULL) {
if (name != NULL) {
char *cur_snap_name;
boolean_t nochange;
cur_snap_name = safe_malloc(max_scf_name_len + 1);
if (scf_snapshot_get_name(cur_snap, cur_snap_name,
max_scf_name_len + 1) < 0)
scfdie();
nochange = strcmp(name, cur_snap_name) == 0;
free(cur_snap_name);
if (nochange)
return;
}
unselect_cursnap();
}
if (name == NULL)
return;
if ((snap = scf_snapshot_create(g_hndl)) == NULL ||
(level = scf_snaplevel_create(g_hndl)) == NULL)
scfdie();
if (scf_instance_get_snapshot(cur_inst, name, snap) !=
SCF_SUCCESS) {
switch (scf_error()) {
case SCF_ERROR_INVALID_ARGUMENT:
semerr(gettext("Invalid name \"%s\".\n"), name);
break;
case SCF_ERROR_NOT_FOUND:
semerr(gettext("No such snapshot \"%s\".\n"), name);
break;
default:
scfdie();
}
scf_snaplevel_destroy(level);
scf_snapshot_destroy(snap);
return;
}
/* Load the snaplevels into our list. */
cur_levels = uu_list_create(snaplevel_pool, NULL, 0);
if (cur_levels == NULL)
uu_die(gettext("Could not create list: %s\n"),
uu_strerror(uu_error()));
if (scf_snapshot_get_base_snaplevel(snap, level) != SCF_SUCCESS) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
semerr(gettext("Snapshot has no snaplevels.\n"));
scf_snaplevel_destroy(level);
scf_snapshot_destroy(snap);
return;
}
cur_snap = snap;
for (;;) {
cur_elt = safe_malloc(sizeof (*cur_elt));
uu_list_node_init(cur_elt, &cur_elt->list_node,
snaplevel_pool);
cur_elt->sl = level;
if (uu_list_insert_after(cur_levels, NULL, cur_elt) != 0)
uu_die(gettext("libuutil error: %s\n"),
uu_strerror(uu_error()));
level = scf_snaplevel_create(g_hndl);
if (level == NULL)
scfdie();
if (scf_snaplevel_get_next_snaplevel(cur_elt->sl,
level) != SCF_SUCCESS) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
scf_snaplevel_destroy(level);
break;
}
}
cur_elt = uu_list_last(cur_levels);
cur_level = cur_elt->sl;
}
/*
* Copies the properties & values in src to dst. Assumes src won't change.
* Returns -1 if permission is denied, -2 if another transaction interrupts,
* and 0 on success.
*
* If enabled is 0 or 1, its value is used for the SCF_PROPERTY_ENABLED
* property, if it is copied and has type boolean. (See comment in
* lscf_revert()).
*/
static int
pg_copy(const scf_propertygroup_t *src, scf_propertygroup_t *dst,
uint8_t enabled)
{
scf_transaction_t *tx;
scf_iter_t *iter, *viter;
scf_property_t *prop;
scf_value_t *v;
char *nbuf;
int r;
tx = scf_transaction_create(g_hndl);
if (tx == NULL)
scfdie();
if (scf_transaction_start(tx, dst) != SCF_SUCCESS) {
if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
scf_transaction_destroy(tx);
return (-1);
}
if ((iter = scf_iter_create(g_hndl)) == NULL ||
(prop = scf_property_create(g_hndl)) == NULL ||
(viter = scf_iter_create(g_hndl)) == NULL)
scfdie();
nbuf = safe_malloc(max_scf_name_len + 1);
if (scf_iter_pg_properties(iter, src) != SCF_SUCCESS)
scfdie();
for (;;) {
scf_transaction_entry_t *e;
scf_type_t ty;
r = scf_iter_next_property(iter, prop);
if (r == -1)
scfdie();
if (r == 0)
break;
e = scf_entry_create(g_hndl);
if (e == NULL)
scfdie();
if (scf_property_type(prop, &ty) != SCF_SUCCESS)
scfdie();
if (scf_property_get_name(prop, nbuf, max_scf_name_len + 1) < 0)
scfdie();
if (scf_transaction_property_new(tx, e, nbuf,
ty) != SCF_SUCCESS)
scfdie();
if ((enabled == 0 || enabled == 1) &&
strcmp(nbuf, scf_property_enabled) == 0 &&
ty == SCF_TYPE_BOOLEAN) {
v = scf_value_create(g_hndl);
if (v == NULL)
scfdie();
scf_value_set_boolean(v, enabled);
if (scf_entry_add_value(e, v) != 0)
scfdie();
} else {
if (scf_iter_property_values(viter, prop) != 0)
scfdie();
for (;;) {
v = scf_value_create(g_hndl);
if (v == NULL)
scfdie();
r = scf_iter_next_value(viter, v);
if (r == -1)
scfdie();
if (r == 0) {
scf_value_destroy(v);
break;
}
if (scf_entry_add_value(e, v) != SCF_SUCCESS)
scfdie();
}
}
}
free(nbuf);
scf_iter_destroy(viter);
scf_property_destroy(prop);
scf_iter_destroy(iter);
r = scf_transaction_commit(tx);
if (r == -1 && scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
scf_transaction_destroy_children(tx);
scf_transaction_destroy(tx);
switch (r) {
case 1: return (0);
case 0: return (-2);
case -1: return (-1);
default:
abort();
}
/* NOTREACHED */
}
void
lscf_revert(const char *snapname)
{
scf_snapshot_t *snap, *prev;
scf_snaplevel_t *level, *nlevel;
scf_iter_t *iter;
scf_propertygroup_t *pg, *npg;
scf_property_t *prop;
scf_value_t *val;
char *nbuf, *tbuf;
uint8_t enabled;
lscf_prep_hndl();
if (cur_inst == NULL) {
semerr(gettext("Instance not selected.\n"));
return;
}
if (snapname != NULL) {
snap = scf_snapshot_create(g_hndl);
if (snap == NULL)
scfdie();
if (scf_instance_get_snapshot(cur_inst, snapname, snap) !=
SCF_SUCCESS) {
switch (scf_error()) {
case SCF_ERROR_INVALID_ARGUMENT:
semerr(gettext("Invalid snapshot name "
"\"%s\".\n"), snapname);
break;
case SCF_ERROR_NOT_FOUND:
semerr(gettext("No such snapshot.\n"));
break;
default:
scfdie();
}
scf_snapshot_destroy(snap);
return;
}
} else {
if (cur_snap != NULL) {
snap = cur_snap;
} else {
semerr(gettext("No snapshot selected.\n"));
return;
}
}
if ((prev = scf_snapshot_create(g_hndl)) == NULL ||
(level = scf_snaplevel_create(g_hndl)) == NULL ||
(iter = scf_iter_create(g_hndl)) == NULL ||
(pg = scf_pg_create(g_hndl)) == NULL ||
(npg = scf_pg_create(g_hndl)) == NULL ||
(prop = scf_property_create(g_hndl)) == NULL ||
(val = scf_value_create(g_hndl)) == NULL)
scfdie();
nbuf = safe_malloc(max_scf_name_len + 1);
tbuf = safe_malloc(max_scf_pg_type_len + 1);
/* Take the "previous" snapshot before we blow away the properties. */
if (scf_instance_get_snapshot(cur_inst, snap_previous, prev) == 0) {
if (_scf_snapshot_take_attach(cur_inst, prev) != 0)
scfdie();
} else {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
if (_scf_snapshot_take_new(cur_inst, snap_previous, prev) != 0)
scfdie();
}
/* Save general/enabled, since we're probably going to replace it. */
enabled = 2;
if (scf_instance_get_pg(cur_inst, scf_pg_general, pg) == 0 &&
scf_pg_get_property(pg, scf_property_enabled, prop) == 0 &&
scf_property_get_value(prop, val) == 0)
(void) scf_value_get_boolean(val, &enabled);
if (scf_snapshot_get_base_snaplevel(snap, level) != SCF_SUCCESS) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
goto out;
}
for (;;) {
boolean_t isinst;
uint32_t flags;
int r;
/* Clear the properties from the corresponding entity. */
isinst = snaplevel_is_instance(level);
if (!isinst)
r = scf_iter_service_pgs(iter, cur_svc);
else
r = scf_iter_instance_pgs(iter, cur_inst);
if (r != SCF_SUCCESS)
scfdie();
while ((r = scf_iter_next_pg(iter, pg)) == 1) {
if (scf_pg_get_flags(pg, &flags) != SCF_SUCCESS)
scfdie();
/* Skip nonpersistent pgs. */
if (flags & SCF_PG_FLAG_NONPERSISTENT)
continue;
if (scf_pg_delete(pg) != SCF_SUCCESS) {
if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
semerr(emsg_permission_denied);
goto out;
}
}
if (r == -1)
scfdie();
/* Copy the properties to the corresponding entity. */
if (scf_iter_snaplevel_pgs(iter, level) != SCF_SUCCESS)
scfdie();
while ((r = scf_iter_next_pg(iter, pg)) == 1) {
if (scf_pg_get_name(pg, nbuf, max_scf_name_len + 1) < 0)
scfdie();
if (scf_pg_get_type(pg, tbuf, max_scf_pg_type_len + 1) <
0)
scfdie();
if (scf_pg_get_flags(pg, &flags) != SCF_SUCCESS)
scfdie();
if (!isinst)
r = scf_service_add_pg(cur_svc, nbuf, tbuf,
flags, npg);
else
r = scf_instance_add_pg(cur_inst, nbuf, tbuf,
flags, npg);
if (r != SCF_SUCCESS) {
if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
semerr(emsg_permission_denied);
goto out;
}
if ((enabled == 0 || enabled == 1) &&
strcmp(nbuf, scf_pg_general) == 0)
r = pg_copy(pg, npg, enabled);
else
r = pg_copy(pg, npg, 2);
switch (r) {
case 0:
break;
case -1:
semerr(emsg_permission_denied);
goto out;
case -2:
semerr(gettext(
"Interrupted by another change.\n"));
goto out;
default:
abort();
}
}
if (r == -1)
scfdie();
/* Get next level. */
nlevel = scf_snaplevel_create(g_hndl);
if (nlevel == NULL)
scfdie();
if (scf_snaplevel_get_next_snaplevel(level, nlevel) !=
SCF_SUCCESS) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
scf_snaplevel_destroy(nlevel);
break;
}
scf_snaplevel_destroy(level);
level = nlevel;
}
if (snapname == NULL) {
lscf_selectsnap(NULL);
snap = NULL; /* cur_snap has been destroyed */
}
out:
free(tbuf);
free(nbuf);
scf_value_destroy(val);
scf_property_destroy(prop);
scf_pg_destroy(npg);
scf_pg_destroy(pg);
scf_iter_destroy(iter);
scf_snaplevel_destroy(level);
scf_snapshot_destroy(prev);
if (snap != cur_snap)
scf_snapshot_destroy(snap);
}
void
lscf_refresh(void)
{
ssize_t fmrilen;
size_t bufsz;
char *fmribuf;
int r;
lscf_prep_hndl();
if (cur_inst == NULL) {
semerr(gettext("Instance not selected.\n"));
return;
}
bufsz = max_scf_fmri_len + 1;
fmribuf = safe_malloc(bufsz);
fmrilen = scf_instance_to_fmri(cur_inst, fmribuf, bufsz);
if (fmrilen < 0) {
free(fmribuf);
if (scf_error() != SCF_ERROR_DELETED)
scfdie();
scf_instance_destroy(cur_inst);
cur_inst = NULL;
warn(emsg_deleted);
return;
}
assert(fmrilen < bufsz);
r = refresh_entity(0, cur_inst, fmribuf, NULL, NULL, NULL);
switch (r) {
case 0:
break;
case ECONNABORTED:
warn(gettext("Could not refresh %s "
"(repository connection broken).\n"), fmribuf);
break;
case ECANCELED:
warn(emsg_deleted);
break;
case EPERM:
warn(gettext("Could not refresh %s "
"(permission denied).\n"), fmribuf);
break;
case ENOSPC:
warn(gettext("Could not refresh %s "
"(repository server out of resources).\n"),
fmribuf);
break;
case EACCES:
default:
bad_error("refresh_entity", scf_error());
}
free(fmribuf);
}
/*
* describe [-v] [-t] [pg/prop]
*/
int
lscf_describe(uu_list_t *args, int hasargs)
{
int ret = 0;
size_t i;
int argc;
char **argv = NULL;
string_list_t *slp;
int do_verbose = 0;
int do_templates = 0;
char *pattern = NULL;
lscf_prep_hndl();
if (hasargs != 0) {
argc = uu_list_numnodes(args);
if (argc < 1)
goto usage;
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;
/*
* We start optind = 0 because our list of arguments
* starts at argv[0]
*/
optind = 0;
opterr = 0;
for (;;) {
ret = getopt(argc, argv, "vt");
if (ret == -1)
break;
switch (ret) {
case 'v':
do_verbose = 1;
break;
case 't':
do_templates = 1;
break;
case '?':
goto usage;
default:
bad_error("getopt", ret);
}
}
pattern = argv[optind];
}
if (cur_inst == NULL && cur_svc == NULL) {
semerr(emsg_entity_not_selected);
ret = -1;
goto out;
}
/*
* list_entity_tmpl(), listprop() and listtmpl() produce verbose
* output if their last parameter is set to 2. Less information is
* produced if the parameter is set to 1.
*/
if (pattern == NULL) {
if (do_verbose == 1)
list_entity_tmpl(2);
else
list_entity_tmpl(1);
}
if (do_templates == 0) {
if (do_verbose == 1)
listprop(pattern, 0, 2);
else
listprop(pattern, 0, 1);
} else {
if (do_verbose == 1)
listtmpl(pattern, 2);
else
listtmpl(pattern, 1);
}
ret = 0;
out:
if (argv != NULL)
free(argv);
return (ret);
usage:
ret = -2;
goto out;
}
#define PARAM_ACTIVE ((const char *) "active")
#define PARAM_INACTIVE ((const char *) "inactive")
#define PARAM_SMTP_TO ((const char *) "to")
/*
* tokenize()
* Breaks down the string according to the tokens passed.
* Caller is responsible for freeing array of pointers returned.
* Returns NULL on failure
*/
char **
tokenize(char *str, const char *sep)
{
char *token, *lasts;
char **buf;
int n = 0; /* number of elements */
int size = 8; /* size of the array (initial) */
buf = safe_malloc(size * sizeof (char *));
for (token = strtok_r(str, sep, &lasts); token != NULL;
token = strtok_r(NULL, sep, &lasts), ++n) {
if (n + 1 >= size) {
size *= 2;
if ((buf = realloc(buf, size * sizeof (char *))) ==
NULL) {
uu_die(gettext("Out of memory"));
}
}
buf[n] = token;
}
/* NULL terminate the pointer array */
buf[n] = NULL;
return (buf);
}
int32_t
check_tokens(char **p)
{
int32_t smf = 0;
int32_t fma = 0;
while (*p) {
int32_t t = string_to_tset(*p);
if (t == 0) {
if (is_fma_token(*p) == 0)
return (INVALID_TOKENS);
fma = 1; /* this token is an fma event */
} else {
smf |= t;
}
if (smf != 0 && fma == 1)
return (MIXED_TOKENS);
++p;
}
if (smf > 0)
return (smf);
else if (fma == 1)
return (FMA_TOKENS);
return (INVALID_TOKENS);
}
static int
get_selection_str(char *fmri, size_t sz)
{
if (g_hndl == NULL) {
semerr(emsg_entity_not_selected);
return (-1);
} else if (cur_level != NULL) {
semerr(emsg_invalid_for_snapshot);
return (-1);
} else {
lscf_get_selection_str(fmri, sz);
}
return (0);
}
void
lscf_delnotify(const char *set, int global)
{
char *str = strdup(set);
char **pgs;
char **p;
int32_t tset;
char *fmri = NULL;
if (str == NULL)
uu_die(gettext("Out of memory.\n"));
pgs = tokenize(str, ",");
if ((tset = check_tokens(pgs)) > 0) {
size_t sz = max_scf_fmri_len + 1;
fmri = safe_malloc(sz);
if (global) {
(void) strlcpy(fmri, SCF_INSTANCE_GLOBAL, sz);
} else if (get_selection_str(fmri, sz) != 0) {
goto out;
}
if (smf_notify_del_params(SCF_SVC_TRANSITION_CLASS, fmri,
tset) != SCF_SUCCESS) {
uu_warn(gettext("Failed smf_notify_del_params: %s\n"),
scf_strerror(scf_error()));
}
} else if (tset == FMA_TOKENS) {
if (global) {
semerr(gettext("Can't use option '-g' with FMA event "
"definitions\n"));
goto out;
}
for (p = pgs; *p; ++p) {
if (smf_notify_del_params(de_tag(*p), NULL, 0) !=
SCF_SUCCESS) {
uu_warn(gettext("Failed for \"%s\": %s\n"), *p,
scf_strerror(scf_error()));
goto out;
}
}
} else if (tset == MIXED_TOKENS) {
semerr(gettext("Can't mix SMF and FMA event definitions\n"));
goto out;
} else {
uu_die(gettext("Invalid input.\n"));
}
out:
free(fmri);
free(pgs);
free(str);
}
void
lscf_listnotify(const char *set, int global)
{
char *str = safe_strdup(set);
char **pgs;
char **p;
int32_t tset;
nvlist_t *nvl;
char *fmri = NULL;
if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
uu_die(gettext("Out of memory.\n"));
pgs = tokenize(str, ",");
if ((tset = check_tokens(pgs)) > 0) {
size_t sz = max_scf_fmri_len + 1;
fmri = safe_malloc(sz);
if (global) {
(void) strlcpy(fmri, SCF_INSTANCE_GLOBAL, sz);
} else if (get_selection_str(fmri, sz) != 0) {
goto out;
}
if (_scf_get_svc_notify_params(fmri, nvl, tset, 1, 1) !=
SCF_SUCCESS) {
if (scf_error() != SCF_ERROR_NOT_FOUND &&
scf_error() != SCF_ERROR_DELETED)
uu_warn(gettext(
"Failed listnotify: %s\n"),
scf_strerror(scf_error()));
goto out;
}
listnotify_print(nvl, NULL);
} else if (tset == FMA_TOKENS) {
if (global) {
semerr(gettext("Can't use option '-g' with FMA event "
"definitions\n"));
goto out;
}
for (p = pgs; *p; ++p) {
if (_scf_get_fma_notify_params(de_tag(*p), nvl, 1) !=
SCF_SUCCESS) {
/*
* if the preferences have just been deleted
* or does not exist, just skip.
*/
if (scf_error() == SCF_ERROR_NOT_FOUND ||
scf_error() == SCF_ERROR_DELETED)
continue;
uu_warn(gettext(
"Failed listnotify: %s\n"),
scf_strerror(scf_error()));
goto out;
}
listnotify_print(nvl, re_tag(*p));
}
} else if (tset == MIXED_TOKENS) {
semerr(gettext("Can't mix SMF and FMA event definitions\n"));
goto out;
} else {
semerr(gettext("Invalid input.\n"));
}
out:
nvlist_free(nvl);
free(fmri);
free(pgs);
free(str);
}
static char *
strip_quotes_and_blanks(char *s)
{
char *start = s;
char *end = strrchr(s, '\"');
if (s[0] == '\"' && end != NULL && *(end + 1) == '\0') {
start = s + 1;
while (isblank(*start))
start++;
while (isblank(*(end - 1)) && end > start) {
end--;
}
*end = '\0';
}
return (start);
}
static int
set_active(nvlist_t *mech, const char *hier_part)
{
boolean_t b;
if (*hier_part == '\0' || strcmp(hier_part, PARAM_ACTIVE) == 0) {
b = B_TRUE;
} else if (strcmp(hier_part, PARAM_INACTIVE) == 0) {
b = B_FALSE;
} else {
return (-1);
}
if (nvlist_add_boolean_value(mech, PARAM_ACTIVE, b) != 0)
uu_die(gettext("Out of memory.\n"));
return (0);
}
static int
add_snmp_params(nvlist_t *mech, char *hier_part)
{
return (set_active(mech, hier_part));
}
static int
add_syslog_params(nvlist_t *mech, char *hier_part)
{
return (set_active(mech, hier_part));
}
/*
* add_mailto_paramas()
* parse the hier_part of mailto URI
* mailto:<addr>[?<header1>=<value1>[&<header2>=<value2>]]
* or mailto:{[active]|inactive}
*/
static int
add_mailto_params(nvlist_t *mech, char *hier_part)
{
const char *tok = "?&";
char *p;
char *lasts;
char *param;
char *val;
/*
* If the notification parametes are in the form of
*
* malito:{[active]|inactive}
*
* we set the property accordingly and return.
* Otherwise, we make the notification type active and
* process the hier_part.
*/
if (set_active(mech, hier_part) == 0)
return (0);
else if (set_active(mech, PARAM_ACTIVE) != 0)
return (-1);
if ((p = strtok_r(hier_part, tok, &lasts)) == NULL) {
/*
* sanity check: we only get here if hier_part = "", but
* that's handled by set_active
*/
uu_die("strtok_r");
}
if (nvlist_add_string(mech, PARAM_SMTP_TO, p) != 0)
uu_die(gettext("Out of memory.\n"));
while ((p = strtok_r(NULL, tok, &lasts)) != NULL)
if ((param = strtok_r(p, "=", &val)) != NULL)
if (nvlist_add_string(mech, param, val) != 0)
uu_die(gettext("Out of memory.\n"));
return (0);
}
static int
uri_split(char *uri, char **scheme, char **hier_part)
{
int r = -1;
if ((*scheme = strtok_r(uri, ":", hier_part)) == NULL ||
*hier_part == NULL) {
semerr(gettext("'%s' is not an URI\n"), uri);
return (r);
}
if ((r = check_uri_scheme(*scheme)) < 0) {
semerr(gettext("Unkown URI scheme: %s\n"), *scheme);
return (r);
}
return (r);
}
static int
process_uri(nvlist_t *params, char *uri)
{
char *scheme;
char *hier_part;
nvlist_t *mech;
int index;
int r;
if ((index = uri_split(uri, &scheme, &hier_part)) < 0)
return (-1);
if (nvlist_alloc(&mech, NV_UNIQUE_NAME, 0) != 0)
uu_die(gettext("Out of memory.\n"));
switch (index) {
case 0:
/* error messages displayed by called function */
r = add_mailto_params(mech, hier_part);
break;
case 1:
if ((r = add_snmp_params(mech, hier_part)) != 0)
semerr(gettext("Not valid parameters: '%s'\n"),
hier_part);
break;
case 2:
if ((r = add_syslog_params(mech, hier_part)) != 0)
semerr(gettext("Not valid parameters: '%s'\n"),
hier_part);
break;
default:
r = -1;
}
if (r == 0 && nvlist_add_nvlist(params, uri_scheme[index].protocol,
mech) != 0)
uu_die(gettext("Out of memory.\n"));
nvlist_free(mech);
return (r);
}
static int
set_params(nvlist_t *params, char **p)
{
char *uri;
if (p == NULL)
/* sanity check */
uu_die("set_params");
while (*p) {
uri = strip_quotes_and_blanks(*p);
if (process_uri(params, uri) != 0)
return (-1);
++p;
}
return (0);
}
static int
setnotify(const char *e, char **p, int global)
{
char *str = safe_strdup(e);
char **events;
int32_t tset;
int r = -1;
nvlist_t *nvl, *params;
char *fmri = NULL;
if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
nvlist_alloc(&params, NV_UNIQUE_NAME, 0) != 0 ||
nvlist_add_uint32(nvl, SCF_NOTIFY_NAME_VERSION,
SCF_NOTIFY_PARAMS_VERSION) != 0)
uu_die(gettext("Out of memory.\n"));
events = tokenize(str, ",");
if ((tset = check_tokens(events)) > 0) {
/* SMF state transitions parameters */
size_t sz = max_scf_fmri_len + 1;
fmri = safe_malloc(sz);
if (global) {
(void) strlcpy(fmri, SCF_INSTANCE_GLOBAL, sz);
} else if (get_selection_str(fmri, sz) != 0) {
goto out;
}
if (nvlist_add_string(nvl, SCF_NOTIFY_NAME_FMRI, fmri) != 0 ||
nvlist_add_int32(nvl, SCF_NOTIFY_NAME_TSET, tset) != 0)
uu_die(gettext("Out of memory.\n"));
if ((r = set_params(params, p)) == 0) {
if (nvlist_add_nvlist(nvl, SCF_NOTIFY_PARAMS,
params) != 0)
uu_die(gettext("Out of memory.\n"));
if (smf_notify_set_params(SCF_SVC_TRANSITION_CLASS,
nvl) != SCF_SUCCESS) {
r = -1;
uu_warn(gettext(
"Failed smf_notify_set_params(3SCF): %s\n"),
scf_strerror(scf_error()));
}
}
} else if (tset == FMA_TOKENS) {
/* FMA event parameters */
if (global) {
semerr(gettext("Can't use option '-g' with FMA event "
"definitions\n"));
goto out;
}
if ((r = set_params(params, p)) != 0)
goto out;
if (nvlist_add_nvlist(nvl, SCF_NOTIFY_PARAMS, params) != 0)
uu_die(gettext("Out of memory.\n"));
while (*events) {
if (smf_notify_set_params(de_tag(*events), nvl) !=
SCF_SUCCESS)
uu_warn(gettext(
"Failed smf_notify_set_params(3SCF) for "
"event %s: %s\n"), *events,
scf_strerror(scf_error()));
events++;
}
} else if (tset == MIXED_TOKENS) {
semerr(gettext("Can't mix SMF and FMA event definitions\n"));
} else {
/* Sanity check */
uu_die(gettext("Invalid input.\n"));
}
out:
nvlist_free(nvl);
nvlist_free(params);
free(fmri);
free(str);
return (r);
}
int
lscf_setnotify(uu_list_t *args)
{
int argc;
char **argv = NULL;
string_list_t *slp;
int global;
char *events;
char **p;
int i;
int ret;
if ((argc = uu_list_numnodes(args)) < 2)
goto usage;
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;
if (strcmp(argv[0], "-g") == 0) {
global = 1;
events = argv[1];
p = argv + 2;
} else {
global = 0;
events = argv[0];
p = argv + 1;
}
ret = setnotify(events, p, global);
out:
free(argv);
return (ret);
usage:
ret = -2;
goto out;
}
/*
* Creates a list of instance name strings associated with a service. If
* wohandcrafted flag is set, get only instances that have a last-import
* snapshot, instances that were imported via svccfg.
*/
static uu_list_t *
create_instance_list(scf_service_t *svc, int wohandcrafted)
{
scf_snapshot_t *snap = NULL;
scf_instance_t *inst;
scf_iter_t *inst_iter;
uu_list_t *instances;
char *instname;
int r;
inst_iter = scf_iter_create(g_hndl);
inst = scf_instance_create(g_hndl);
if (inst_iter == NULL || inst == NULL) {
uu_warn(gettext("Could not create instance or iterator\n"));
scfdie();
}
if ((instances = uu_list_create(string_pool, NULL, 0)) == NULL)
return (instances);
if (scf_iter_service_instances(inst_iter, svc) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_DELETED:
uu_list_destroy(instances);
instances = NULL;
goto out;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_iter_service_instances", scf_error());
}
}
instname = safe_malloc(max_scf_name_len + 1);
while ((r = scf_iter_next_instance(inst_iter, inst)) != 0) {
if (r == -1) {
(void) uu_warn(gettext("Unable to iterate through "
"instances to create instance list : %s\n"),
scf_strerror(scf_error()));
uu_list_destroy(instances);
instances = NULL;
goto out;
}
/*
* If the instance does not have a last-import snapshot
* then do not add it to the list as it is a hand-crafted
* instance that should not be managed.
*/
if (wohandcrafted) {
if (snap == NULL &&
(snap = scf_snapshot_create(g_hndl)) == NULL) {
uu_warn(gettext("Unable to create snapshot "
"entity\n"));
scfdie();
}
if (scf_instance_get_snapshot(inst,
snap_lastimport, snap) != 0) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND :
case SCF_ERROR_DELETED:
continue;
case SCF_ERROR_CONNECTION_BROKEN:
uu_list_destroy(instances);
instances = NULL;
goto out;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_iter_service_instances",
scf_error());
}
}
}
if (scf_instance_get_name(inst, instname,
max_scf_name_len + 1) < 0) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND :
continue;
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_DELETED:
uu_list_destroy(instances);
instances = NULL;
goto out;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_iter_service_instances",
scf_error());
}
}
add_string(instances, instname);
}
out:
if (snap)
scf_snapshot_destroy(snap);
scf_instance_destroy(inst);
scf_iter_destroy(inst_iter);
free(instname);
return (instances);
}
/*
* disable an instance but wait for the instance to
* move out of the running state.
*
* Returns 0 : if the instance did not disable
* Returns non-zero : if the instance disabled.
*
*/
static int
disable_instance(scf_instance_t *instance)
{
char *fmribuf;
int enabled = 10000;
if (inst_is_running(instance)) {
fmribuf = safe_malloc(max_scf_name_len + 1);
if (scf_instance_to_fmri(instance, fmribuf,
max_scf_name_len + 1) < 0) {
free(fmribuf);
return (0);
}
/*
* If the instance cannot be disabled then return
* failure to disable and let the caller decide
* if that is of importance.
*/
if (smf_disable_instance(fmribuf, 0) != 0) {
free(fmribuf);
return (0);
}
while (enabled) {
if (!inst_is_running(instance))
break;
(void) poll(NULL, 0, 5);
enabled = enabled - 5;
}
free(fmribuf);
}
return (enabled);
}
/*
* Function to compare two service_manifest structures.
*/
/* ARGSUSED2 */
static int
service_manifest_compare(const void *left, const void *right, void *unused)
{
service_manifest_t *l = (service_manifest_t *)left;
service_manifest_t *r = (service_manifest_t *)right;
int rc;
rc = strcmp(l->servicename, r->servicename);
return (rc);
}
/*
* Look for the provided service in the service to manifest
* tree. If the service exists, and a manifest was provided
* then add the manifest to that service. If the service
* does not exist, then add the service and manifest to the
* list.
*
* If the manifest is NULL, return the element if found. If
* the service is not found return NULL.
*/
service_manifest_t *
find_add_svc_mfst(const char *svnbuf, const char *mfst)
{
service_manifest_t elem;
service_manifest_t *fnelem;
uu_avl_index_t marker;
elem.servicename = svnbuf;
fnelem = uu_avl_find(service_manifest_tree, &elem, NULL, &marker);
if (mfst) {
if (fnelem) {
add_string(fnelem->mfstlist, strdup(mfst));
} else {
fnelem = safe_malloc(sizeof (*fnelem));
fnelem->servicename = safe_strdup(svnbuf);
if ((fnelem->mfstlist =
uu_list_create(string_pool, NULL, 0)) == NULL)
uu_die(gettext("Could not create property "
"list: %s\n"), uu_strerror(uu_error()));
add_string(fnelem->mfstlist, safe_strdup(mfst));
uu_avl_insert(service_manifest_tree, fnelem, marker);
}
}
return (fnelem);
}
/*
* Create the service to manifest avl tree.
*
* Walk each of the manifests currently installed in the supported
* directories, /lib/svc/manifests and /var/svc/manifests. For
* each of the manifests, inventory the services and add them to
* the tree.
*
* Code that calls this function should make sure fileystem/minimal is online,
* /var is available, since this function walks the /var/svc/manifest directory.
*/
static void
create_manifest_tree(void)
{
manifest_info_t **entry;
manifest_info_t **manifests;
uu_list_walk_t *svcs;
bundle_t *b;
entity_t *mfsvc;
char *dirs[] = {LIBSVC_DIR, VARSVC_DIR, NULL};
int c, status;
if (service_manifest_pool)
return;
/*
* Create the list pool for the service manifest list
*/
service_manifest_pool = uu_avl_pool_create("service_manifest",
sizeof (service_manifest_t),
offsetof(service_manifest_t, svcmfst_node),
service_manifest_compare, UU_DEFAULT);
if (service_manifest_pool == NULL)
uu_die(gettext("service_manifest pool creation failed: %s\n"),
uu_strerror(uu_error()));
/*
* Create the list
*/
service_manifest_tree = uu_avl_create(service_manifest_pool, NULL,
UU_DEFAULT);
if (service_manifest_tree == NULL)
uu_die(gettext("service_manifest tree creation failed: %s\n"),
uu_strerror(uu_error()));
/*
* Walk the manifests adding the service(s) from each manifest.
*
* If a service already exists add the manifest to the manifest
* list for that service. This covers the case of a service that
* is supported by multiple manifest files.
*/
for (c = 0; dirs[c]; c++) {
status = find_manifests(g_hndl, dirs[c], &manifests, CHECKEXT);
if (status < 0) {
uu_warn(gettext("file tree walk of %s encountered "
"error %s\n"), dirs[c], strerror(errno));
uu_avl_destroy(service_manifest_tree);
service_manifest_tree = NULL;
return;
}
/*
* If a manifest that was in the list is not found
* then skip and go to the next manifest file.
*/
if (manifests != NULL) {
for (entry = manifests; *entry != NULL; entry++) {
b = internal_bundle_new();
if (lxml_get_bundle_file(b, (*entry)->mi_path,
SVCCFG_OP_IMPORT) != 0) {
internal_bundle_free(b);
continue;
}
svcs = uu_list_walk_start(b->sc_bundle_services,
0);
if (svcs == NULL) {
internal_bundle_free(b);
continue;
}
while ((mfsvc = uu_list_walk_next(svcs)) !=
NULL) {
/* Add manifest to service */
(void) find_add_svc_mfst(mfsvc->sc_name,
(*entry)->mi_path);
}
uu_list_walk_end(svcs);
internal_bundle_free(b);
}
free_manifest_array(manifests);
}
}
}
/*
* Check the manifest history file to see
* if the service was ever installed from
* one of the supported directories.
*
* Return Values :
* -1 - if there's error reading manifest history file
* 1 - if the service is not found
* 0 - if the service is found
*/
static int
check_mfst_history(const char *svcname)
{
struct stat st;
caddr_t mfsthist_start;
char *svnbuf;
int fd;
int r = 1;
fd = open(MFSTHISTFILE, O_RDONLY);
if (fd == -1) {
uu_warn(gettext("Unable to open the history file\n"));
return (-1);
}
if (fstat(fd, &st) == -1) {
uu_warn(gettext("Unable to stat the history file\n"));
return (-1);
}
mfsthist_start = mmap(0, st.st_size, PROT_READ,
MAP_PRIVATE, fd, 0);
(void) close(fd);
if (mfsthist_start == MAP_FAILED ||
*(mfsthist_start + st.st_size) != '\0') {
(void) munmap(mfsthist_start, st.st_size);
return (-1);
}
/*
* The manifest history file is a space delimited list
* of service and instance to manifest linkage. Adding
* a space to the end of the service name so to get only
* the service that is being searched for.
*/
svnbuf = uu_msprintf("%s ", svcname);
if (svnbuf == NULL)
uu_die(gettext("Out of memory"));
if (strstr(mfsthist_start, svnbuf) != NULL)
r = 0;
(void) munmap(mfsthist_start, st.st_size);
uu_free(svnbuf);
return (r);
}
/*
* Take down each of the instances in the service
* and remove them, then delete the service.
*/
static void
teardown_service(scf_service_t *svc, const char *svnbuf)
{
scf_instance_t *instance;
scf_iter_t *iter;
int r;
safe_printf(gettext("Delete service %s as there are no "
"supporting manifests\n"), svnbuf);
instance = scf_instance_create(g_hndl);
iter = scf_iter_create(g_hndl);
if (iter == NULL || instance == NULL) {
uu_warn(gettext("Unable to create supporting entities to "
"teardown the service\n"));
uu_warn(gettext("scf error is : %s\n"),
scf_strerror(scf_error()));
scfdie();
}
if (scf_iter_service_instances(iter, svc) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_DELETED:
goto out;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_iter_service_instances",
scf_error());
}
}
while ((r = scf_iter_next_instance(iter, instance)) != 0) {
if (r == -1) {
uu_warn(gettext("Error - %s\n"),
scf_strerror(scf_error()));
goto out;
}
(void) disable_instance(instance);
}
/*
* Delete the service... forcing the deletion in case
* any of the instances did not disable.
*/
(void) lscf_service_delete(svc, 1);
out:
scf_instance_destroy(instance);
scf_iter_destroy(iter);
}
/*
* Get the list of instances supported by the manifest
* file.
*
* Return 0 if there are no instances.
*
* Return -1 if there are errors attempting to collect instances.
*
* Return the count of instances found if there are no errors.
*
*/
static int
check_instance_support(char *mfstfile, const char *svcname,
uu_list_t *instances)
{
uu_list_walk_t *svcs, *insts;
uu_list_t *ilist;
bundle_t *b;
entity_t *mfsvc, *mfinst;
const char *svcn;
int rminstcnt = 0;
b = internal_bundle_new();
if (lxml_get_bundle_file(b, mfstfile, SVCCFG_OP_IMPORT) != 0) {
/*
* Unable to process the manifest file for
* instance support, so just return as
* don't want to remove instances that could
* not be accounted for that might exist here.
*/
internal_bundle_free(b);
return (0);
}
svcs = uu_list_walk_start(b->sc_bundle_services, 0);
if (svcs == NULL) {
internal_bundle_free(b);
return (0);
}
svcn = svcname + (sizeof (SCF_FMRI_SVC_PREFIX) - 1) +
(sizeof (SCF_FMRI_SERVICE_PREFIX) - 1);
while ((mfsvc = uu_list_walk_next(svcs)) != NULL) {
if (strcmp(mfsvc->sc_name, svcn) == 0)
break;
}
uu_list_walk_end(svcs);
if (mfsvc == NULL) {
internal_bundle_free(b);
return (-1);
}
ilist = mfsvc->sc_u.sc_service.sc_service_instances;
if ((insts = uu_list_walk_start(ilist, 0)) == NULL) {
internal_bundle_free(b);
return (0);
}
while ((mfinst = uu_list_walk_next(insts)) != NULL) {
/*
* Remove the instance from the instances list.
* The unaccounted for instances will be removed
* from the service once all manifests are
* processed.
*/
(void) remove_string(instances,
mfinst->sc_name);
rminstcnt++;
}
uu_list_walk_end(insts);
internal_bundle_free(b);
return (rminstcnt);
}
/*
* For the given service, set its SCF_PG_MANIFESTFILES/SUPPORT property to
* 'false' to indicate there's no manifest file(s) found for the service.
*/
static void
svc_add_no_support(scf_service_t *svc)
{
char *pname;
/* Add no support */
cur_svc = svc;
if (addpg(SCF_PG_MANIFESTFILES, SCF_GROUP_FRAMEWORK))
return;
pname = uu_msprintf("%s/%s", SCF_PG_MANIFESTFILES, SUPPORTPROP);
if (pname == NULL)
uu_die(gettext("Out of memory.\n"));
(void) lscf_addpropvalue(pname, "boolean:", "0");
uu_free(pname);
cur_svc = NULL;
}
/*
* This function handles all upgrade scenarios for a service that doesn't have
* SCF_PG_MANIFESTFILES pg. The function creates and populates
* SCF_PG_MANIFESTFILES pg for the given service to keep track of service to
* manifest(s) mapping. Manifests under supported directories are inventoried
* and a property is added for each file that delivers configuration to the
* service. A service that has no corresponding manifest files (deleted) are
* removed from repository.
*
* Unsupported services:
*
* A service is considered unsupported if there is no corresponding manifest
* in the supported directories for that service and the service isn't in the
* history file list. The history file, MFSTHISTFILE, contains a list of all
* services and instances that were delivered by Solaris before the introduction
* of the SCF_PG_MANIFESTFILES property group. The history file also contains
* the path to the manifest file that defined the service or instance.
*
* Another type of unsupported services is 'handcrafted' services,
* programmatically created services or services created by dependent entries
* in other manifests. A handcrafted service is identified by its lack of any
* instance containing last-import snapshot which is created during svccfg
* import.
*
* This function sets a flag for unsupported services by setting services'
* SCF_PG_MANIFESTFILES/support property to false.
*/
static void
upgrade_svc_mfst_connection(scf_service_t *svc, const char *svcname)
{
service_manifest_t *elem;
uu_list_walk_t *mfwalk;
string_list_t *mfile;
uu_list_t *instances;
const char *sname;
char *pname;
int r;
/*
* Since there's no guarantee manifests under /var are available during
* early import, don't perform any upgrade during early import.
*/
if (IGNORE_VAR)
return;
if (service_manifest_tree == NULL) {
create_manifest_tree();
}
/*
* Find service's supporting manifest(s) after
* stripping off the svc:/ prefix that is part
* of the fmri that is not used in the service
* manifest bundle list.
*/
sname = svcname + strlen(SCF_FMRI_SVC_PREFIX) +
strlen(SCF_FMRI_SERVICE_PREFIX);
elem = find_add_svc_mfst(sname, NULL);
if (elem == NULL) {
/*
* A handcrafted service, one that has no instance containing
* last-import snapshot, should get unsupported flag.
*/
instances = create_instance_list(svc, 1);
if (instances == NULL) {
uu_warn(gettext("Unable to create instance list %s\n"),
svcname);
return;
}
if (uu_list_numnodes(instances) == 0) {
svc_add_no_support(svc);
return;
}
/*
* If the service is in the history file, and its supporting
* manifests are not found, we can safely delete the service
* because its manifests are removed from the system.
*
* Services not found in the history file are not delivered by
* Solaris and/or delivered outside supported directories, set
* unsupported flag for these services.
*/
r = check_mfst_history(svcname);
if (r == -1)
return;
if (r) {
/* Set unsupported flag for service */
svc_add_no_support(svc);
} else {
/* Delete the service */
teardown_service(svc, svcname);
}
return;
}
/*
* Walk through the list of manifests and add them
* to the service.
*
* Create a manifestfiles pg and add the property.
*/
mfwalk = uu_list_walk_start(elem->mfstlist, 0);
if (mfwalk == NULL)
return;
cur_svc = svc;
r = addpg(SCF_PG_MANIFESTFILES, SCF_GROUP_FRAMEWORK);
if (r != 0) {
cur_svc = NULL;
return;
}
while ((mfile = uu_list_walk_next(mfwalk)) != NULL) {
pname = uu_msprintf("%s/%s", SCF_PG_MANIFESTFILES,
mhash_filename_to_propname(mfile->str, 0));
if (pname == NULL)
uu_die(gettext("Out of memory.\n"));
(void) lscf_addpropvalue(pname, "astring:", mfile->str);
uu_free(pname);
}
uu_list_walk_end(mfwalk);
cur_svc = NULL;
}
/*
* Take a service and process the manifest file entires to see if
* there is continued support for the service and instances. If
* not cleanup as appropriate.
*
* If a service does not have a manifest files entry flag it for
* upgrade and return.
*
* For each manifestfiles property check if the manifest file is
* under the supported /lib/svc/manifest or /var/svc/manifest path
* and if not then return immediately as this service is not supported
* by the cleanup mechanism and should be ignored.
*
* For each manifest file that is supported, check to see if the
* file exists. If not then remove the manifest file property
* from the service and the smf/manifest hash table. If the manifest
* file exists then verify that it supports the instances that are
* part of the service.
*
* Once all manifest files have been accounted for remove any instances
* that are no longer supported in the service.
*
* Return values :
* 0 - Successfully processed the service
* non-zero - failed to process the service
*
* On most errors, will just return to wait and get the next service,
* unless in case of unable to create the needed structures which is
* most likely a fatal error that is not going to be recoverable.
*/
int
lscf_service_cleanup(void *act, scf_walkinfo_t *wip)
{
struct mpg_mfile *mpntov;
struct mpg_mfile **mpvarry = NULL;
scf_service_t *svc;
scf_propertygroup_t *mpg;
scf_property_t *mp;
scf_value_t *mv;
scf_iter_t *mi;
scf_instance_t *instance;
uu_list_walk_t *insts;
uu_list_t *instances = NULL;
boolean_t activity = (boolean_t)act;
char *mpnbuf;
char *mpvbuf;
char *pgpropbuf;
int mfstcnt, rminstct, instct, mfstmax;
int index;
int r = 0;
assert(g_hndl != NULL);
assert(wip->svc != NULL);
assert(wip->fmri != NULL);
svc = wip->svc;
mpg = scf_pg_create(g_hndl);
mp = scf_property_create(g_hndl);
mi = scf_iter_create(g_hndl);
mv = scf_value_create(g_hndl);
instance = scf_instance_create(g_hndl);
if (mpg == NULL || mp == NULL || mi == NULL || mv == NULL ||
instance == NULL) {
uu_warn(gettext("Unable to create the supporting entities\n"));
uu_warn(gettext("scf error is : %s\n"),
scf_strerror(scf_error()));
scfdie();
}
/*
* Get the manifestfiles property group to be parsed for
* files existence.
*/
if (scf_service_get_pg(svc, SCF_PG_MANIFESTFILES, mpg) != SCF_SUCCESS) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
upgrade_svc_mfst_connection(svc, wip->fmri);
break;
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
goto out;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_iter_pg_properties",
scf_error());
}
goto out;
}
/*
* Iterate through each of the manifestfiles properties
* to determine what manifestfiles are available.
*
* If a manifest file is supported then increment the
* count and therefore the service is safe.
*/
if (scf_iter_pg_properties(mi, mpg) != 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
goto out;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_iter_pg_properties",
scf_error());
}
}
mfstcnt = 0;
mfstmax = MFSTFILE_MAX;
mpvarry = safe_malloc(sizeof (struct mpg_file *) * MFSTFILE_MAX);
while ((r = scf_iter_next_property(mi, mp)) != 0) {
if (r == -1)
bad_error(gettext("Unable to iterate through "
"manifestfiles properties : %s"),
scf_error());
mpntov = safe_malloc(sizeof (struct mpg_mfile));
mpnbuf = safe_malloc(max_scf_name_len + 1);
mpvbuf = safe_malloc(max_scf_value_len + 1);
mpntov->mpg = mpnbuf;
mpntov->mfile = mpvbuf;
mpntov->access = 1;
if (scf_property_get_name(mp, mpnbuf,
max_scf_name_len + 1) < 0) {
uu_warn(gettext("Unable to get manifest file "
"property : %s\n"),
scf_strerror(scf_error()));
switch (scf_error()) {
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
r = scferror2errno(scf_error());
goto out_free;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_iter_pg_properties",
scf_error());
}
}
/*
* The support property is a boolean value that indicates
* if the service is supported for manifest file deletion.
* Currently at this time there is no code that sets this
* value to true. So while we could just let this be caught
* by the support check below, in the future this by be set
* to true and require processing. So for that, go ahead
* and check here, and just return if false. Otherwise,
* fall through expecting that other support checks will
* handle the entries.
*/
if (strcmp(mpnbuf, SUPPORTPROP) == 0) {
uint8_t support;
if (scf_property_get_value(mp, mv) != 0 ||
scf_value_get_boolean(mv, &support) != 0) {
uu_warn(gettext("Unable to get the manifest "
"support value: %s\n"),
scf_strerror(scf_error()));
switch (scf_error()) {
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
r = scferror2errno(scf_error());
goto out_free;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_iter_pg_properties",
scf_error());
}
}
if (support == B_FALSE)
goto out_free;
}
/*
* Anything with a manifest outside of the supported
* directories, immediately bail out because that makes
* this service non-supported. We don't even want
* to do instance processing in this case because the
* instances could be part of the non-supported manifest.
*/
if (strncmp(mpnbuf, LIBSVC_PR, strlen(LIBSVC_PR)) != 0) {
/*
* Manifest is not in /lib/svc, so we need to
* consider the /var/svc case.
*/
if (strncmp(mpnbuf, VARSVC_PR,
strlen(VARSVC_PR)) != 0 || IGNORE_VAR) {
/*
* Either the manifest is not in /var/svc or
* /var is not yet mounted. We ignore the
* manifest either because it is not in a
* standard location or because we cannot
* currently access the manifest.
*/
goto out_free;
}
}
/*
* Get the value to of the manifest file for this entry
* for access verification and instance support
* verification if it still exists.
*
* During Early Manifest Import if the manifest is in
* /var/svc then it may not yet be available for checking
* so we must determine if /var/svc is available. If not
* then defer until Late Manifest Import to cleanup.
*/
if (scf_property_get_value(mp, mv) != 0) {
uu_warn(gettext("Unable to get the manifest file "
"value: %s\n"),
scf_strerror(scf_error()));
switch (scf_error()) {
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
r = scferror2errno(scf_error());
goto out_free;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_property_get_value",
scf_error());
}
}
if (scf_value_get_astring(mv, mpvbuf,
max_scf_value_len + 1) < 0) {
uu_warn(gettext("Unable to get the manifest "
"file : %s\n"),
scf_strerror(scf_error()));
switch (scf_error()) {
case SCF_ERROR_DELETED:
case SCF_ERROR_CONNECTION_BROKEN:
r = scferror2errno(scf_error());
goto out_free;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_value_get_astring",
scf_error());
}
}
mpvarry[mfstcnt] = mpntov;
mfstcnt++;
/*
* Check for the need to reallocate array
*/
if (mfstcnt >= (mfstmax - 1)) {
struct mpg_mfile **newmpvarry;
mfstmax = mfstmax * 2;
newmpvarry = realloc(mpvarry,
sizeof (struct mpg_mfile *) * mfstmax);
if (newmpvarry == NULL)
goto out_free;
mpvarry = newmpvarry;
}
mpvarry[mfstcnt] = NULL;
}
for (index = 0; mpvarry[index]; index++) {
mpntov = mpvarry[index];
/*
* Check to see if the manifestfile is accessable, if so hand
* this service and manifestfile off to be processed for
* instance support.
*/
mpnbuf = mpntov->mpg;
mpvbuf = mpntov->mfile;
if (access(mpvbuf, F_OK) != 0) {
mpntov->access = 0;
activity++;
mfstcnt--;
/* Remove the entry from the service */
cur_svc = svc;
pgpropbuf = uu_msprintf("%s/%s", SCF_PG_MANIFESTFILES,
mpnbuf);
if (pgpropbuf == NULL)
uu_die(gettext("Out of memory.\n"));
lscf_delprop(pgpropbuf);
cur_svc = NULL;
uu_free(pgpropbuf);
}
}
/*
* If mfstcnt is 0, none of the manifests that supported the service
* existed so remove the service.
*/
if (mfstcnt == 0) {
teardown_service(svc, wip->fmri);
goto out_free;
}
if (activity) {
int nosvcsupport = 0;
/*
* If the list of service instances is NULL then
* create the list.
*/
instances = create_instance_list(svc, 1);
if (instances == NULL) {
uu_warn(gettext("Unable to create instance list %s\n"),
wip->fmri);
goto out_free;
}
rminstct = uu_list_numnodes(instances);
instct = rminstct;
for (index = 0; mpvarry[index]; index++) {
mpntov = mpvarry[index];
if (mpntov->access == 0)
continue;
mpnbuf = mpntov->mpg;
mpvbuf = mpntov->mfile;
r = check_instance_support(mpvbuf, wip->fmri,
instances);
if (r == -1) {
nosvcsupport++;
} else {
rminstct -= r;
}
}
if (instct && instct == rminstct && nosvcsupport == mfstcnt) {
teardown_service(svc, wip->fmri);
goto out_free;
}
}
/*
* If there are instances left on the instance list, then
* we must remove them.
*/
if (instances != NULL && uu_list_numnodes(instances)) {
string_list_t *sp;
insts = uu_list_walk_start(instances, 0);
while ((sp = uu_list_walk_next(insts)) != NULL) {
/*
* Remove the instance from the instances list.
*/
safe_printf(gettext("Delete instance %s from "
"service %s\n"), sp->str, wip->fmri);
if (scf_service_get_instance(svc, sp->str,
instance) != SCF_SUCCESS) {
(void) uu_warn("scf_error - %s\n",
scf_strerror(scf_error()));
continue;
}
(void) disable_instance(instance);
(void) lscf_instance_delete(instance, 1);
}
scf_instance_destroy(instance);
uu_list_walk_end(insts);
}
out_free:
if (mpvarry) {
struct mpg_mfile *fmpntov;
for (index = 0; mpvarry[index]; index++) {
fmpntov = mpvarry[index];
if (fmpntov->mpg == mpnbuf)
mpnbuf = NULL;
free(fmpntov->mpg);
if (fmpntov->mfile == mpvbuf)
mpvbuf = NULL;
free(fmpntov->mfile);
if (fmpntov == mpntov)
mpntov = NULL;
free(fmpntov);
}
if (mpnbuf)
free(mpnbuf);
if (mpvbuf)
free(mpvbuf);
if (mpntov)
free(mpntov);
free(mpvarry);
}
out:
scf_pg_destroy(mpg);
scf_property_destroy(mp);
scf_iter_destroy(mi);
scf_value_destroy(mv);
return (0);
}
/*
* Take the service and search for the manifestfiles property
* in each of the property groups. If the manifest file
* associated with the property does not exist then remove
* the property group.
*/
int
lscf_hash_cleanup()
{
scf_service_t *svc;
scf_scope_t *scope;
scf_propertygroup_t *pg;
scf_property_t *prop;
scf_value_t *val;
scf_iter_t *iter;
char *pgname = NULL;
char *mfile = NULL;
int r;
svc = scf_service_create(g_hndl);
scope = scf_scope_create(g_hndl);
pg = scf_pg_create(g_hndl);
prop = scf_property_create(g_hndl);
val = scf_value_create(g_hndl);
iter = scf_iter_create(g_hndl);
if (pg == NULL || prop == NULL || val == NULL || iter == NULL ||
svc == NULL || scope == NULL) {
uu_warn(gettext("Unable to create a property group, or "
"property\n"));
uu_warn("%s\n", pg == NULL ? "pg is NULL" :
"pg is not NULL");
uu_warn("%s\n", prop == NULL ? "prop is NULL" :
"prop is not NULL");
uu_warn("%s\n", val == NULL ? "val is NULL" :
"val is not NULL");
uu_warn("%s\n", iter == NULL ? "iter is NULL" :
"iter is not NULL");
uu_warn("%s\n", svc == NULL ? "svc is NULL" :
"svc is not NULL");
uu_warn("%s\n", scope == NULL ? "scope is NULL" :
"scope is not NULL");
uu_warn(gettext("scf error is : %s\n"),
scf_strerror(scf_error()));
scfdie();
}
if (scf_handle_get_scope(g_hndl, SCF_SCOPE_LOCAL, scope) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_NOT_FOUND:
goto out;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_INVALID_ARGUMENT:
default:
bad_error("scf_handle_get_scope", scf_error());
}
}
if (scf_scope_get_service(scope, HASH_SVC, svc) != 0) {
uu_warn(gettext("Unable to process the hash service, %s\n"),
HASH_SVC);
goto out;
}
pgname = safe_malloc(max_scf_name_len + 1);
mfile = safe_malloc(max_scf_value_len + 1);
if (scf_iter_service_pgs(iter, svc) != SCF_SUCCESS) {
uu_warn(gettext("Unable to cleanup smf hash table : %s\n"),
scf_strerror(scf_error()));
goto out;
}
while ((r = scf_iter_next_pg(iter, pg)) != 0) {
if (r == -1)
goto out;
if (scf_pg_get_name(pg, pgname, max_scf_name_len + 1) < 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
return (ENODEV);
case SCF_ERROR_CONNECTION_BROKEN:
return (ECONNABORTED);
case SCF_ERROR_NOT_SET:
case SCF_ERROR_NOT_BOUND:
default:
bad_error("scf_pg_get_name", scf_error());
}
}
if (IGNORE_VAR) {
if (strncmp(pgname, VARSVC_PR, strlen(VARSVC_PR)) == 0)
continue;
}
/*
* If unable to get the property continue as this is an
* entry that has no location to check against.
*/
if (scf_pg_get_property(pg, MFSTFILEPR, prop) != SCF_SUCCESS) {
continue;
}
if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
uu_warn(gettext("Unable to get value from %s\n"),
pgname);
switch (scf_error()) {
case SCF_ERROR_DELETED:
case SCF_ERROR_CONSTRAINT_VIOLATED:
case SCF_ERROR_NOT_FOUND:
case SCF_ERROR_NOT_SET:
continue;
case SCF_ERROR_CONNECTION_BROKEN:
r = scferror2errno(scf_error());
goto out;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
default:
bad_error("scf_property_get_value",
scf_error());
}
}
if (scf_value_get_astring(val, mfile, max_scf_value_len + 1)
== -1) {
uu_warn(gettext("Unable to get astring from %s : %s\n"),
pgname, scf_strerror(scf_error()));
switch (scf_error()) {
case SCF_ERROR_NOT_SET:
case SCF_ERROR_TYPE_MISMATCH:
continue;
default:
bad_error("scf_value_get_astring", scf_error());
}
}
if (access(mfile, F_OK) == 0)
continue;
(void) scf_pg_delete(pg);
}
out:
scf_scope_destroy(scope);
scf_service_destroy(svc);
scf_pg_destroy(pg);
scf_property_destroy(prop);
scf_value_destroy(val);
scf_iter_destroy(iter);
free(pgname);
free(mfile);
return (0);
}
#ifndef NATIVE_BUILD
/* ARGSUSED */
CPL_MATCH_FN(complete_select)
{
const char *arg0, *arg1, *arg1end;
int word_start, err = 0, r;
size_t len;
char *buf;
lscf_prep_hndl();
arg0 = line + strspn(line, " \t");
assert(strncmp(arg0, "select", sizeof ("select") - 1) == 0);
arg1 = arg0 + sizeof ("select") - 1;
arg1 += strspn(arg1, " \t");
word_start = arg1 - line;
arg1end = arg1 + strcspn(arg1, " \t");
if (arg1end < line + word_end)
return (0);
len = line + word_end - arg1;
buf = safe_malloc(max_scf_name_len + 1);
if (cur_snap != NULL) {
return (0);
} else if (cur_inst != NULL) {
return (0);
} else if (cur_svc != NULL) {
scf_instance_t *inst;
scf_iter_t *iter;
if ((inst = scf_instance_create(g_hndl)) == NULL ||
(iter = scf_iter_create(g_hndl)) == NULL)
scfdie();
if (scf_iter_service_instances(iter, cur_svc) != 0)
scfdie();
for (;;) {
r = scf_iter_next_instance(iter, inst);
if (r == 0)
break;
if (r != 1)
scfdie();
if (scf_instance_get_name(inst, buf,
max_scf_name_len + 1) < 0)
scfdie();
if (strncmp(buf, arg1, len) == 0) {
err = cpl_add_completion(cpl, line, word_start,
word_end, buf + len, "", " ");
if (err != 0)
break;
}
}
scf_iter_destroy(iter);
scf_instance_destroy(inst);
return (err);
} else {
scf_service_t *svc;
scf_iter_t *iter;
assert(cur_scope != NULL);
if ((svc = scf_service_create(g_hndl)) == NULL ||
(iter = scf_iter_create(g_hndl)) == NULL)
scfdie();
if (scf_iter_scope_services(iter, cur_scope) != 0)
scfdie();
for (;;) {
r = scf_iter_next_service(iter, svc);
if (r == 0)
break;
if (r != 1)
scfdie();
if (scf_service_get_name(svc, buf,
max_scf_name_len + 1) < 0)
scfdie();
if (strncmp(buf, arg1, len) == 0) {
err = cpl_add_completion(cpl, line, word_start,
word_end, buf + len, "", " ");
if (err != 0)
break;
}
}
scf_iter_destroy(iter);
scf_service_destroy(svc);
return (err);
}
}
/* ARGSUSED */
CPL_MATCH_FN(complete_command)
{
uint32_t scope = 0;
if (cur_snap != NULL)
scope = CS_SNAP;
else if (cur_inst != NULL)
scope = CS_INST;
else if (cur_svc != NULL)
scope = CS_SVC;
else
scope = CS_SCOPE;
return (scope ? add_cmd_matches(cpl, line, word_end, scope) : 0);
}
#endif /* NATIVE_BUILD */