Explorar o código

Qnetd: Add local IPC

Basic commands shutdown and status are implemented.

Signed-off-by: Jan Friesse <jfriesse@redhat.com>
Jan Friesse %!s(int64=9) %!d(string=hai) anos
pai
achega
a419f1685d

+ 4 - 1
qdevices/Makefile.am

@@ -46,7 +46,10 @@ corosync_qnetd_SOURCES	= corosync-qnetd.c dynar.c msg.c msgio.c nss-sock.c  \
 			    qnetd-algo-ffsplit.c qnetd-cluster-list.c qnetd-client-send.c \
 			    qnetd-algo-ffsplit.c qnetd-cluster-list.c qnetd-client-send.c \
 			    qnetd-algo-2nodelms.c qnetd-algo-lms.c utils.c qnetd-instance.c \
 			    qnetd-algo-2nodelms.c qnetd-algo-lms.c utils.c qnetd-instance.c \
 			    qnetd-client-net.c qnetd-client-msg-received.c qnetd-log-debug.c \
 			    qnetd-client-net.c qnetd-client-msg-received.c qnetd-log-debug.c \
-			    qnetd-client-algo-timer.c qnetd-dpd-timer.c
+			    qnetd-client-algo-timer.c qnetd-dpd-timer.c \
+			    qnetd-ipc.c unix-socket-ipc.c dynar-simple-lex.c dynar-str.c \
+			    unix-socket-client.c unix-socket-client-list.c unix-socket.c \
+			    qnetd-ipc-cmd.c
 
 
 corosync_qdevice_SOURCES	= corosync-qdevice.c qdevice-cmap.c qdevice-instance.c node-list.c \
 corosync_qdevice_SOURCES	= corosync-qdevice.c qdevice-cmap.c qdevice-instance.c node-list.c \
 				  utils.c qdevice-log.c qdevice-log-debug.c qdevice-votequorum.c \
 				  utils.c qdevice-log.c qdevice-log-debug.c qdevice-votequorum.c \

+ 90 - 10
qdevices/corosync-qnetd.c

@@ -44,6 +44,7 @@
 #include "pr-poll-array.h"
 #include "pr-poll-array.h"
 #include "qnetd-algorithm.h"
 #include "qnetd-algorithm.h"
 #include "qnetd-instance.h"
 #include "qnetd-instance.h"
+#include "qnetd-ipc.h"
 #include "qnetd-log.h"
 #include "qnetd-log.h"
 #include "qnetd-client-net.h"
 #include "qnetd-client-net.h"
 #include "qnetd-client-msg-received.h"
 #include "qnetd-client-msg-received.h"
@@ -53,7 +54,7 @@
 /*
 /*
  * This is global variable used for comunication with main loop and signal (calls close)
  * This is global variable used for comunication with main loop and signal (calls close)
  */
  */
-PRFileDesc *global_server_socket;
+struct qnetd_instance *global_instance;
 
 
 enum tlv_decision_algorithm_type
 enum tlv_decision_algorithm_type
     qnetd_static_supported_decision_algorithms[QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE] = {
     qnetd_static_supported_decision_algorithms[QNETD_STATIC_SUPPORTED_DECISION_ALGORITHMS_SIZE] = {
@@ -64,7 +65,8 @@ enum tlv_decision_algorithm_type
 };
 };
 
 
 static void
 static void
