Exemplo n.º 1
0
/*
 * dhcp_lookup_dd_classify: perform lookup_dd(), or use existing records
 * if supplied, and classify the results based on the type of search criteria
 * being employed. Centralized policy for DN_FMANUAL and DN_FUNUSABLE flag
 * processing are implemented here. Classification is specialized
 * based on these specific search criteria:
 *
 *	S_CID		A CID match is requested. Perform DN_FMANUAL and
 *			DN_FUNUSABLE processing.
 *	S_FREE		A search for free records. Only examine first
 *			matching record.
 *	S_LRU		A search for lru records. Perform sort if needed,
 *			and only examine first matching record.
 *
 * A matching record is detached and returned if found (ok ||
 * manual + unusable). Other successful matches are returned in recordsp as
 * a cache.
 */
void *
dhcp_lookup_dd_classify(dsvc_dnet_t *pnd, boolean_t partial, uint_t query,
    int count, const dn_rec_t *targetp, void **recordsp, int searchtype)
{
	int		err;
	uint_t		rec_cnt = 0, manual = 0;
	dn_rec_t	*dnp;
	dn_rec_list_t	*nlp = NULL, *dnlp = NULL;
	dn_rec_list_t	*unulp = NULL;		/* list of unusables, !manual */
	dn_rec_list_t	*unu_m_lp = NULL;	/* list of unusable + manual */
	dn_rec_list_t	*m_lp = NULL;		/* list of manual records */
	dn_rec_list_t	*cachep = NULL;		/* match cache */
	struct in_addr	swapaddr;
	char		ntoab[INET_ADDRSTRLEN];

	/*
	 * Lookup records matching the specified criteria, or use
	 * records from a previous lookup supplied for classification.
	 */
	if (*recordsp == NULL) {

		TNF_PROBE_1_DEBUG(classify, "classify classify",
		    "classify_query%debug 'in func classify'",
		    tnf_long, query, query);

		err = dhcp_lookup_dd(pnd->dh, partial, query, count, targetp,
		    (void **)recordsp, &rec_cnt);

		TNF_PROBE_1_DEBUG(classify_cid_end, "classify classify_end",
		    "classify_end%debug 'in func classify'",
		    tnf_long, rec_cnt, rec_cnt);

		/*
		 * If any error occurs, mark the dsvc_dnet_t table
		 * for immediate close and reopen. Let the protocol
		 * perform recover, rather than attempting time-consuming
		 * in-place error recovery.
		 */
		if (err != DSVC_SUCCESS) {
			(void) mutex_lock(&pnd->pnd_mtx);
			pnd->flags |= DHCP_PND_ERROR;
			hash_Dtime(pnd->hand, 0);
			(void) mutex_unlock(&pnd->pnd_mtx);
#ifdef	DEBUG
			dhcpmsg(LOG_DEBUG, "classify failure %s\n",
				dhcpsvc_errmsg(err));
#endif	/* DEBUG */
			*recordsp = NULL;
			return (NULL);
		}

		/*
		 * For LRU classification, sort returned records based
		 * on dn_lease field. Discards records with valid lease
		 * times; adjusts rec_cnt accordingly.
		 */
		if (searchtype & S_LRU)
			*recordsp = get_lrusort(pnd, *recordsp, &rec_cnt);

	}

	/*
	 * Record classification: scan through all records, performing
	 * DN_FUNUSABLE and DN_FMANUAL processing. Note that most of the
	 * work has been performed by the datastore query. Remove the matching
	 * entry from the singlely-linked record list, for return. Free any
	 * non-matching entries prior to the match. Pass back any additional
	 * entries after the match in the recordsp pointer for possible re-use
	 * by the caching code.
	 */

	for (nlp = detach_dnrec_from_list(NULL, *recordsp,
	    (dn_rec_list_t **)recordsp); nlp != NULL;
	    nlp = detach_dnrec_from_list(NULL, *recordsp,
	    (dn_rec_list_t **)recordsp)) {
		/*
		 * If we find that there is a DN_FMANUAL entry that is
		 * DN_FUNUSABLE, we fail the request, when performing a
		 * CID search, even though there may be other CID matches. In
		 * the CID case, those other CID matches are errors, because
		 * there should be one and only one record for a client if that
		 * record is marked as being DN_FMANUALly assigned. We tell
		 * the user how many of those CID matches there are. If there
		 * are no DN_FMANUAL records, the first matching record which
		 * is USABLE wins.
		 */
		dnp = nlp->dnl_rec;
		if (dnp->dn_flags & DN_FUNUSABLE) {
			if ((searchtype & (S_CID|S_FREE|S_LRU)) == S_CID) {
				char	cidbuf[DHCP_MAX_OPT_SIZE];
				uint_t	blen = sizeof (cidbuf);

				(void) octet_to_hexascii(targetp->dn_cid,
				    targetp->dn_cid_len,
				    cidbuf, &blen);

				swapaddr.s_addr = htonl(dnp->dn_cip.s_addr);

				dhcpmsg(LOG_NOTICE, "(%1$s,%2$s) "
				    "currently marked as unusable.\n", cidbuf,
				    inet_ntop(AF_INET, &swapaddr, ntoab,
				    sizeof (ntoab)));
			}

			/* build list of unusable records */
			if (dnp->dn_flags & DN_FMANUAL) {
				attach_dnrec_to_list(nlp, &unu_m_lp);
				manual++;
			} else
				attach_dnrec_to_list(nlp, &unulp);
		} else {
			if (dnp->dn_flags & DN_FMANUAL) {
				attach_dnrec_to_list(nlp, &m_lp);
				manual++;
			} else
				attach_dnrec_to_list(nlp, &cachep);
			/*
			 * These searches do not require examining all
			 * matches.
			 */
			if (searchtype & (S_FREE|S_LRU))
				break;
		}
	}

	/*
	 * Warnings are printed for CID searches which end with
	 * DN_FUNUSABLE|DN_FMANUAL match(es).
	 */
	if (m_lp != NULL || unu_m_lp != NULL) {
		if (manual > 1) {
			char	cidbuf[DHCP_MAX_OPT_SIZE];
			uint_t	blen = sizeof (cidbuf);

			(void) octet_to_hexascii(targetp->dn_cid,
				targetp->dn_cid_len,
				cidbuf, &blen);
			dhcpmsg(LOG_WARNING,
			    "Manual allocation (%1$s) has %2$d other MANUAL"
			    " records. It should have 0.\n", cidbuf,
			    manual - 1);
		}
		if (unu_m_lp != NULL) {
			dnlp = detach_dnrec_from_list(NULL, unu_m_lp,
			    &unu_m_lp);
		} else
			dnlp = detach_dnrec_from_list(NULL, m_lp, &m_lp);
	}

	/* Free any unusable entries */
	if (unulp != NULL)
		dhcp_free_dd_list(pnd->dh, unulp);

	/* any other... */
	if (dnlp == NULL)
		dnlp = detach_dnrec_from_list(NULL, cachep, &cachep);

	/*
	 * Return any unused elements for possible caching use. These are
	 * the  additional manual + unusable (as punishment for having
	 * multiple items), manual, and and any others.
	 */
	if (cachep != NULL)
		attach_dnrec_to_list(cachep, (dn_rec_list_t **)recordsp);
	if (m_lp != NULL)
		attach_dnrec_to_list(m_lp, (dn_rec_list_t **)recordsp);
	if (unu_m_lp != NULL)
		attach_dnrec_to_list(unu_m_lp, (dn_rec_list_t **)recordsp);

	/*
	 * Return one of the matching record(s).
	 */
	return (dnlp);
}
Exemplo n.º 2
0
/* ARGSUSED */
static void
svc_lock(void *cookie, dsvcd_request_t *req, size_t reqsize,
    door_desc_t *doorp, uint_t ndoors)
{
	dsvcd_reply_t		reply;
	door_desc_t		door_desc;
	dsvcd_lock_request_t	*lreq = (dsvcd_lock_request_t *)req;
	dsvcd_datastore_t	*ds = (dsvcd_datastore_t *)cookie;
	dsvcd_container_t	*cn;
	dsvcd_unlock_desc_t	*ud;
	char			conid[MAXPATHLEN];
	unsigned int		attempts = 0;

	reply.rp_version = DSVCD_DOOR_VERSION;
	reply.rp_retval  = check_door_req(req, reqsize,
	    sizeof (dsvcd_lock_request_t));
	if (reply.rp_retval != DSVC_SUCCESS) {
		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
		return;
	}

	/*
	 * Verify that this is a lock request; if in the future we support
	 * other requests, we'll have to abstract this a bit.
	 */
	if (req->rq_reqtype != DSVCD_LOCK) {
		dhcpmsg(MSG_WARNING, "unsupported request `%d' on lock "
		    "request door", req->rq_reqtype);
		reply.rp_retval = DSVC_SYNCH_ERR;
		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
		return;
	}
	if (lreq->lrq_locktype != DSVCD_RDLOCK &&
	    lreq->lrq_locktype != DSVCD_WRLOCK) {
		dhcpmsg(MSG_WARNING, "request for unsupported locktype `%d'",
		    lreq->lrq_locktype);
		reply.rp_retval = DSVC_SYNCH_ERR;
		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
		return;
	}

	/*
	 * Find the container; create if it doesn't already exist.  We do
	 * this as a single operation to avoid race conditions.
	 */
	(void) snprintf(conid, sizeof (conid), "%s/%s%d_%s", lreq->lrq_loctoken,
	    ds->ds_name, lreq->lrq_conver, lreq->lrq_conname);
	cn = ds_get_container(ds, conid, lreq->lrq_crosshost);
	if (cn == NULL) {
		reply.rp_retval = DSVC_NO_MEMORY;
		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
		return;
	}

	/*
	 * We need another door descriptor which is passed back with the
	 * request.  This descriptor is used when the caller wants to
	 * gracefully unlock or when the caller terminates abnormally.
	 */
	ud = ud_create(cn, &reply.rp_retval);
	if (ud == NULL) {
		ds_release_container(ds, cn);
		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
		return;
	}

	/*
	 * We pass a duped door descriptor with the DOOR_RELEASE flag set
	 * instead of just passing the descriptor itself to handle the case
	 * where the client has gone away before we door_return().  Since
	 * we duped, the door descriptor itself will have a refcount of 2
	 * when we go to pass it to the client; if the client does not
	 * exist, the DOOR_RELEASE will drop the count from 2 to 1 which
	 * will cause a DOOR_UNREF_DATA call.
	 *
	 * In the regular (non-error) case, the door_return() will handoff
	 * the descriptor to the client, bumping the refcount to 3, and
	 * then the DOOR_RELEASE will drop the count to 2.  If the client
	 * terminates abnormally after this point, the count will drop from
	 * 2 to 1 which will cause a DOOR_UNREF_DATA call.  If the client
	 * unlocks gracefully, the refcount will still be 2 when the unlock
	 * door server procedure is called, and the unlock procedure will
	 * unlock the lock and note that the lock has been unlocked (so
	 * that we know the DOOR_UNREF_DATA call generated from the client
	 * subsequently closing the unlock descriptor is benign).
	 *
	 * Note that a DOOR_UNREF_DATA call will be generated *any time*
	 * the refcount goes from 2 to 1 -- even if *we* cause it to
	 * happen, which by default will happen in some of the error logic
	 * below (when we close the duped descriptor).  To prevent this
	 * scenario, we tell ud_destroy() *not* to cache the unlock
	 * descriptor, which forces it to blow away the descriptor using
	 * door_revoke(), making the close() that follows benign.
	 */
	door_desc.d_attributes = DOOR_DESCRIPTOR|DOOR_RELEASE;
	door_desc.d_data.d_desc.d_descriptor = dup(ud->ud_fd);
	if (door_desc.d_data.d_desc.d_descriptor == -1) {
		dhcpmsg(MSG_ERR, "cannot dup unlock door; denying %s "
		    "lock request", cn->cn_id);
		ud_destroy(ud, B_TRUE);
		ds_release_container(ds, cn);
		reply.rp_retval = DSVC_NO_RESOURCES;
		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
		return;
	}

	/*
	 * Acquire the actual read or write lock on the container.
	 */
	dhcpmsg(MSG_DEBUG, "tid %d: %s locking %s", thr_self(),
	    lreq->lrq_locktype == DSVCD_RDLOCK ? "read" : "write", cn->cn_id);

	if (lreq->lrq_locktype == DSVCD_RDLOCK)
		reply.rp_retval = cn_rdlock(cn, lreq->lrq_nonblock);
	else if (lreq->lrq_locktype == DSVCD_WRLOCK)
		reply.rp_retval = cn_wrlock(cn, lreq->lrq_nonblock);

	dhcpmsg(MSG_DEBUG, "tid %d: %s %s lock operation: %s", thr_self(),
	    cn->cn_id, lreq->lrq_locktype == DSVCD_RDLOCK ? "read" : "write",
	    dhcpsvc_errmsg(reply.rp_retval));

	ds_release_container(ds, cn);
	if (reply.rp_retval != DSVC_SUCCESS) {
		ud_destroy(ud, B_FALSE);
		(void) close(door_desc.d_data.d_desc.d_descriptor);
		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
		return;
	}

	while (door_return((char *)&reply, sizeof (reply), &door_desc, 1)
	    == -1 && errno == EMFILE) {
		if (lreq->lrq_nonblock) {
			dhcpmsg(MSG_WARNING, "unable to grant lock; client"
			    " is out of file descriptors");
			(void) cn_unlock(cn);
			ud_destroy(ud, B_FALSE);
			(void) close(door_desc.d_data.d_desc.d_descriptor);
			reply.rp_retval = DSVC_BUSY;
			(void) door_return((char *)&reply, sizeof (reply),
			    NULL, 0);
			return;
		}

		if (attempts++ == 0) {
			dhcpmsg(MSG_WARNING, "unable to grant lock; client"
			    " is out of file descriptors (retrying)");
		}
		(void) poll(NULL, 0, 100);
	}
}
Exemplo n.º 3
0
/* ARGSUSED */
static void
svc_unlock(void *cookie, dsvcd_request_t *req, size_t reqsize,
    door_desc_t *doorp, uint_t ndoors)
{
	dsvcd_unlock_desc_t	*ud = cookie;
	dsvcd_container_t	*cn;
	dsvcd_reply_t		reply;

	/*
	 * Although unlock descriptors are handed out to only a single
	 * thread who has been granted a lock (ergo it seems that only one
	 * thread should be able to call us back), there's a potential race
	 * here if the process crashes while in this door_call(), since
	 * both this thread and the unref kernel upcall thread may run at
	 * the same time.  Protect against this case with a mutex.
	 */
	(void) mutex_lock(&ud->ud_lock);
	cn = ud->ud_cn;

	/*
	 * First handle the case where the lock owner has closed the unlock
	 * descriptor, either because they have unlocked the lock and are
	 * thus done using the descriptor, or because they crashed.  In the
	 * second case, print a message.
	 */
	if (req == DOOR_UNREF_DATA) {
		/*
		 * The last reference is ours; we can free the descriptor.
		 */
		(void) mutex_unlock(&ud->ud_lock);
		ud_destroy(ud, B_TRUE);

		/*
		 * Normal case: the caller is closing the unlock descriptor
		 * on a lock they've already unlocked -- just return.
		 */
		if (cn == NULL) {
			(void) door_return(NULL, 0, NULL, 0);
			return;
		}

		/*
		 * Error case: the caller has crashed while holding the
		 * unlock descriptor (or is otherwise in violation of
		 * protocol).  Since all datastores are required to be
		 * robust even if unexpected termination occurs, we assume
		 * the container is not corrupt, even if the process
		 * crashed with the write lock held.
		 */
		switch (cn_locktype(cn)) {
		case DSVCD_RDLOCK:
			dhcpmsg(MSG_WARNING, "process exited while reading "
			    "`%s'; unlocking", cn->cn_id);
			(void) cn_unlock(cn);
			break;

		case DSVCD_WRLOCK:
			dhcpmsg(MSG_WARNING, "process exited while writing "
			    "`%s'; unlocking", cn->cn_id);
			dhcpmsg(MSG_WARNING, "note that this write operation "
			    "may or may not have succeeded");
			(void) cn_unlock(cn);
			break;

		case DSVCD_NOLOCK:
			dhcpmsg(MSG_CRIT, "unreferenced unheld lock");
			break;
		}

		(void) door_return(NULL, 0, NULL, 0);
		return;
	}

	/*
	 * Verify that this is a unlock request; if in the future we support
	 * other requests, we'll have to abstract this a bit.
	 */
	reply.rp_version = DSVCD_DOOR_VERSION;
	reply.rp_retval = check_door_req(req, reqsize,
	    sizeof (dsvcd_unlock_request_t));
	if (reply.rp_retval != DSVC_SUCCESS) {
		(void) mutex_unlock(&ud->ud_lock);
		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
		return;
	}

	if (req->rq_reqtype != DSVCD_UNLOCK) {
		dhcpmsg(MSG_WARNING, "unsupported request `%d' on unlock "
		    "request door", req->rq_reqtype);
		(void) mutex_unlock(&ud->ud_lock);
		reply.rp_retval = DSVC_SYNCH_ERR;
		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
		return;
	}

	/*
	 * Attempt to unlock an already-unlocked container; log and return.
	 */
	if (cn == NULL) {
		dhcpmsg(MSG_WARNING, "process tried to re-unlock a lock");
		(void) mutex_unlock(&ud->ud_lock);
		reply.rp_retval = DSVC_SYNCH_ERR;
		(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
		return;
	}
	ud->ud_cn = NULL;

	/*
	 * Unlock the container; note that after cn_unlock() has been done
	 * cn->cn_id is no longer accessible.
	 */
	dhcpmsg(MSG_DEBUG, "tid %d: unlocking %s", thr_self(), cn->cn_id);
	reply.rp_retval = cn_unlock(cn);
	dhcpmsg(MSG_DEBUG, "tid %d: unlock operation: %s", thr_self(),
	    dhcpsvc_errmsg(reply.rp_retval));

	/*
	 * Even though we've unlocked the lock, we cannot yet destroy the
	 * unlock descriptor (even if we revoke the door) because it's
	 * possible the unref thread is already waiting on ud_lock.
	 */
	(void) mutex_unlock(&ud->ud_lock);
	(void) door_return((char *)&reply, sizeof (reply), NULL, 0);
}
Exemplo n.º 4
0
/*
 * mkdstore <table> <nrecords> <cid> <flags> <cip> <sip> <lease> <macro>
 * <comment>
 */
main(int c, char **v)
{
	long long	cid;
	uchar_t	flags;
	struct in_addr	cip;
	struct in_addr	sip;
	int		i, j;
	char		**entries;
	uint_t		lease;
	char		*network = v[1];
	int		ct = strtol(v[2], 0L, 0L);
	char		*server;
	char		*macro;
	int		err;
	uint32_t	query;
	dn_rec_t	dn;
	dn_rec_list_t	*dncp = NULL;
	dhcp_confopt_t	*dsp = NULL;

#ifdef	DEBUG
	mallocctl(MTDEBUGPATTERN, 1);
	mallocctl(MTINITBUFFER, 1);
#endif				/* DEBUG */

	if (c == 1) {
		(void) fprintf(stderr, "/*\n * mkdstore <table> <nrecords> "
		    "<cid> <flags> <cip> <sip> <lease> <comment>\n*/");
		return (0);
	}

	cid = (c > 3) ? strtoul(v[3], 0L, 0L) : 0;
	flags = (c > 4) ? (char)strtol(v[4], 0L, 0L) : 0;
	cip.s_addr = (c > 5) ? strtoul(v[5], 0L, 0L) : 0;
	sip.s_addr = (c > 6) ? strtoul(v[6], 0L, 0L) : 0;
	lease = (c > 7) ? strtoul(v[7], 0L, 0L) : 0;
	macro = (c > 8) ? v[8] : 0;
	server = (c > 9) ? v[9] : "unknown";

	entries = (char **) malloc(ct * (sizeof (char *) * 8 + 4));

	/* Load current datastore. */
	(void) read_dsvc_conf(&dsp);
	if ((i = confopt_to_datastore(dsp, &datastore)) != DSVC_SUCCESS) {
		(void) fprintf(stderr, "Invalid datastore: %s\n",
		    dhcpsvc_errmsg(i));
		return (EINVAL);
	}
	err = open_dd(&dh, &datastore, DSVC_DHCPNETWORK, network,
	    DSVC_READ | DSVC_WRITE);

	if (err != DSVC_SUCCESS) {
		(void) fprintf(stderr, "Invalid network: %s trying create...\n",
		    dhcpsvc_errmsg(err));

		err = open_dd(&dh, &datastore, DSVC_DHCPNETWORK, network,
		    DSVC_READ | DSVC_WRITE | DSVC_CREATE);
		if (err != DSVC_SUCCESS) {
			(void) fprintf(stderr, "Can't create network: %s\n",
			    dhcpsvc_errmsg(err));
			return (err);
		}
	}
	/* XXXX: bug: currently can't get the count as advertised */
	(void) memset(&dn, '\0', sizeof (dn));
	DSVC_QINIT(query);
	err = lookup_dd(dh, B_FALSE, query, -1,
		    (const void *) &dn, (void **) &dncp, &nrecords);
	if (dncp)
		free_dd_list(dh, dncp);

	if (err != DSVC_SUCCESS) {
		(void) fprintf(stderr, "Bad nrecords: %s [%d]\n",
		    dhcpsvc_errmsg(err), nrecords);
		return (err);
	}

	for (i = 0, j = 0; i < ct; i++) {
		TNF_PROBE_1(main, "main",
			    "main%debug 'in function main'",
			    tnf_ulong, record, i);
		if (cid) {
			(void) memcpy(dn.dn_cid, &cid, sizeof (long long));
			dn.dn_cid_len = 7;
		} else {
			(void) memset(dn.dn_cid, '\0', sizeof (long long));
			dn.dn_cid_len = 1;
		}
		dn.dn_sig = 0;
		dn.dn_flags = flags;
		dn.dn_cip.s_addr = cip.s_addr;
		dn.dn_sip.s_addr = sip.s_addr;
		dn.dn_lease = lease;
		strcpy(dn.dn_macro, macro);
		strcpy(dn.dn_comment, server);
		(void) add_dd_entry(dh, &dn);
		if (cid)
			cid += 0x100;
		cip.s_addr++;

		TNF_PROBE_0(main_end, "main", "");
	}
	(void) close_dd(&dh);

	return (0);
}