summaryrefslogtreecommitdiffstats
path: root/src/nis.c
diff options
context:
space:
mode:
authorNalin Dahyabhai <nalin.dahyabhai@pobox.com>2008-05-30 14:59:11 -0400
committerNalin Dahyabhai <nalin.dahyabhai@pobox.com>2008-05-30 14:59:11 -0400
commit3d207eb9f9f4b3bdc7e4775622fe75d318d054ae (patch)
treeb1a93be89a1a54915c81bc3c2194c6c04a99c67e /src/nis.c
parent32dd26f4052b57bf1cdc7fad2cc47874ff989cc1 (diff)
downloadslapi-nis-3d207eb9f9f4b3bdc7e4775622fe75d318d054ae.tar.gz
slapi-nis-3d207eb9f9f4b3bdc7e4775622fe75d318d054ae.tar.xz
slapi-nis-3d207eb9f9f4b3bdc7e4775622fe75d318d054ae.zip
- move nis_all processing to a works-in-chunks state machine
Diffstat (limited to 'src/nis.c')
-rw-r--r--src/nis.c394
1 files changed, 327 insertions, 67 deletions
diff --git a/src/nis.c b/src/nis.c
index a12b60b..b61163a 100644
--- a/src/nis.c
+++ b/src/nis.c
@@ -353,6 +353,55 @@ nis_maplist(struct plugin_state *state,
}
}
+/* Enumeration, if we want to break it down into chunks, happens in a few
+ * phases (given the protocol):
+ * 1. we're sending the first entry in a map
+ * 2. we're sending a not-the-first entry in a map
+ * 3. we're sending an end-of-map
+ */
+struct nis_all_cookie {
+ enum nis_all_cookie_state {
+ cookie_bad,
+ cookie_first,
+ cookie_next,
+ cookie_this,
+ cookie_end1,
+ cookie_end2,
+ } state;
+ unsigned int key_length;
+ char key[1];
+};
+static void
+nis_all_free_cookie(struct nis_all_cookie *cookie)
+{
+ free(cookie);
+}
+static struct nis_all_cookie *
+nis_all_make_cookie(enum nis_all_cookie_state state,
+ unsigned int length, const char *value)
+{
+ struct nis_all_cookie *cookie;
+ cookie = malloc(sizeof(*cookie) + ((length > 0) ? length : 0));
+ if (cookie != NULL) {
+ cookie->state = state;
+ switch (cookie->state) {
+ case cookie_bad:
+ case cookie_first:
+ case cookie_end1:
+ case cookie_end2:
+ break;
+ case cookie_this:
+ case cookie_next:
+ cookie->key_length = length;
+ if ((length > 0) && (value != NULL)) {
+ memcpy(&cookie->key, value, length);
+ }
+ break;
+ }
+ }
+ return cookie;
+}
+
static void
nis_all(struct plugin_state *state,
dispatch_reply_fragment *reply_fragment_fn,
@@ -365,92 +414,303 @@ nis_all(struct plugin_state *state,
struct ypreq_nokey req_nokey;
keydat_t *reply_key;
valdat_t *reply_val;
- bool_t supported;
+ struct nis_all_cookie *cookie;
+ enum nis_all_cookie_state next_state;
+ bool_t supported, stop;
memset(&req_nokey, 0, sizeof(req_nokey));
reply_key = &reply_all->ypresp_all_u.val.keydat;
reply_val = &reply_all->ypresp_all_u.val.valdat;
if (xdr_ypreq_nokey(request_xdrs, &req_nokey)) {
+ /* Take ownership of the cookie data. */
+ if (continuation_cookie) {
+ if (*continuation_cookie != NULL) {
+ cookie = *continuation_cookie;
+ } else {
+ cookie = nis_all_make_cookie(cookie_bad,
+ 0, NULL);
+ }
+ *continuation_cookie = NULL;
+ } else {
+ cookie = nis_all_make_cookie(cookie_bad,
+ 0, NULL);
+ }
+ /* Check if we even support the map. */
if (!map_supports_map(state, req_nokey.domain, req_nokey.map,
&supported) ||
!supported) {
- /* No entries? No such map final status. */
+ /* No entries? No-such-map final status. */
reply_all->more = TRUE;
reply_all->ypresp_all_u.val.status = YP_NOMAP;
reply_key->keydat_len = 0;
reply_val->valdat_len = 0;
- (*reply_fragment_fn)(state, cdata,
- reply, reply_xdrs, reply_buf,
- TRUE, FALSE);
+ /* Encode the reply header so that we can queue the
+ * entire reply as one block. */
+ xdr_replymsg(reply_xdrs, reply);
/* End of data. */
reply_all->more = FALSE;
- xdr_setpos(reply_xdrs, 0);
xdr_ypresp_all(reply_xdrs, reply_all);
- (*reply_fragment_fn)(state, cdata,
- reply, reply_xdrs, reply_buf,
- FALSE, TRUE);
- } else {
- bool_t first;
- reply_all->more = TRUE;
- reply_all->ypresp_all_u.val.status = YP_TRUE;
- reply_all->more = map_first(state,
- req_nokey.domain,
- req_nokey.map,
- &reply_key->keydat_len,
- &reply_key->keydat_val,
- &reply_val->valdat_len,
- &reply_val->valdat_val);
- first = TRUE;
- do {
- /* Send back a result. If it's the first
- * entry, also send back the reply header. */
+ /* Queue the entire response. */
+ if (!(*reply_fragment_fn)(state, cdata,
+ reply, reply_xdrs, reply_buf,
+ FALSE, TRUE)) {
slapi_log_error(SLAPI_LOG_PLUGIN,
state->plugin_desc->spd_id,
- "all(%s/%s) -> (%.*s),%d\n",
+ "all(%s/%s) - error queueing "
+ "error response\n",
req_nokey.domain,
- req_nokey.map,
- reply_key->keydat_len,
- reply_key->keydat_val,
- reply_all->more);
- (*reply_fragment_fn)(state, cdata,
- reply,
- reply_xdrs, reply_buf,
- first, FALSE);
- first = FALSE;
- /* Find the next entry. */
- reply_all->more = map_next(state,
- req_nokey.domain,
- req_nokey.map,
- reply_key->keydat_len,
- reply_key->keydat_val,
- &reply_key->keydat_len,
- &reply_key->keydat_val,
- &reply_val->valdat_len,
- &reply_val->valdat_val);
- /* If we got an entry, encode it. */
- if (reply_all->more) {
- xdr_setpos(reply_xdrs, 0);
- xdr_ypresp_all(reply_xdrs, reply_all);
- }
- } while (reply_all->more);
- /* Send the end-of-map marker. */
- reply_all->ypresp_all_u.val.status = YP_NOMORE;
- reply_key->keydat_len = 0;
- reply_val->valdat_len = 0;
+ req_nokey.map);
+ }
+ /* Don't return a cookie, if one was passed to us. */
+ nis_all_free_cookie(cookie);
+ cookie = NULL;
+ } else
+ for (stop = FALSE; stop == FALSE;) {
+ bool_t found, skip;
xdr_setpos(reply_xdrs, 0);
- reply_all->more = TRUE;
- xdr_ypresp_all(reply_xdrs, reply_all);
- /* End of data. */
- reply_all->more = FALSE;
- xdr_ypresp_all(reply_xdrs, reply_all);
- /* Bundle those two chunks into one reply. */
- (*reply_fragment_fn)(state, cdata,
- reply, reply_xdrs, reply_buf,
- FALSE, TRUE);
- slapi_log_error(SLAPI_LOG_PLUGIN,
- state->plugin_desc->spd_id,
- "all(%s/%s) done\n",
- req_nokey.domain, req_nokey.map);
+ memset(reply_all, 0, sizeof(reply_all));
+ /* Follow any instructions we left for this iteration.
+ */
+ switch (cookie->state) {
+ case cookie_bad:
+ /* fall through */
+ case cookie_first:
+ /* Read the first key in the map, and make the
+ * next state either be queuing the first item
+ * or queueing the end-of-map reply. */
+ found = map_first(state,
+ req_nokey.domain,
+ req_nokey.map,
+ &reply_key->keydat_len,
+ &reply_key->keydat_val,
+ &reply_val->valdat_len,
+ &reply_val->valdat_val);
+ if (found) {
+ /* Next time grab the entry after this
+ * one. */
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ state->plugin_desc->spd_id,
+ "all(%s/%s) \"%.*s\"\n",
+ req_nokey.domain,
+ req_nokey.map,
+ reply_key->keydat_len,
+ reply_key->keydat_val);
+ skip = FALSE;
+ reply_all->more = TRUE;
+ reply_all->ypresp_all_u.val.status = YP_TRUE;
+ next_state = cookie_next;
+ } else {
+ /* Don't reply, just move to end-of-map
+ * state. */
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ state->plugin_desc->spd_id,
+ "all(%s/%s) no-first\n",
+ req_nokey.domain,
+ req_nokey.map);
+ skip = TRUE;
+ next_state = cookie_end1;
+ }
+ /* Try to queue the packet. */
+ nis_all_free_cookie(cookie);
+ if (skip ||
+ (*reply_fragment_fn)(state, cdata,
+ reply,
+ reply_xdrs, reply_buf,
+ TRUE, FALSE)) {
+ /* Leave a note to choose the next
+ * entry or send end1, whichever is
+ * appropriate. */
+ cookie = nis_all_make_cookie(next_state,
+ reply_key->keydat_len,
+ reply_key->keydat_val);
+ } else {
+ /* Leave a note to try sending the
+ * first entry again. */
+ cookie = nis_all_make_cookie(cookie_first,
+ 0, NULL);
+ stop = TRUE;
+ }
+ break;
+ case cookie_next:
+ /* Read the next key in the map, and set up the
+ * cookie to note that we're queuing a not-
+ * first item. */
+ found = map_next(state,
+ req_nokey.domain,
+ req_nokey.map,
+ cookie->key_length,
+ cookie->key,
+ &reply_key->keydat_len,
+ &reply_key->keydat_val,
+ &reply_val->valdat_len,
+ &reply_val->valdat_val);
+ if (found) {
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ state->plugin_desc->spd_id,
+ "all(%s/%s) \"%.*s\"\n",
+ req_nokey.domain,
+ req_nokey.map,
+ reply_key->keydat_len,
+ reply_key->keydat_val);
+ /* Next time grab the entry after this
+ * one. */
+ skip = FALSE;
+ reply_all->more = TRUE;
+ reply_all->ypresp_all_u.val.status = YP_TRUE;
+ next_state = cookie_next;
+ } else {
+ /* Don't reply, just move to end-of-map
+ * state. */
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ state->plugin_desc->spd_id,
+ "all(%s/%s) no-next\n",
+ req_nokey.domain,
+ req_nokey.map);
+ skip = TRUE;
+ next_state = cookie_end1;
+ }
+ /* Try to queue the packet. */
+ if (skip ||
+ (xdr_ypresp_all(reply_xdrs, reply_all) &&
+ (*reply_fragment_fn)(state, cdata,
+ reply,
+ reply_xdrs, reply_buf,
+ FALSE, FALSE))) {
+ /* Leave a note to choose the next
+ * entry or send end1, whichever is
+ * appropriate. */
+ nis_all_free_cookie(cookie);
+ cookie = nis_all_make_cookie(next_state,
+ reply_key->keydat_len,
+ reply_key->keydat_val);
+ } else {
+ /* Leave a note to retry sending this
+ * entry the next time. */
+ nis_all_free_cookie(cookie);
+ cookie = nis_all_make_cookie(cookie_this,
+ reply_key->keydat_len,
+ reply_key->keydat_val);
+ stop = TRUE;
+ }
+ break;
+ case cookie_this:
+ /* Read the matching key in the map, and set up
+ * the cookie to note that we're queuing a not-
+ * first item. */
+ reply_key->keydat_len = cookie->key_length;
+ reply_key->keydat_val = cookie->key;
+ found = map_match(state,
+ req_nokey.domain,
+ req_nokey.map,
+ reply_key->keydat_len,
+ reply_key->keydat_val,
+ &reply_val->valdat_len,
+ &reply_val->valdat_val);
+ if (found) {
+ /* Next time grab the entry after this
+ * one. */
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ state->plugin_desc->spd_id,
+ "all(%s/%s) \"%.*s\" "
+ "(retry)\n",
+ req_nokey.domain,
+ req_nokey.map,
+ cookie->key_length,
+ cookie->key);
+ skip = FALSE;
+ reply_all->more = TRUE;
+ reply_all->ypresp_all_u.val.status = YP_TRUE;
+ next_state = cookie_next;
+ } else {
+ /* Don't reply, just move to end-of-map
+ * state. */
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ state->plugin_desc->spd_id,
+ "all(%s/%s) \"%.*s\" "
+ "(disappeared?)\n",
+ req_nokey.domain,
+ req_nokey.map,
+ cookie->key_length,
+ cookie->key);
+ skip = TRUE;
+ next_state = cookie_end1;
+ }
+ /* Try to queue the packet. */
+ if (skip ||
+ (xdr_ypresp_all(reply_xdrs, reply_all) &&
+ (*reply_fragment_fn)(state, cdata,
+ reply,
+ reply_xdrs, reply_buf,
+ FALSE, FALSE))) {
+ /* Leave a note to choose the next
+ * entry or send end1, whichever is
+ * appropriate. */
+ nis_all_free_cookie(cookie);
+ cookie = nis_all_make_cookie(next_state,
+ reply_key->keydat_len,
+ reply_key->keydat_val);
+ } else {
+ /* Leave a note to retry sending this
+ * entry the next time. But that's how
+ * we got here, so do nothing. */
+ stop = TRUE;
+ }
+ break;
+ case cookie_end1:
+ /* Send the end-of-map message. */
+ memset(reply_key, 0, sizeof(*reply_key));
+ memset(reply_val, 0, sizeof(*reply_key));
+ reply_all->more = TRUE;
+ reply_all->ypresp_all_u.val.status = YP_NOMORE;
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ state->plugin_desc->spd_id,
+ "all(%s/%s) end-of-map\n",
+ req_nokey.domain,
+ req_nokey.map);
+ if (xdr_ypresp_all(reply_xdrs, reply_all) &&
+ (*reply_fragment_fn)(state, cdata,
+ reply,
+ reply_xdrs, reply_buf,
+ FALSE, FALSE)) {
+ /* Leave a note to finish the reply. */
+ nis_all_free_cookie(cookie);
+ cookie = nis_all_make_cookie(cookie_end2,
+ 0, NULL);
+ } else {
+ /* Leave the note alone, so that we'll
+ * have to try again. */
+ stop = TRUE;
+ }
+ break;
+ case cookie_end2:
+ /* Send the final message. */
+ reply_all->more = FALSE;
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ state->plugin_desc->spd_id,
+ "all(%s/%s) done\n",
+ req_nokey.domain,
+ req_nokey.map);
+ if (xdr_ypresp_all(reply_xdrs, reply_all) &&
+ (*reply_fragment_fn)(state, cdata,
+ reply,
+ reply_xdrs, reply_buf,
+ FALSE, TRUE)) {
+ /* We're done. */
+ nis_all_free_cookie(cookie);
+ cookie = NULL;
+ } else {
+ /* Leave the note alone, so that we'll
+ * have to try again. */
+ }
+ stop = TRUE;
+ break;
+ }
+ }
+ /* Return the cookie if we can, else destroy it. */
+ if (continuation_cookie) {
+ *continuation_cookie = cookie;
+ } else {
+ nis_all_free_cookie(cookie);
}
} else {
/* XXX */