-qnetd_err_nss(void) {
+qnetd_err_nss(void)
+{
 
 
 	qnetd_log_nss(LOG_CRIT, "NSS error");
 	qnetd_log_nss(LOG_CRIT, "NSS error");
 
 
@@ -72,7 +74,8 @@ qnetd_err_nss(void) {
 }
 }
 
 
 static void
 static void
-qnetd_warn_nss(void) {
+qnetd_warn_nss(void)
+{
 
 
 	qnetd_log_nss(LOG_WARNING, "NSS warning");
 	qnetd_log_nss(LOG_WARNING, "NSS warning");
 }
 }
@@ -85,9 +88,12 @@ qnetd_pr_poll_array_create(struct qnetd_instance *instance)
 	struct qnetd_client *client;
 	struct qnetd_client *client;
 	PRPollDesc *poll_desc;
 	PRPollDesc *poll_desc;
 	struct qnetd_poll_array_user_data *user_data;
 	struct qnetd_poll_array_user_data *user_data;
+	const struct unix_socket_client_list *ipc_client_list;
+	struct unix_socket_client *ipc_client;
 
 
 	poll_array = &instance->poll_array;
 	poll_array = &instance->poll_array;
 	client_list = &instance->clients;
 	client_list = &instance->clients;
+	ipc_client_list = &instance->local_ipc.clients;
 
 
 	pr_poll_array_clean(poll_array);
 	pr_poll_array_clean(poll_array);
 
 
@@ -100,6 +106,14 @@ qnetd_pr_poll_array_create(struct qnetd_instance *instance)
 
 
 	user_data->type = QNETD_POLL_ARRAY_USER_DATA_TYPE_SOCKET;
 	user_data->type = QNETD_POLL_ARRAY_USER_DATA_TYPE_SOCKET;
 
 
+	if (pr_poll_array_add(poll_array, &poll_desc, (void **)&user_data) < 0) {
+		return (NULL);
+	}
+
+	poll_desc->fd = instance->ipc_socket_poll_fd;
+	poll_desc->in_flags = PR_POLL_READ;
+	user_data->type = QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_SOCKET;
+
 	TAILQ_FOREACH(client, client_list, entries) {
 	TAILQ_FOREACH(client, client_list, entries) {
 		if (pr_poll_array_add(poll_array, &poll_desc, (void **)&user_data) < 0) {
 		if (pr_poll_array_add(poll_array, &poll_desc, (void **)&user_data) < 0) {
 			return (NULL);
 			return (NULL);
@@ -115,6 +129,28 @@ qnetd_pr_poll_array_create(struct qnetd_instance *instance)
 		user_data->client = client;
 		user_data->client = client;
 	}
 	}
 
 
+	TAILQ_FOREACH(ipc_client, ipc_client_list, entries) {
+		if (!ipc_client->reading_line && !ipc_client->writing_buffer) {
+			continue;
+		}
+
+		if (pr_poll_array_add(poll_array, &poll_desc, (void **)&user_data) < 0) {
+			return (NULL);
+		}
+
+		poll_desc->fd = ((struct qnetd_ipc_user_data *)ipc_client->user_data)->nspr_poll_fd;
+		if (ipc_client->reading_line) {
+			poll_desc->in_flags |= PR_POLL_READ;
+		}
+
+		if (ipc_client->writing_buffer) {
+			poll_desc->in_flags |= PR_POLL_WRITE;
+		}
+
+		user_data->type = QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_CLIENT;
+		user_data->ipc_client = ipc_client;
+	}
+
 	pr_poll_array_gc(poll_array);
 	pr_poll_array_gc(poll_array);
 
 
 	return (poll_array->array);
 	return (poll_array->array);
@@ -129,6 +165,7 @@ qnetd_poll(struct qnetd_instance *instance)
 	ssize_t i;
 	ssize_t i;
 	int client_disconnect;
 	int client_disconnect;
 	struct qnetd_poll_array_user_data *user_data;
 	struct qnetd_poll_array_user_data *user_data;
+	struct unix_socket_client *ipc_client;
 
 
 	client = NULL;
 	client = NULL;
 	client_disconnect = 0;
 	client_disconnect = 0;
@@ -148,14 +185,22 @@ qnetd_poll(struct qnetd_instance *instance)
 		for (i = 0; i < pr_poll_array_size(&instance->poll_array); i++) {
 		for (i = 0; i < pr_poll_array_size(&instance->poll_array); i++) {
 			user_data = pr_poll_array_get_user_data(&instance->poll_array, i);
 			user_data = pr_poll_array_get_user_data(&instance->poll_array, i);
 
 
+			client = NULL;
+			ipc_client = NULL;
+			client_disconnect = 0;
+
 			switch (user_data->type) {
 			switch (user_data->type) {
 			case QNETD_POLL_ARRAY_USER_DATA_TYPE_SOCKET:
 			case QNETD_POLL_ARRAY_USER_DATA_TYPE_SOCKET:
-				client_disconnect = 0;
 				break;
 				break;
 			case QNETD_POLL_ARRAY_USER_DATA_TYPE_CLIENT:
 			case QNETD_POLL_ARRAY_USER_DATA_TYPE_CLIENT:
 				client = user_data->client;
 				client = user_data->client;
 				client_disconnect = client->schedule_disconnect;
 				client_disconnect = client->schedule_disconnect;
 				break;
 				break;
+			case QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_SOCKET:
+				break;
+			case QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_CLIENT:
+				ipc_client = user_data->ipc_client;
+				client_disconnect = ipc_client->schedule_disconnect;
 			}
 			}
 
 
 			if (!client_disconnect && poll_res > 0 &&
 			if (!client_disconnect && poll_res > 0 &&
@@ -169,6 +214,12 @@ qnetd_poll(struct qnetd_instance *instance)
 						client_disconnect = 1;
 						client_disconnect = 1;
 					}
 					}
 					break;
 					break;
+				case QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_SOCKET:
+					qnetd_ipc_accept(instance, &ipc_client);
+					break;
+				case QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_CLIENT:
+					qnetd_ipc_io_read(instance, ipc_client);
+					break;
 				}
 				}
 			}
 			}
 
 
@@ -188,6 +239,13 @@ qnetd_poll(struct qnetd_instance *instance)
 						client_disconnect = 1;
 						client_disconnect = 1;
 					}
 					}
 					break;
 					break;
