aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/nscd/mp_ws_query.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/nscd/mp_ws_query.c')
-rw-r--r--usr.sbin/nscd/mp_ws_query.c543
1 files changed, 543 insertions, 0 deletions
diff --git a/usr.sbin/nscd/mp_ws_query.c b/usr.sbin/nscd/mp_ws_query.c
new file mode 100644
index 000000000000..a2416655b991
--- /dev/null
+++ b/usr.sbin/nscd/mp_ws_query.c
@@ -0,0 +1,543 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cachelib.h"
+#include "config.h"
+#include "debug.h"
+#include "log.h"
+#include "query.h"
+#include "mp_ws_query.h"
+#include "singletons.h"
+
+static int on_mp_write_session_abandon_notification(struct query_state *);
+static int on_mp_write_session_close_notification(struct query_state *);
+static void on_mp_write_session_destroy(struct query_state *);
+static int on_mp_write_session_mapper(struct query_state *);
+/* int on_mp_write_session_request_read1(struct query_state *); */
+static int on_mp_write_session_request_read2(struct query_state *);
+static int on_mp_write_session_request_process(struct query_state *);
+static int on_mp_write_session_response_write1(struct query_state *);
+static int on_mp_write_session_write_request_read1(struct query_state *);
+static int on_mp_write_session_write_request_read2(struct query_state *);
+static int on_mp_write_session_write_request_process(struct query_state *);
+static int on_mp_write_session_write_response_write1(struct query_state *);
+
+/*
+ * This function is used as the query_state's destroy_func to make the
+ * proper cleanup in case of errors.
+ */
+static void
+on_mp_write_session_destroy(struct query_state *qstate)
+{
+
+ TRACE_IN(on_mp_write_session_destroy);
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ if (qstate->mdata != NULL) {
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ abandon_cache_mp_write_session(
+ (cache_mp_write_session)qstate->mdata);
+ configuration_unlock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+ }
+ TRACE_OUT(on_mp_write_session_destroy);
+}
+
+/*
+ * The functions below are used to process multipart write session initiation
+ * requests.
+ * - on_mp_write_session_request_read1 and on_mp_write_session_request_read2
+ * read the request itself
+ * - on_mp_write_session_request_process processes it
+ * - on_mp_write_session_response_write1 sends the response
+ */
+int
+on_mp_write_session_request_read1(struct query_state *qstate)
+{
+ struct cache_mp_write_session_request *c_mp_ws_request;
+ ssize_t result;
+
+ TRACE_IN(on_mp_write_session_request_read1);
+ if (qstate->kevent_watermark == 0)
+ qstate->kevent_watermark = sizeof(size_t);
+ else {
+ init_comm_element(&qstate->request,
+ CET_MP_WRITE_SESSION_REQUEST);
+ c_mp_ws_request = get_cache_mp_write_session_request(
+ &qstate->request);
+
+ result = qstate->read_func(qstate,
+ &c_mp_ws_request->entry_length, sizeof(size_t));
+
+ if (result != sizeof(size_t)) {
+ LOG_ERR_3("on_mp_write_session_request_read1",
+ "read failed");
+ TRACE_OUT(on_mp_write_session_request_read1);
+ return (-1);
+ }
+
+ if (BUFSIZE_INVALID(c_mp_ws_request->entry_length)) {
+ LOG_ERR_3("on_mp_write_session_request_read1",
+ "invalid entry_length value");
+ TRACE_OUT(on_mp_write_session_request_read1);
+ return (-1);
+ }
+
+ c_mp_ws_request->entry = calloc(1,
+ c_mp_ws_request->entry_length + 1);
+ assert(c_mp_ws_request->entry != NULL);
+
+ qstate->kevent_watermark = c_mp_ws_request->entry_length;
+ qstate->process_func = on_mp_write_session_request_read2;
+ }
+ TRACE_OUT(on_mp_write_session_request_read1);
+ return (0);
+}
+
+static int
+on_mp_write_session_request_read2(struct query_state *qstate)
+{
+ struct cache_mp_write_session_request *c_mp_ws_request;
+ ssize_t result;
+
+ TRACE_IN(on_mp_write_session_request_read2);
+ c_mp_ws_request = get_cache_mp_write_session_request(&qstate->request);
+
+ result = qstate->read_func(qstate, c_mp_ws_request->entry,
+ c_mp_ws_request->entry_length);
+
+ if (result < 0 || (size_t)result != qstate->kevent_watermark) {
+ LOG_ERR_3("on_mp_write_session_request_read2",
+ "read failed");
+ TRACE_OUT(on_mp_write_session_request_read2);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = on_mp_write_session_request_process;
+
+ TRACE_OUT(on_mp_write_session_request_read2);
+ return (0);
+}
+
+static int
+on_mp_write_session_request_process(struct query_state *qstate)
+{
+ struct cache_mp_write_session_request *c_mp_ws_request;
+ struct cache_mp_write_session_response *c_mp_ws_response;
+ cache_mp_write_session ws;
+ cache_entry c_entry;
+ char *dec_cache_entry_name;
+
+ TRACE_IN(on_mp_write_session_request_process);
+ init_comm_element(&qstate->response, CET_MP_WRITE_SESSION_RESPONSE);
+ c_mp_ws_response = get_cache_mp_write_session_response(
+ &qstate->response);
+ c_mp_ws_request = get_cache_mp_write_session_request(&qstate->request);
+
+ qstate->config_entry = configuration_find_entry(
+ s_configuration, c_mp_ws_request->entry);
+ if (qstate->config_entry == NULL) {
+ c_mp_ws_response->error_code = ENOENT;
+
+ LOG_ERR_2("write_session_request",
+ "can't find configuration entry '%s'. "
+ "aborting request", c_mp_ws_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->enabled == 0) {
+ c_mp_ws_response->error_code = EACCES;
+
+ LOG_ERR_2("write_session_request",
+ "configuration entry '%s' is disabled",
+ c_mp_ws_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->perform_actual_lookups != 0) {
+ c_mp_ws_response->error_code = EOPNOTSUPP;
+
+ LOG_ERR_2("write_session_request",
+ "entry '%s' performs lookups by itself: "
+ "can't write to it", c_mp_ws_request->entry);
+ goto fin;
+ } else {
+#ifdef NS_NSCD_EID_CHECKING
+ if (check_query_eids(qstate) != 0) {
+ c_mp_ws_response->error_code = EPERM;
+ goto fin;
+ }
+#endif
+ }
+
+ /*
+ * All multipart entries are separated by their name decorations.
+ * For one configuration entry there will be a lot of multipart
+ * cache entries - each with its own decorated name.
+ */
+ asprintf(&dec_cache_entry_name, "%s%s", qstate->eid_str,
+ qstate->config_entry->mp_cache_params.cep.entry_name);
+ assert(dec_cache_entry_name != NULL);
+
+ configuration_lock_rdlock(s_configuration);
+ c_entry = find_cache_entry(s_cache,
+ dec_cache_entry_name);
+ configuration_unlock(s_configuration);
+
+ if (c_entry == INVALID_CACHE_ENTRY)
+ c_entry = register_new_mp_cache_entry(qstate,
+ dec_cache_entry_name);
+
+ free(dec_cache_entry_name);
+
+ assert(c_entry != NULL);
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ ws = open_cache_mp_write_session(c_entry);
+ if (ws == INVALID_CACHE_MP_WRITE_SESSION)
+ c_mp_ws_response->error_code = -1;
+ else {
+ qstate->mdata = ws;
+ qstate->destroy_func = on_mp_write_session_destroy;
+
+ if ((qstate->config_entry->mp_query_timeout.tv_sec != 0) ||
+ (qstate->config_entry->mp_query_timeout.tv_usec != 0))
+ memcpy(&qstate->timeout,
+ &qstate->config_entry->mp_query_timeout,
+ sizeof(struct timeval));
+ }
+ configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART);
+
+fin:
+ qstate->process_func = on_mp_write_session_response_write1;
+ qstate->kevent_watermark = sizeof(int);
+ qstate->kevent_filter = EVFILT_WRITE;
+
+ TRACE_OUT(on_mp_write_session_request_process);
+ return (0);
+}
+
+static int
+on_mp_write_session_response_write1(struct query_state *qstate)
+{
+ struct cache_mp_write_session_response *c_mp_ws_response;
+ ssize_t result;
+
+ TRACE_IN(on_mp_write_session_response_write1);
+ c_mp_ws_response = get_cache_mp_write_session_response(
+ &qstate->response);
+ result = qstate->write_func(qstate, &c_mp_ws_response->error_code,
+ sizeof(int));
+ if (result != sizeof(int)) {
+ LOG_ERR_3("on_mp_write_session_response_write1",
+ "write failed");
+ TRACE_OUT(on_mp_write_session_response_write1);
+ return (-1);
+ }
+
+ if (c_mp_ws_response->error_code == 0) {
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_mp_write_session_mapper;
+ qstate->kevent_filter = EVFILT_READ;
+ } else {
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ }
+ TRACE_OUT(on_mp_write_session_response_write1);
+ return (0);
+}
+
+/*
+ * Mapper function is used to avoid multiple connections for each session
+ * write or read requests. After processing the request, it does not close
+ * the connection, but waits for the next request.
+ */
+static int
+on_mp_write_session_mapper(struct query_state *qstate)
+{
+ ssize_t result;
+ int elem_type;
+
+ TRACE_IN(on_mp_write_session_mapper);
+ if (qstate->kevent_watermark == 0) {
+ qstate->kevent_watermark = sizeof(int);
+ } else {
+ result = qstate->read_func(qstate, &elem_type, sizeof(int));
+ if (result != sizeof(int)) {
+ LOG_ERR_3("on_mp_write_session_mapper",
+ "read failed");
+ TRACE_OUT(on_mp_write_session_mapper);
+ return (-1);
+ }
+
+ switch (elem_type) {
+ case CET_MP_WRITE_SESSION_WRITE_REQUEST:
+ qstate->kevent_watermark = sizeof(size_t);
+ qstate->process_func =
+ on_mp_write_session_write_request_read1;
+ break;
+ case CET_MP_WRITE_SESSION_ABANDON_NOTIFICATION:
+ qstate->kevent_watermark = 0;
+ qstate->process_func =
+ on_mp_write_session_abandon_notification;
+ break;
+ case CET_MP_WRITE_SESSION_CLOSE_NOTIFICATION:
+ qstate->kevent_watermark = 0;
+ qstate->process_func =
+ on_mp_write_session_close_notification;
+ break;
+ default:
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ LOG_ERR_2("on_mp_write_session_mapper",
+ "unknown element type");
+ TRACE_OUT(on_mp_write_session_mapper);
+ return (-1);
+ }
+ }
+ TRACE_OUT(on_mp_write_session_mapper);
+ return (0);
+}
+
+/*
+ * The functions below are used to process multipart write sessions write
+ * requests.
+ * - on_mp_write_session_write_request_read1 and
+ * on_mp_write_session_write_request_read2 read the request itself
+ * - on_mp_write_session_write_request_process processes it
+ * - on_mp_write_session_write_response_write1 sends the response
+ */
+static int
+on_mp_write_session_write_request_read1(struct query_state *qstate)
+{
+ struct cache_mp_write_session_write_request *write_request;
+ ssize_t result;
+
+ TRACE_IN(on_mp_write_session_write_request_read1);
+ init_comm_element(&qstate->request,
+ CET_MP_WRITE_SESSION_WRITE_REQUEST);
+ write_request = get_cache_mp_write_session_write_request(
+ &qstate->request);
+
+ result = qstate->read_func(qstate, &write_request->data_size,
+ sizeof(size_t));
+
+ if (result != sizeof(size_t)) {
+ LOG_ERR_3("on_mp_write_session_write_request_read1",
+ "read failed");
+ TRACE_OUT(on_mp_write_session_write_request_read1);
+ return (-1);
+ }
+
+ if (BUFSIZE_INVALID(write_request->data_size)) {
+ LOG_ERR_3("on_mp_write_session_write_request_read1",
+ "invalid data_size value");
+ TRACE_OUT(on_mp_write_session_write_request_read1);
+ return (-1);
+ }
+
+ write_request->data = calloc(1, write_request->data_size);
+ assert(write_request->data != NULL);
+
+ qstate->kevent_watermark = write_request->data_size;
+ qstate->process_func = on_mp_write_session_write_request_read2;
+ TRACE_OUT(on_mp_write_session_write_request_read1);
+ return (0);
+}
+
+static int
+on_mp_write_session_write_request_read2(struct query_state *qstate)
+{
+ struct cache_mp_write_session_write_request *write_request;
+ ssize_t result;
+
+ TRACE_IN(on_mp_write_session_write_request_read2);
+ write_request = get_cache_mp_write_session_write_request(
+ &qstate->request);
+
+ result = qstate->read_func(qstate, write_request->data,
+ write_request->data_size);
+
+ if (result < 0 || (size_t)result != qstate->kevent_watermark) {
+ LOG_ERR_3("on_mp_write_session_write_request_read2",
+ "read failed");
+ TRACE_OUT(on_mp_write_session_write_request_read2);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = on_mp_write_session_write_request_process;
+ TRACE_OUT(on_mp_write_session_write_request_read2);
+ return (0);
+}
+
+static int
+on_mp_write_session_write_request_process(struct query_state *qstate)
+{
+ struct cache_mp_write_session_write_request *write_request;
+ struct cache_mp_write_session_write_response *write_response;
+
+ TRACE_IN(on_mp_write_session_write_request_process);
+ init_comm_element(&qstate->response,
+ CET_MP_WRITE_SESSION_WRITE_RESPONSE);
+ write_response = get_cache_mp_write_session_write_response(
+ &qstate->response);
+ write_request = get_cache_mp_write_session_write_request(
+ &qstate->request);
+
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ write_response->error_code = cache_mp_write(
+ (cache_mp_write_session)qstate->mdata,
+ write_request->data,
+ write_request->data_size);
+ configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART);
+
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_mp_write_session_write_response_write1;
+ qstate->kevent_filter = EVFILT_WRITE;
+
+ TRACE_OUT(on_mp_write_session_write_request_process);
+ return (0);
+}
+
+static int
+on_mp_write_session_write_response_write1(struct query_state *qstate)
+{
+ struct cache_mp_write_session_write_response *write_response;
+ ssize_t result;
+
+ TRACE_IN(on_mp_write_session_write_response_write1);
+ write_response = get_cache_mp_write_session_write_response(
+ &qstate->response);
+ result = qstate->write_func(qstate, &write_response->error_code,
+ sizeof(int));
+ if (result != sizeof(int)) {
+ LOG_ERR_3("on_mp_write_session_write_response_write1",
+ "write failed");
+ TRACE_OUT(on_mp_write_session_write_response_write1);
+ return (-1);
+ }
+
+ if (write_response->error_code == 0) {
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_mp_write_session_mapper;
+ qstate->kevent_filter = EVFILT_READ;
+ } else {
+ qstate->kevent_watermark = 0;
+ qstate->process_func = 0;
+ }
+
+ TRACE_OUT(on_mp_write_session_write_response_write1);
+ return (0);
+}
+
+/*
+ * Handles abandon notifications. Destroys the session by calling the
+ * abandon_cache_mp_write_session.
+ */
+static int
+on_mp_write_session_abandon_notification(struct query_state *qstate)
+{
+ TRACE_IN(on_mp_write_session_abandon_notification);
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ abandon_cache_mp_write_session((cache_mp_write_session)qstate->mdata);
+ configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART);
+ qstate->mdata = INVALID_CACHE_MP_WRITE_SESSION;
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ TRACE_OUT(on_mp_write_session_abandon_notification);
+ return (0);
+}
+
+/*
+ * Handles close notifications. Commits the session by calling
+ * the close_cache_mp_write_session.
+ */
+static int
+on_mp_write_session_close_notification(struct query_state *qstate)
+{
+ TRACE_IN(on_mp_write_session_close_notification);
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ close_cache_mp_write_session((cache_mp_write_session)qstate->mdata);
+ configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART);
+ qstate->mdata = INVALID_CACHE_MP_WRITE_SESSION;
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ TRACE_OUT(on_mp_write_session_close_notification);
+ return (0);
+}
+
+cache_entry register_new_mp_cache_entry(struct query_state *qstate,
+ const char *dec_cache_entry_name)
+{
+ cache_entry c_entry;
+ char *en_bkp;
+
+ TRACE_IN(register_new_mp_cache_entry);
+ c_entry = INVALID_CACHE_ENTRY;
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+
+ configuration_lock_wrlock(s_configuration);
+ en_bkp = qstate->config_entry->mp_cache_params.cep.entry_name;
+ qstate->config_entry->mp_cache_params.cep.entry_name =
+ (char *)dec_cache_entry_name;
+ register_cache_entry(s_cache, (struct cache_entry_params *)
+ &qstate->config_entry->mp_cache_params);
+ qstate->config_entry->mp_cache_params.cep.entry_name = en_bkp;
+ configuration_unlock(s_configuration);
+
+ configuration_lock_rdlock(s_configuration);
+ c_entry = find_cache_entry(s_cache,
+ dec_cache_entry_name);
+ configuration_unlock(s_configuration);
+
+ configuration_entry_add_mp_cache_entry(qstate->config_entry,
+ c_entry);
+
+ configuration_unlock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+
+ TRACE_OUT(register_new_mp_cache_entry);
+ return (c_entry);
+}