blob: 36d77df6c88d7de195dc57bf89cd8f5bd9a49d86 [file] [log] [blame]
/*
* 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.
* Copyright 2017 RackTop Systems.
* Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
*/
#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