+				case QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_SOCKET:
+					qnetd_log(LOG_CRIT, "POLL_WRITE on listening IPC socket");
+					return (-1);
+					break;
+				case QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_CLIENT:
+					qnetd_ipc_io_write(instance, ipc_client);
+					break;
 				}
 				}
 			}
 			}
 
 
@@ -196,6 +254,7 @@ qnetd_poll(struct qnetd_instance *instance)
 			    !(pfds[i].out_flags & (PR_POLL_READ|PR_POLL_WRITE))) {
 			    !(pfds[i].out_flags & (PR_POLL_READ|PR_POLL_WRITE))) {
 				switch (user_data->type) {
 				switch (user_data->type) {
 				case QNETD_POLL_ARRAY_USER_DATA_TYPE_SOCKET:
 				case QNETD_POLL_ARRAY_USER_DATA_TYPE_SOCKET:
+				case QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_SOCKET:
 					if (pfds[i].out_flags != PR_POLL_NVAL) {
 					if (pfds[i].out_flags != PR_POLL_NVAL) {
 						/*
 						/*
 						 * Poll ERR on listening socket is fatal error.
 						 * Poll ERR on listening socket is fatal error.
@@ -213,6 +272,12 @@ qnetd_poll(struct qnetd_instance *instance)
 					qnetd_log(LOG_DEBUG, "POLL_ERR (%u) on client socket. "
 					qnetd_log(LOG_DEBUG, "POLL_ERR (%u) on client socket. "
 					    "Disconnecting.", pfds[i].out_flags);
 					    "Disconnecting.", pfds[i].out_flags);
 
 
+					client_disconnect = 1;
+					break;
+				case QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_CLIENT:
+					qnetd_log(LOG_DEBUG, "POLL_ERR (%u) on ipc client socket."
+					    " Disconnecting.", pfds[i].out_flags);
+
 					client_disconnect = 1;
 					client_disconnect = 1;
 					break;
 					break;
 				}
 				}
@@ -221,8 +286,12 @@ qnetd_poll(struct qnetd_instance *instance)
 			/*
 			/*
 			 * If client is scheduled for disconnect, disconnect it
 			 * If client is scheduled for disconnect, disconnect it
 			 */
 			 */
-			if (client_disconnect) {
+			if (user_data->type == QNETD_POLL_ARRAY_USER_DATA_TYPE_CLIENT &&
+			    client_disconnect) {
 				qnetd_instance_client_disconnect(instance, client, 0);
 				qnetd_instance_client_disconnect(instance, client, 0);
+			} else if (user_data->type == QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_CLIENT &&
+			    (client_disconnect || ipc_client->schedule_disconnect)) {
+				qnetd_ipc_client_disconnect(instance, ipc_client);
 			}
 			}
 		}
 		}
 	}
 	}
@@ -235,18 +304,18 @@ static void
 signal_int_handler(int sig)
 signal_int_handler(int sig)
 {
 {
 
 
-	qnetd_log(LOG_DEBUG, "SIGINT received - closing server socket");
+	qnetd_log(LOG_DEBUG, "SIGINT received - closing server IPC socket");
 
 
-	PR_Close(global_server_socket);
+	qnetd_ipc_close(global_instance);
 }
 }
 
 
 static void
 static void
 signal_term_handler(int sig)
 signal_term_handler(int sig)
 {
 {
 
 
-	qnetd_log(LOG_DEBUG, "SIGTERM received - closing server socket");
+	qnetd_log(LOG_DEBUG, "SIGTERM received - closing server IPC socket");
 
 
-	PR_Close(global_server_socket);
+	qnetd_ipc_close(global_instance);
 }
 }
 
 
 static void
 static void
@@ -422,6 +491,11 @@ main(int argc, char * const argv[])
 		qnetd_err_nss();
 		qnetd_err_nss();
 	}
 	}
 
 
+	qnetd_log(LOG_DEBUG, "Initializing local socket");
+	if (qnetd_ipc_init(&instance) != 0) {
+		return (1);
+	}
+
 	qnetd_log(LOG_DEBUG, "Creating listening socket");
 	qnetd_log(LOG_DEBUG, "Creating listening socket");
 	instance.server.socket = nss_sock_create_listen_socket(instance.host_addr,
 	instance.server.socket = nss_sock_create_listen_socket(instance.host_addr,
 	    instance.host_port, address_family);
 	    instance.host_port, address_family);
