summaryrefslogtreecommitdiff
path: root/usr/src/cmd/svc/startd/contract.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/svc/startd/contract.c')
-rw-r--r--usr/src/cmd/svc/startd/contract.c373
1 files changed, 373 insertions, 0 deletions
diff --git a/usr/src/cmd/svc/startd/contract.c b/usr/src/cmd/svc/startd/contract.c
new file mode 100644
index 0000000000..d9eca7631a
--- /dev/null
+++ b/usr/src/cmd/svc/startd/contract.c
@@ -0,0 +1,373 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef _FILE_OFFSET_BITS
+#undef _FILE_OFFSET_BITS
+#endif /* _FILE_OFFSET_BITS */
+
+#include <sys/contract/process.h>
+#include <sys/ctfs.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libcontract.h>
+#include <libcontract_priv.h>
+#include <libuutil.h>
+#include <limits.h>
+#include <procfs.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "startd.h"
+
+void
+contract_abandon(ctid_t ctid)
+{
+ int err;
+
+ assert(ctid != 0);
+
+ err = contract_abandon_id(ctid);
+
+ if (err)
+ log_framework(LOG_NOTICE,
+ "failed to abandon contract %ld: %s\n", ctid,
+ strerror(err));
+}
+
+int
+contract_kill(ctid_t ctid, int sig, const char *fmri)
+{
+ if (sigsend(P_CTID, ctid, sig) == -1 && errno != ESRCH) {
+ log_error(LOG_WARNING,
+ "%s: Could not signal all contract members: %s\n", fmri,
+ strerror(errno));
+ return (-1);
+ }
+
+ return (0);
+}
+
+ctid_t
+contract_init()
+{
+ int psfd, csfd;
+ ctid_t ctid, configd_ctid = -1;
+ psinfo_t psi;
+ ct_stathdl_t s;
+ ctid_t *ctids;
+ uint_t nctids;
+ uint_t n;
+ int err;
+
+ /*
+ * 2. Acquire any contracts we should have inherited. First, find the
+ * contract we belong to, then get its status.
+ */
+ if ((psfd = open("/proc/self/psinfo", O_RDONLY)) < 0) {
+ log_error(LOG_WARNING, "Can not open /proc/self/psinfo; unable "
+ "to check to adopt contracts: %s\n", strerror(errno));
+ return (-1);
+ }
+
+ if (read(psfd, &psi, sizeof (psinfo_t)) != sizeof (psinfo_t)) {
+ log_error(LOG_WARNING, "Can not read from /proc/self/psinfo; "
+ "unable to adopt contracts: %s\n",
+ strerror(errno));
+ startd_close(psfd);
+ return (-1);
+ }
+
+ ctid = psi.pr_contract;
+
+ startd_close(psfd);
+
+ if ((csfd = contract_open(ctid, "process", "status", O_RDONLY)) < 0) {
+ log_error(LOG_WARNING, "Can not open containing contract "
+ "status; unable to adopt contracts: %s\n", strerror(errno));
+ return (-1);
+ }
+
+ /* 3. Go about adopting our member list. */
+
+ err = ct_status_read(csfd, CTD_ALL, &s);
+ startd_close(csfd);
+ if (err) {
+ log_error(LOG_WARNING, "Can not read containing contract "
+ "status; unable to adopt: %s\n", strerror(err));
+ return (-1);
+ }
+
+ if (err = ct_pr_status_get_contracts(s, &ctids, &nctids)) {
+ log_error(LOG_WARNING, "Can not get my inherited contracts; "
+ "unable to adopt: %s\n", strerror(err));
+ ct_status_free(s);
+ return (-1);
+ }
+
+ if (nctids == 0) {
+ /*
+ * We're booting, as a svc.startd which managed to fork a
+ * child will always have a svc.configd contract to adopt.
+ */
+ st->st_initial = 1;
+ ct_status_free(s);
+ return (-1);
+ }
+
+ /*
+ * We're restarting after an interruption of some kind.
+ */
+ log_framework(LOG_NOTICE, "restarting after interruption\n");
+ st->st_initial = 0;
+
+ /*
+ * 3'. Loop through the array, adopting them all where possible, and
+ * noting which one contains svc.configd (via a cookie vlaue of
+ * CONFIGD_COOKIE).
+ */
+ for (n = 0; n < nctids; n++) {
+ int ccfd;
+ ct_stathdl_t cs;
+
+ if ((ccfd = contract_open(ctids[n], "process", "ctl",
+ O_WRONLY)) < 0) {
+ log_error(LOG_WARNING, "Can not open contract %ld ctl "
+ "for adoption: %s\n", ctids[n], strerror(err));
+
+ continue;
+ }
+
+ if ((csfd = contract_open(ctids[n], "process", "status",
+ O_RDONLY)) < 0) {
+ log_error(LOG_WARNING, "Can not open contract %ld "
+ "status for cookie: %s\n", ctids[n], strerror(err));
+ startd_close(ccfd);
+
+ continue;
+ }
+
+ if (err = ct_ctl_adopt(ccfd)) {
+ log_error(LOG_WARNING, "Can not adopt contract %ld: "
+ "%s\n", ctids[n], strerror(err));
+ startd_close(ccfd);
+ startd_close(csfd);
+
+ continue;
+ }
+
+ startd_close(ccfd);
+
+ if (err = ct_status_read(csfd, CTD_COMMON, &cs)) {
+ log_error(LOG_WARNING, "Can not read contract %ld"
+ "status; unable to fetch cookie: %s\n", ctids[n],
+ strerror(err));
+
+ ct_status_free(cs);
+ startd_close(csfd);
+
+ continue;
+ }
+
+ if (ct_status_get_cookie(cs) == CONFIGD_COOKIE)
+ configd_ctid = ctids[n];
+
+ ct_status_free(cs);
+
+ startd_close(csfd);
+ }
+
+ ct_status_free(s);
+
+ return (configd_ctid);
+}
+
+int
+contract_is_empty(ctid_t ctid)
+{
+ int fd;
+ ct_stathdl_t ctstat;
+ pid_t *members;
+ uint_t num;
+ int ret;
+
+ fd = contract_open(ctid, "process", "status", O_RDONLY);
+ if (fd < 0)
+ return (1);
+
+ ret = ct_status_read(fd, CTD_ALL, &ctstat);
+ (void) close(fd);
+ if (ret != 0)
+ return (1);
+
+ ret = ct_pr_status_get_members(ctstat, &members, &num);
+ ct_status_free(ctstat);
+ if (ret != 0)
+ return (1);
+
+ if (num == 0)
+ return (1);
+ else
+ return (0);
+}
+
+typedef struct contract_bucket {
+ pthread_mutex_t cb_lock;
+ uu_list_t *cb_list;
+} contract_bucket_t;
+
+#define CI_HASH_SIZE 64
+#define CI_HASH_MASK (CI_HASH_SIZE - 1);
+
+/*
+ * contract_hash is a hash table of contract ids to restarter instance
+ * IDs. It can be used for quick lookups when processing contract events,
+ * because the restarter instance lock doesn't need to be held to access
+ * its entries.
+ */
+static contract_bucket_t contract_hash[CI_HASH_SIZE];
+
+static contract_bucket_t *
+contract_hold_bucket(ctid_t ctid)
+{
+ contract_bucket_t *bp;
+ int hash;
+
+ hash = ctid & CI_HASH_MASK;
+
+ bp = &contract_hash[hash];
+ MUTEX_LOCK(&bp->cb_lock);
+ return (bp);
+}
+
+static void
+contract_release_bucket(contract_bucket_t *bp)
+{
+ assert(PTHREAD_MUTEX_HELD(&bp->cb_lock));
+ MUTEX_UNLOCK(&bp->cb_lock);
+}
+
+static contract_entry_t *
+contract_lookup(contract_bucket_t *bp, ctid_t ctid)
+{
+ contract_entry_t *ce;
+
+ assert(PTHREAD_MUTEX_HELD(&bp->cb_lock));
+
+ if (bp->cb_list == NULL)
+ return (NULL);
+
+ for (ce = uu_list_first(bp->cb_list); ce != NULL;
+ ce = uu_list_next(bp->cb_list, ce)) {
+ if (ce->ce_ctid == ctid)
+ return (ce);
+ }
+
+ return (NULL);
+}
+
+static void
+contract_insert(contract_bucket_t *bp, contract_entry_t *ce)
+{
+ int r;
+
+ if (bp->cb_list == NULL)
+ bp->cb_list = startd_list_create(contract_list_pool, bp, 0);
+
+ uu_list_node_init(ce, &ce->ce_link, contract_list_pool);
+ r = uu_list_insert_before(bp->cb_list, NULL, ce);
+ assert(r == 0);
+}
+
+void
+contract_hash_init()
+{
+ int i;
+
+ for (i = 0; i < CI_HASH_SIZE; i++)
+ (void) pthread_mutex_init(&contract_hash[i].cb_lock,
+ &mutex_attrs);
+}
+
+void
+contract_hash_store(ctid_t ctid, int instid)
+{
+ contract_bucket_t *bp;
+ contract_entry_t *ce;
+
+ bp = contract_hold_bucket(ctid);
+ assert(contract_lookup(bp, ctid) == NULL);
+ ce = startd_alloc(sizeof (contract_entry_t));
+ ce->ce_ctid = ctid;
+ ce->ce_instid = instid;
+
+ contract_insert(bp, ce);
+
+ contract_release_bucket(bp);
+}
+
+void
+contract_hash_remove(ctid_t ctid)
+{
+ contract_bucket_t *bp;
+ contract_entry_t *ce;
+
+ bp = contract_hold_bucket(ctid);
+
+ ce = contract_lookup(bp, ctid);
+ if (ce != NULL) {
+ uu_list_remove(bp->cb_list, ce);
+ startd_free(ce, sizeof (contract_entry_t));
+ }
+
+ contract_release_bucket(bp);
+}
+
+/*
+ * int lookup_inst_by_contract()
+ * Lookup the instance id in the hash table by the contract id.
+ * Returns instid if found, -1 if not. Doesn't do a hold on the
+ * instance, so a check for continued existence is required.
+ */
+int
+lookup_inst_by_contract(ctid_t ctid)
+{
+ contract_bucket_t *bp;
+ contract_entry_t *ce;
+ int id = -1;
+
+ bp = contract_hold_bucket(ctid);
+ ce = contract_lookup(bp, ctid);
+ if (ce != NULL)
+ id = ce->ce_instid;
+ contract_release_bucket(bp);
+
+ return (id);
+}