@@ -437,7 +511,7 @@ main(int argc, char * const argv[])
 		qnetd_err_nss();
 		qnetd_err_nss();
 	}
 	}
 
 
-	global_server_socket = instance.server.socket;
+	global_instance = &instance;
 	signal_handlers_register();
 	signal_handlers_register();
 
 
 	qnetd_log(LOG_DEBUG, "Registering algorithms");
 	qnetd_log(LOG_DEBUG, "Registering algorithms");
@@ -455,6 +529,12 @@ main(int argc, char * const argv[])
 	/*
 	/*
 	 * Cleanup
 	 * Cleanup
 	 */
 	 */
+	qnetd_ipc_destroy(&instance);
+
+	if (PR_Close(instance.server.socket) != PR_SUCCESS) {
+		qnetd_warn_nss();
+	}
+
 	CERT_DestroyCertificate(instance.server.cert);
 	CERT_DestroyCertificate(instance.server.cert);
 	SECKEY_DestroyPrivateKey(instance.server.private_key);
 	SECKEY_DestroyPrivateKey(instance.server.private_key);
 
 

+ 2 - 0
qdevices/dynar-str.h

@@ -35,6 +35,8 @@
 #ifndef _DYNAR_STR_H_
 #ifndef _DYNAR_STR_H_
 #define _DYNAR_STR_H_
 #define _DYNAR_STR_H_
 
 
+#include <stdarg.h>
+
 #include "dynar.h"
 #include "dynar.h"
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus

+ 2 - 2
qdevices/qdevice-ipc.c

@@ -155,7 +155,7 @@ qdevice_ipc_send_error(struct qdevice_instance *instance, struct unix_socket_cli
 	if (res) {
 	if (res) {
 		unix_socket_client_write_buffer(client, 1);
 		unix_socket_client_write_buffer(client, 1);
 	} else {
 	} else {
-		qdevice_log(LOG_ERR, "Can't send error to client (buffer too small)");
+		qdevice_log(LOG_ERR, "Can't send ipc error to client (buffer too small)");
 	}
 	}
 
 
 	return (res ? 0 : -1);
 	return (res ? 0 : -1);
@@ -166,7 +166,7 @@ qdevice_ipc_send_buffer(struct qdevice_instance *instance, struct unix_socket_cl
 {
 {
 
 
 	if (dynar_str_prepend(&client->send_buffer, "OK\n") != 0) {
 	if (dynar_str_prepend(&client->send_buffer, "OK\n") != 0) {
-		qdevice_log(LOG_ERR, "Can't send error to client (buffer too small)");
+		qdevice_log(LOG_ERR, "Can't send ipc message to client (buffer too small)");
 
 
 		if (qdevice_ipc_send_error(instance, client, "Internal IPC buffer too small") != 0) {
 		if (qdevice_ipc_send_error(instance, client, "Internal IPC buffer too small") != 0) {
 			return (-1);
 			return (-1);

+ 6 - 0
qdevices/qnet-config.h

@@ -69,6 +69,12 @@ extern "C" {
 #define QNETD_DPD_INTERVAL			(10*1000)
 #define QNETD_DPD_INTERVAL			(10*1000)
 
 
 #define QNETD_LOCK_FILE				LOCALSTATEDIR"/run/corosync-qnetd.pid"
 #define QNETD_LOCK_FILE				LOCALSTATEDIR"/run/corosync-qnetd.pid"
+#define QNETD_LOCAL_SOCKET_FILE			LOCALSTATEDIR"/run/corosync-qnetd.sock"
+#define QNETD_LOCAL_SOCKET_BACKLOG		10
+
+#define QNETD_IPC_MAX_CLIENTS			10
+#define QNETD_IPC_MAX_RECEIVE_SIZE		(4*1024)
+#define QNETD_IPC_MAX_SEND_SIZE			(10*1024*1024)
 
 
 #define QDEVICE_NET_NSS_DB_DIR			COROSYSCONFDIR "/qdevice/net/node/nssdb"
 #define QDEVICE_NET_NSS_DB_DIR			COROSYSCONFDIR "/qdevice/net/node/nssdb"
 
 

+ 15 - 0
qdevices/qnetd-cluster-list.c

@@ -129,3 +129,18 @@ qnetd_cluster_list_free(struct qnetd_cluster_list *list)
 
 
 	TAILQ_INIT(list);
 	TAILQ_INIT(list);
 }
 }
+
+size_t
+qnetd_cluster_list_no_clusters(struct qnetd_cluster_list *list)
+{
+	size_t res;
+	struct qnetd_cluster *cluster;
+
+	res = 0;
+
+	TAILQ_FOREACH(cluster, list, entries) {
+		res++;
+	}
+
+	return (res);
+}

+ 3 - 0
qdevices/qnetd-cluster-list.h

@@ -69,6 +69,9 @@ extern void				 qnetd_cluster_list_del_client(
 
 
 extern void				 qnetd_cluster_list_free(struct qnetd_cluster_list *list);
 extern void				 qnetd_cluster_list_free(struct qnetd_cluster_list *list);
 
 
+extern size_t				 qnetd_cluster_list_no_clusters(
+    struct qnetd_cluster_list *list);
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif
 #endif

+ 3 - 0
qdevices/qnetd-instance.h

@@ -46,6 +46,7 @@
 #include "pr-poll-array.h"
 #include "pr-poll-array.h"
 #include "qnet-config.h"
 #include "qnet-config.h"
 #include "timer-list.h"
 #include "timer-list.h"
+#include "unix-socket-ipc.h"
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 extern "C" {
 extern "C" {
@@ -70,6 +71,8 @@ struct qnetd_instance {
 	uint16_t host_port;
 	uint16_t host_port;
 	struct timer_list main_timer_list;
 	struct timer_list main_timer_list;
 	struct timer_list_entry *dpd_timer;		/* Dead peer detection timer */
 	struct timer_list_entry *dpd_timer;		/* Dead peer detection timer */
+	struct unix_socket_ipc local_ipc;
+	PRFileDesc *ipc_socket_poll_fd;
 };
 };
 
 
 extern int		qnetd_instance_init(struct qnetd_instance *instance,
 extern int		qnetd_instance_init(struct qnetd_instance *instance,

+ 83 - 0
qdevices/qnetd-ipc-cmd.c

@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2015-2016 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Jan Friesse (jfriesse@redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - 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.
+ * - Neither the name of the Red Hat, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 "dynar-str.h"
+#include "qnetd-ipc-cmd.h"
+#include "qnetd-log.h"
+#include "utils.h"
+
+int
+qnetd_ipc_cmd_status(struct qnetd_instance *instance, struct dynar *outbuf, int verbose)
+{
+
+	if (dynar_str_catf(outbuf, "QNetd address:\t\t\t%s:%"PRIu16"\n",
+	    (instance->host_addr != NULL ? instance->host_addr : "*"), instance->host_port) == -1) {
+		return (-1);
+	}
+
+	if (dynar_str_catf(outbuf, "TLS:\t\t\t\t%s%s\n",
+	    tlv_tls_supported_to_str(instance->tls_supported),
+	    ((instance->tls_supported != TLV_TLS_UNSUPPORTED && instance->tls_client_cert_required) ?
+	    " (client certificate required)" : "")) == -1) {
+		return (-1);
+	}
+
+	if (dynar_str_catf(outbuf, "Connected clients:\t\t%zu\n",
+	    qnetd_client_list_no_clients(&instance->clients)) == -1) {
+		return (-1);
+	}
+
+	if (dynar_str_catf(outbuf, "Connected clusters:\t\t%zu\n",
+	    qnetd_cluster_list_no_clusters(&instance->clusters)) == -1) {
+		return (-1);
+	}
+
+	if (!verbose) {
+		return (0);
+	}
+
+	if (instance->max_clients != 0) {
+		if (dynar_str_catf(outbuf, "Maximum allowed clients:\t%zu\n",
+		    instance->max_clients) == -1) {
+			return (-1);
+		}
+	}
+
+	if (dynar_str_catf(outbuf, "Maximum send/receive size:\t%zu/%zu bytes\n",
+	    instance->max_client_send_size, instance->max_client_receive_size) == -1) {
+		return (-1);
+	}
+
+	return (0);
+}

+ 52 - 0
qdevices/qnetd-ipc-cmd.h

@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015-2016 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Jan Friesse (jfriesse@redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - 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.
+ * - Neither the name of the Red Hat, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
+ */
+
+#ifndef _QNETD_IPC_CMD_H_
+#define _QNETD_IPC_CMD_H_
+
+#include "dynar.h"
+#include "qnetd-instance.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int	qnetd_ipc_cmd_status(struct qnetd_instance *instance,
+    struct dynar *outbuf, int verbose);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_IPC_CMD_H_ */

+ 360 - 0
qdevices/qnetd-ipc.c

@@ -0,0 +1,360 @@
+/*
+ * Copyright (c) 2015-2016 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Jan Friesse (jfriesse@redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - 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.
+ * - Neither the name of the Red Hat, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 "qnet-config.h"
+#include "qnetd-ipc.h"
+#include "qnetd-ipc-cmd.h"
+#include "qnetd-log.h"
+#include "unix-socket-ipc.h"
+#include "dynar-simple-lex.h"
+#include "dynar-str.h"
+
+/*
+ * Needed for creating nspr handle from unix fd
+ */
+#include <private/pprio.h>
+
+int
+qnetd_ipc_init(struct qnetd_instance *instance)
+{
+
+	if (unix_socket_ipc_init(&instance->local_ipc, QNETD_LOCAL_SOCKET_FILE,
+	    QNETD_LOCAL_SOCKET_BACKLOG, QNETD_IPC_MAX_CLIENTS, QNETD_IPC_MAX_RECEIVE_SIZE,
+	    QNETD_IPC_MAX_SEND_SIZE) != 0) {
+		qnetd_log_err(LOG_ERR, "Can't create unix socket");
+
+		return (-1);
+	}
+
+	if ((instance->ipc_socket_poll_fd = PR_CreateSocketPollFd(instance->local_ipc.socket)) == NULL) {
+		qnetd_log_nss(LOG_CRIT, "Can't create NSPR IPC socket poll fd");
+
+		return (-1);
+	}
+
+	return (0);
+}
+
+int
+qnetd_ipc_close(struct qnetd_instance *instance)
+{
+	int res;
+
+	res = unix_socket_ipc_close(&instance->local_ipc);
+	if (res != 0) {
+		qnetd_log_err(LOG_WARNING, "Can't close local IPC");
+	}
+
+	return (res);
+}
+
+int
+qnetd_ipc_destroy(struct qnetd_instance *instance)
+{
+	int res;
+	struct unix_socket_client *client;
+	const struct unix_socket_client_list *ipc_client_list;
+
+	ipc_client_list = &instance->local_ipc.clients;
+
+	TAILQ_FOREACH(client, ipc_client_list, entries) {
+		free(client->user_data);
+	}
+
+	if (PR_DestroySocketPollFd(instance->ipc_socket_poll_fd) != PR_SUCCESS) {
+		qnetd_log_nss(LOG_WARNING, "Unable to destroy IPC poll socket fd");
+	}
+
+	res = unix_socket_ipc_destroy(&instance->local_ipc);
+	if (res != 0) {
+		qnetd_log_err(LOG_WARNING, "Can't destroy local IPC");
+	}
+
+	return (res);
+}
+
+int
+qnetd_ipc_accept(struct qnetd_instance *instance, struct unix_socket_client **res_client)
+{
+	int res;
+	int accept_res;
+	PRFileDesc *prfd;
+
+	accept_res = unix_socket_ipc_accept(&instance->local_ipc, res_client);
+
+	switch (accept_res) {
+	case -1:
+		qnetd_log_err(LOG_ERR, "Can't accept local IPC connection");
+		res = -1;
+		goto return_res;
+		break;
+	case -2:
+		qnetd_log(LOG_ERR, "Maximum IPC clients reached. Not accepting connection");
+		res = -1;
+		goto return_res;
+		break;
+	case -3:
+		qnetd_log(LOG_ERR, "Can't add client to list");
+		res = -1;
+		goto return_res;
+		break;
+	default:
+		unix_socket_client_read_line(*res_client, 1);
+		res = 0;
+		break;
+	}
+
+	(*res_client)->user_data = malloc(sizeof(struct qnetd_ipc_user_data));
+	if ((*res_client)->user_data == NULL) {
+		qnetd_log(LOG_ERR, "Can't alloc IPC client user data");
+		res = -1;
+		qnetd_ipc_client_disconnect(instance, *res_client);
+
+		goto return_res;
+	}
+	memset((*res_client)->user_data, 0, sizeof(struct qnetd_ipc_user_data));
+
+	prfd = PR_CreateSocketPollFd((*res_client)->socket);
+	if (prfd == NULL) {
+		qnetd_log_nss(LOG_CRIT, "Can't create NSPR poll fd for IPC client. Disconnecting client");
+		qnetd_ipc_client_disconnect(instance, *res_client);
+		res = -1;
+
+		goto return_res;
+	}
+
+	((struct qnetd_ipc_user_data *)(*res_client)->user_data)->nspr_poll_fd = prfd;
+
+return_res:
+	return (res);
+}
+
+void
+qnetd_ipc_client_disconnect(struct qnetd_instance *instance, struct unix_socket_client *client)
+{
+
+	if (PR_DestroySocketPollFd(
+	    ((struct qnetd_ipc_user_data *)(client)->user_data)->nspr_poll_fd) != PR_SUCCESS) {
+		qnetd_log_nss(LOG_WARNING, "Unable to destroy client IPC poll socket fd");
+	}
+
+	free(client->user_data);
+	unix_socket_ipc_client_disconnect(&instance->local_ipc, client);
+}
+
+int
+qnetd_ipc_send_error(struct qnetd_instance *instance, struct unix_socket_client *client,
+    const char *error_fmt, ...)
+{
+	va_list ap;
+	int res;
+
+	va_start(ap, error_fmt);
+	res = ((dynar_str_cpy(&client->send_buffer, "Error\n") == 0) &&
+	    (dynar_str_vcatf(&client->send_buffer, error_fmt, ap) > 0) &&
+	    (dynar_str_cat(&client->send_buffer, "\n") == 0));
+
+	va_end(ap);
+
+	if (res) {
+		unix_socket_client_write_buffer(client, 1);
+	} else {
+		qnetd_log(LOG_ERR, "Can't send ipc error to client (buffer too small)");
+	}
+
+	return (res ? 0 : -1);
+}
+
+int
+qnetd_ipc_send_buffer(struct qnetd_instance *instance, struct unix_socket_client *client)
+{
+
+	if (dynar_str_prepend(&client->send_buffer, "OK\n") != 0) {
+		qnetd_log(LOG_ERR, "Can't send ipc message to client (buffer too small)");
+
+		if (qnetd_ipc_send_error(instance, client, "Internal IPC buffer too small") != 0) {
+			return (-1);
+		}
+
+		return (0);
+	}
+
+	unix_socket_client_write_buffer(client, 1);
+
+	return (0);
+}
+
+static void
+qnetd_ipc_parse_line(struct qnetd_instance *instance, struct unix_socket_client *client)
+{
+	struct dynar_simple_lex lex;
+	struct dynar *token;
+	char *str;
+	struct qnetd_ipc_user_data *ipc_user_data;
+	int verbose;
+
+	ipc_user_data = (struct qnetd_ipc_user_data *)client->user_data;
+
+	dynar_simple_lex_init(&lex, &client->receive_buffer, DYNAR_SIMPLE_LEX_TYPE_QUOTE);
+	token = dynar_simple_lex_token_next(&lex);
+
+	verbose = 0;
+
+	if (token == NULL) {
+		qnetd_log(LOG_ERR, "Can't alloc memory for simple lex");
+
+		if (qnetd_ipc_send_error(instance, client, "Command too long") != 0) {
+			client->schedule_disconnect = 1;
+		}
+
+		return;
+	}
+
+	str = dynar_data(token);
+	if (strcasecmp(str, "") == 0) {
+		qnetd_log(LOG_DEBUG, "IPC client doesn't send command");
+		if (qnetd_ipc_send_error(instance, client, "No command specified") != 0) {
+			client->schedule_disconnect = 1;
+		}
+	} else if (strcasecmp(str, "shutdown") == 0) {
+		qnetd_log(LOG_DEBUG, "IPC client requested shutdown");
+
+		ipc_user_data->shutdown_requested = 1;
+
+		if (qnetd_ipc_send_buffer(instance, client) != 0) {
+			client->schedule_disconnect = 1;
+		}
+	} else if (strcasecmp(str, "status") == 0) {
+		token = dynar_simple_lex_token_next(&lex);
+		str = dynar_data(token);
+
+		if (token != NULL && strcmp(str, "") != 0) {
+			if (strcasecmp(str, "verbose") == 0) {
+				verbose = 1;
+			}
+		}
+
+		if (qnetd_ipc_cmd_status(instance, &client->send_buffer, verbose) != 0) {
+			if (qnetd_ipc_send_error(instance, client, "Can't get QNetd status") != 0) {
+				client->schedule_disconnect = 1;
+			}
+		} else {
+			if (qnetd_ipc_send_buffer(instance, client) != 0) {
+				client->schedule_disconnect = 1;
+			}
+		}
+	} else {
+		qnetd_log(LOG_DEBUG, "IPC client sent unknown command");
+		if (qnetd_ipc_send_error(instance, client, "Unknown command '%s'", str) != 0) {
+			client->schedule_disconnect = 1;
+		}
+	}
+
+	dynar_simple_lex_destroy(&lex);
+}
+
+void
+qnetd_ipc_io_read(struct qnetd_instance *instance, struct unix_socket_client *client)
+{
+	int res;
+
+	res = unix_socket_client_io_read(client);
+
+	switch (res) {
+	case 0:
+		/*
+		 * Partial read
+		 */
+		break;
+	case -1:
+		qnetd_log(LOG_DEBUG, "IPC client closed connection");
+		client->schedule_disconnect = 1;
+		break;
+	case -2:
+		qnetd_log(LOG_ERR, "Can't store message from IPC client. Disconnecting client.");
+		client->schedule_disconnect = 1;
+		break;
+	case -3:
+		qnetd_log_err(LOG_ERR, "Can't receive message from IPC client. Disconnecting client.");
+		client->schedule_disconnect = 1;
+		break;
+	case 1:
+		/*
+		 * Full message received
+		 */
+		unix_socket_client_read_line(client, 0);
+
+		qnetd_ipc_parse_line(instance, client);
+		break;
+	}
+}
+
+void
+qnetd_ipc_io_write(struct qnetd_instance *instance, struct unix_socket_client *client)
+{
+	int res;
+	struct qnetd_ipc_user_data *ipc_user_data;
+
+	ipc_user_data = (struct qnetd_ipc_user_data *)client->user_data;
+
+	res = unix_socket_client_io_write(client);
+
+	switch (res) {
+	case 0:
+		/*
+		 * Partial send
+		 */
+		break;
+	case -1:
+		qnetd_log(LOG_DEBUG, "IPC client closed connection");
+		client->schedule_disconnect = 1;
+		break;
+	case -2:
+		qnetd_log_err(LOG_ERR, "Can't send message to IPC client. Disconnecting client");
+		client->schedule_disconnect = 1;
+		break;
+	case 1:
+		/*
+		 * Full message sent
+		 */
+		unix_socket_client_write_buffer(client, 0);
+		client->schedule_disconnect = 1;
+
+		if (ipc_user_data->shutdown_requested) {
+			qnetd_ipc_close(instance);
+		}
+
+		break;
+	}
+}

+ 78 - 0
qdevices/qnetd-ipc.h

@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2015-2016 Red Hat, Inc.
+ *
+ * All rights reserved.
+ *
+ * Author: Jan Friesse (jfriesse@redhat.com)
+ *
+ * This software licensed under BSD license, the text of which follows:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * - 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.
+ * - Neither the name of the Red Hat, Inc. nor the names of its
+ *   contributors may be used to endorse or promote products derived from this
+ *   software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
+ */
+
+#ifndef _QNETD_IPC_H_
+#define _QNETD_IPC_H_
+
+#include "qnetd-instance.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct qnetd_ipc_user_data {
+	int shutdown_requested;
+	PRFileDesc *nspr_poll_fd;
+};
+
+extern int		qnetd_ipc_init(struct qnetd_instance *instance);
+
+extern int		qnetd_ipc_close(struct qnetd_instance *instance);
+
+extern int		qnetd_ipc_destroy(struct qnetd_instance *instance);
+
+extern int		qnetd_ipc_accept(struct qnetd_instance *instance,
+    struct unix_socket_client **res_client);
+
+extern void		qnetd_ipc_client_disconnect(struct qnetd_instance *instance,
+    struct unix_socket_client *client);
+
+extern void		qnetd_ipc_io_read(struct qnetd_instance *instance,
+    struct unix_socket_client *client);
+
+extern void		qnetd_ipc_io_write(struct qnetd_instance *instance,
+    struct unix_socket_client *client);
+
+extern int		qnetd_ipc_send_error(struct qnetd_instance *instance,
+    struct unix_socket_client *client, const char *error_fmt, ...)
+    __attribute__((__format__(__printf__, 3, 4)));
+
+extern int		qnetd_ipc_send_buffer(struct qnetd_instance *instance,
+    struct unix_socket_client *client);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _QNETD_IPC_H_ */

+ 1 - 0
qdevices/qnetd-log.h

@@ -37,6 +37,7 @@
 
 
 #include <syslog.h>
 #include <syslog.h>
 #include <stdarg.h>
 #include <stdarg.h>
+#include <errno.h>
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 extern "C" {
 extern "C" {

+ 3 - 0
qdevices/qnetd-poll-array-user-data.h

@@ -44,11 +44,14 @@ extern "C" {
 enum qnetd_poll_array_user_data_type {
 enum qnetd_poll_array_user_data_type {
 	QNETD_POLL_ARRAY_USER_DATA_TYPE_SOCKET,
 	QNETD_POLL_ARRAY_USER_DATA_TYPE_SOCKET,
 	QNETD_POLL_ARRAY_USER_DATA_TYPE_CLIENT,
 	QNETD_POLL_ARRAY_USER_DATA_TYPE_CLIENT,
+	QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_SOCKET,
+	QNETD_POLL_ARRAY_USER_DATA_TYPE_IPC_CLIENT,
 };
 };
 
 
 struct qnetd_poll_array_user_data {
 struct qnetd_poll_array_user_data {
 	enum qnetd_poll_array_user_data_type type;
 	enum qnetd_poll_array_user_data_type type;
 	struct qnetd_client *client;
 	struct qnetd_client *client;
+	struct unix_socket_client *ipc_client;
 };
 };
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus