2160 lines
68 KiB
Diff
2160 lines
68 KiB
Diff
|
commit 342dfbeb8275f5ea6ed52dd3f30126614ec1d037
|
||
|
Author: Ingo Franzki <ifranzki@linux.ibm.com>
|
||
|
Date: Mon Feb 15 14:33:07 2021 +0100
|
||
|
|
||
|
Event support: pkcsslotd changes
|
||
|
|
||
|
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
|
||
|
|
||
|
diff --git a/configure.ac b/configure.ac
|
||
|
index e0ae4a82..a0b098e1 100644
|
||
|
--- a/configure.ac
|
||
|
+++ b/configure.ac
|
||
|
@@ -234,6 +234,12 @@ AC_ARG_WITH([systemd],
|
||
|
AS_HELP_STRING([--with-systemd@<:@=DIR@:>@],[systemd system unit files location]),
|
||
|
[],
|
||
|
[with_systemd=no])
|
||
|
+
|
||
|
+dnl --- libudev development files
|
||
|
+AC_ARG_WITH([libudev],
|
||
|
+ AS_HELP_STRING([--with-libudev@<:@=DIR@:>@],[libudev development files location]),
|
||
|
+ [],
|
||
|
+ [with_libudev=check])
|
||
|
|
||
|
dnl ---
|
||
|
dnl ---
|
||
|
@@ -438,6 +444,46 @@ fi
|
||
|
AC_SUBST([XCRYPTOLINZ_CFLAGS])
|
||
|
AC_SUBST([XCRYPTOLINZ_LIBS])
|
||
|
|
||
|
+dnl --- with_libudev
|
||
|
+LIBUDEV_CFLAGS=
|
||
|
+LIBUDEV_LIBS=
|
||
|
+if test "x$with_libudev" != "xno"; then
|
||
|
+ if test "x$with_libudev" != "xyes" -a "x$with_libudev" != "xcheck"; then
|
||
|
+ LIBUDEV_CFLAGS="-I$with_libudev"
|
||
|
+ LIBUDEV_LIBS="-L$with_libudev"
|
||
|
+ fi
|
||
|
+ old_cflags="$CFLAGS"
|
||
|
+ old_libs="$LIBS"
|
||
|
+ CFLAGS="$CFLAGS $LIBUDEV_CFLAGS"
|
||
|
+ LIBS="$LIBS $LIBUDEV_LIBS"
|
||
|
+ # Use libudev only on s390 platforms, only s390 emits AP related uevents
|
||
|
+ case $target in
|
||
|
+ *s390x* | *s390*)
|
||
|
+ CFLAGS="$CFLAGS -DWITH_LIBUDEV"
|
||
|
+ ;;
|
||
|
+ *)
|
||
|
+ if test "x$with_libudev" != "xyes"; then
|
||
|
+ with_libudev=no
|
||
|
+ echo "Default to 'with_libudev=no' on non-s390 platforms"
|
||
|
+ fi
|
||
|
+ ;;
|
||
|
+ esac
|
||
|
+ if test "x$with_libudev" != "xno"; then
|
||
|
+ AC_CHECK_HEADER([libudev.h], [with_libudev=yes], [
|
||
|
+ AC_MSG_ERROR([Build with libudev requested but libudev headers couldn't be found])
|
||
|
+ ])
|
||
|
+ AC_CHECK_LIB([udev], [udev_monitor_new_from_netlink], [with_libudev=yes], [
|
||
|
+ AC_MSG_ERROR([Build with libudev requested but libudev libraries couldn't be found])
|
||
|
+ ])
|
||
|
+ fi
|
||
|
+ if test "x$with_libudev" = "xno"; then
|
||
|
+ CFLAGS="$old_cflags"
|
||
|
+ LIBS="$old_libs"
|
||
|
+ fi
|
||
|
+fi
|
||
|
+AC_SUBST([LIBUDEV_CFLAGS])
|
||
|
+AC_SUBST([LIBUDEV_LIBS])
|
||
|
+AM_CONDITIONAL([HAVE_LIBUDEV], [test "x$with_libudev" = "xyes"])
|
||
|
|
||
|
dnl ---
|
||
|
dnl --- Now check enabled features, while making sure every required
|
||
|
@@ -649,6 +695,7 @@ echo " Daemon build: $enable_daemon"
|
||
|
echo " Library build: $enable_library"
|
||
|
echo " Systemd service: $enable_systemd"
|
||
|
echo " Build with locks: $enable_locks"
|
||
|
+echo " Build with libudev: $with_libudev"
|
||
|
echo " Build p11sak tool: $enable_p11sak"
|
||
|
echo " token migrate tool: $enable_pkcstok_migrate"
|
||
|
echo
|
||
|
diff --git a/usr/include/slotmgr.h b/usr/include/slotmgr.h
|
||
|
index 4d038435..e37368a5 100644
|
||
|
--- a/usr/include/slotmgr.h
|
||
|
+++ b/usr/include/slotmgr.h
|
||
|
@@ -31,6 +31,7 @@
|
||
|
#define OCK_API_LOCK_FILE LOCKDIR_PATH "/LCK..APIlock"
|
||
|
|
||
|
#define PROC_SOCKET_FILE_PATH "/var/run/pkcsslotd.socket"
|
||
|
+#define ADMIN_SOCKET_FILE_PATH "/var/run/pkcsslotd.admin.socket"
|
||
|
|
||
|
#define PID_FILE_PATH "/var/run/pkcsslotd.pid"
|
||
|
#define OCK_CONFIG OCK_CONFDIR "/opencryptoki.conf"
|
||
|
@@ -45,6 +46,7 @@
|
||
|
|
||
|
#define NUMBER_SLOTS_MANAGED 1024
|
||
|
#define NUMBER_PROCESSES_ALLOWED 1000
|
||
|
+#define NUMBER_ADMINS_ALLOWED 1000
|
||
|
|
||
|
//
|
||
|
// Per Process Data structure
|
||
|
diff --git a/usr/sbin/pkcsslotd/pkcsslotd.h b/usr/sbin/pkcsslotd/pkcsslotd.h
|
||
|
index 69eb59f3..d7edcb3c 100644
|
||
|
--- a/usr/sbin/pkcsslotd/pkcsslotd.h
|
||
|
+++ b/usr/sbin/pkcsslotd/pkcsslotd.h
|
||
|
@@ -92,5 +92,8 @@ int init_socket_server();
|
||
|
int term_socket_server();
|
||
|
int init_socket_data(Slot_Mgr_Socket_t *sp);
|
||
|
int socket_connection_handler(int timeout_secs);
|
||
|
+#ifdef DEV
|
||
|
+void dump_socket_handler();
|
||
|
+#endif
|
||
|
|
||
|
#endif /* _SLOTMGR_H */
|
||
|
diff --git a/usr/sbin/pkcsslotd/pkcsslotd.mk b/usr/sbin/pkcsslotd/pkcsslotd.mk
|
||
|
index 2d36b4a9..c574edf8 100644
|
||
|
--- a/usr/sbin/pkcsslotd/pkcsslotd.mk
|
||
|
+++ b/usr/sbin/pkcsslotd/pkcsslotd.mk
|
||
|
@@ -8,6 +8,9 @@ CLEANFILES += usr/lib/common/parser.c usr/lib/common/parser.h \
|
||
|
usr/lib/common/parser.output usr/lib/common/lexer.c
|
||
|
|
||
|
usr_sbin_pkcsslotd_pkcsslotd_LDFLAGS = -lpthread -lcrypto
|
||
|
+if HAVE_LIBUDEV
|
||
|
+usr_sbin_pkcsslotd_pkcsslotd_LDFLAGS += -ludev
|
||
|
+endif
|
||
|
|
||
|
usr_sbin_pkcsslotd_pkcsslotd_CFLAGS = -DPROGRAM_NAME=\"$(@)\" \
|
||
|
-I${srcdir}/usr/include -I${srcdir}/usr/lib/common \
|
||
|
diff --git a/usr/sbin/pkcsslotd/signal.c b/usr/sbin/pkcsslotd/signal.c
|
||
|
index 49482a2f..17167632 100644
|
||
|
--- a/usr/sbin/pkcsslotd/signal.c
|
||
|
+++ b/usr/sbin/pkcsslotd/signal.c
|
||
|
@@ -21,7 +21,7 @@
|
||
|
extern BOOL IsValidProcessEntry(pid_t_64 pid, time_t_64 RegTime);
|
||
|
|
||
|
static int SigsToIntercept[] = {
|
||
|
- SIGHUP, SIGINT, SIGQUIT, SIGPIPE, SIGALRM,
|
||
|
+ SIGHUP, SIGINT, SIGQUIT, SIGALRM,
|
||
|
SIGTERM, SIGTSTP, SIGTTIN,
|
||
|
SIGTTOU, SIGUSR1, SIGUSR2, SIGPROF
|
||
|
};
|
||
|
@@ -32,8 +32,11 @@ static int SigsToIntercept[] = {
|
||
|
/* SIGCHLD - Don't want to exit. Should never receive, but we do, apparently
|
||
|
* when something tries to cancel the GC Thread */
|
||
|
|
||
|
+/* SIGPIPE - Don't want to exit. May happen when a connection to an admin
|
||
|
+ * event sender or a process is closed before all events are delivered. */
|
||
|
+
|
||
|
static int SigsToIgnore[] = {
|
||
|
- SIGCHLD,
|
||
|
+ SIGCHLD, SIGPIPE,
|
||
|
};
|
||
|
|
||
|
|
||
|
@@ -71,6 +74,10 @@ void slotdGenericSignalHandler(int Signal)
|
||
|
CheckForGarbage(shmp);
|
||
|
#endif
|
||
|
|
||
|
+#ifdef DEV
|
||
|
+ dump_socket_handler();
|
||
|
+#endif
|
||
|
+
|
||
|
for (procindex = 0; (procindex < NUMBER_PROCESSES_ALLOWED); procindex++) {
|
||
|
|
||
|
Slot_Mgr_Proc_t_64 *pProc = &(shmp->proc_table[procindex]);
|
||
|
diff --git a/usr/sbin/pkcsslotd/socket_server.c b/usr/sbin/pkcsslotd/socket_server.c
|
||
|
index 1fae0b95..41408670 100644
|
||
|
--- a/usr/sbin/pkcsslotd/socket_server.c
|
||
|
+++ b/usr/sbin/pkcsslotd/socket_server.c
|
||
|
@@ -1,4 +1,6 @@
|
||
|
/*
|
||
|
+ * COPYRIGHT (c) International Business Machines Corp. 2013, 2021
|
||
|
+ *
|
||
|
* This program is provided under the terms of the Common Public License,
|
||
|
* version 1.0 (CPL-1.0). Any use, reproduction or distribution for this
|
||
|
* software constitutes recipient's acceptance of CPL-1.0 terms which can be
|
||
|
@@ -12,6 +14,8 @@
|
||
|
#include <stdio.h>
|
||
|
#include <unistd.h>
|
||
|
#include <string.h>
|
||
|
+#include <stdlib.h>
|
||
|
+#include <fcntl.h>
|
||
|
|
||
|
#include <sys/time.h>
|
||
|
#include <sys/socket.h>
|
||
|
@@ -19,32 +23,1225 @@
|
||
|
#include <sys/select.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <grp.h>
|
||
|
+#include <sys/epoll.h>
|
||
|
+
|
||
|
+#if defined(__GNUC__) && __GNUC__ >= 7 || defined(__clang__) && __clang_major__ >= 12
|
||
|
+ #define FALL_THROUGH __attribute__ ((fallthrough))
|
||
|
+#else
|
||
|
+ #define FALL_THROUGH ((void)0)
|
||
|
+#endif
|
||
|
+
|
||
|
+#ifdef WITH_LIBUDEV
|
||
|
+#include <libudev.h>
|
||
|
+#endif
|
||
|
|
||
|
#include "log.h"
|
||
|
#include "slotmgr.h"
|
||
|
#include "pkcsslotd.h"
|
||
|
#include "apictl.h"
|
||
|
+#include "dlist.h"
|
||
|
+#include "events.h"
|
||
|
+
|
||
|
+#define MAX_EPOLL_EVENTS 128
|
||
|
+
|
||
|
+#ifdef WITH_LIBUDEV
|
||
|
+#define UDEV_RECV_BUFFFER_SIZE 512 * 1024
|
||
|
+#define UDEV_SUBSYSTEM_AP "ap"
|
||
|
+#define UDEV_ACTION_BIND "bind"
|
||
|
+#define UDEV_ACTION_UNBIND "unbind"
|
||
|
+#define UDEV_ACTION_DEVTYPE_APQN "ap_queue"
|
||
|
+#define UDEV_PROERTY_DEVTYPE "DEV_TYPE"
|
||
|
+#endif
|
||
|
+
|
||
|
+struct epoll_info {
|
||
|
+ int (* notify)(int events, void *private);
|
||
|
+ void (* free)(void *private);
|
||
|
+ void *private;
|
||
|
+ unsigned long ref_count;
|
||
|
+};
|
||
|
+
|
||
|
+struct listener_info {
|
||
|
+ int socket;
|
||
|
+ const char *file_path;
|
||
|
+ int (* new_conn)(int socket, struct listener_info *listener);
|
||
|
+ struct epoll_info ep_info;
|
||
|
+ unsigned long num_clients;
|
||
|
+ unsigned long max_num_clients;
|
||
|
+};
|
||
|
+
|
||
|
+enum xfer_state {
|
||
|
+ XFER_IDLE = 0,
|
||
|
+ XFER_RECEIVE = 1,
|
||
|
+ XFER_SEND = 2,
|
||
|
+};
|
||
|
+
|
||
|
+struct client_info {
|
||
|
+ int socket;
|
||
|
+ int (* xfer_complete)(void *client);
|
||
|
+ void (* hangup)(void *client);
|
||
|
+ void (* free)(void *client);
|
||
|
+ void *client;
|
||
|
+ struct epoll_info ep_info;
|
||
|
+ enum xfer_state xfer_state;
|
||
|
+ char *xfer_buffer;
|
||
|
+ size_t xfer_size;
|
||
|
+ size_t xfer_offset;
|
||
|
+};
|
||
|
+
|
||
|
+enum proc_state {
|
||
|
+ PROC_INITIAL_SEND = 0,
|
||
|
+ PROC_WAIT_FOR_EVENT = 1,
|
||
|
+ PROC_SEND_EVENT = 2,
|
||
|
+ PROC_SEND_PAYLOAD = 3,
|
||
|
+ PROC_RECEIVE_REPLY = 4,
|
||
|
+ PROC_HANGUP = 5,
|
||
|
+};
|
||
|
+
|
||
|
+struct proc_conn_info {
|
||
|
+ struct client_info client_info;
|
||
|
+ enum proc_state state;
|
||
|
+ DL_NODE *events;
|
||
|
+ struct event_info *event;
|
||
|
+ event_reply_t reply;
|
||
|
+};
|
||
|
+
|
||
|
+enum admin_state {
|
||
|
+ ADMIN_RECEIVE_EVENT = 0,
|
||
|
+ ADMIN_RECEIVE_PAYLOAD = 1,
|
||
|
+ ADMIN_EVENT_DELIVERED = 2,
|
||
|
+ ADMIN_SEND_REPLY = 3,
|
||
|
+ ADMIN_WAIT_FOR_EVENT_LIMIT = 4,
|
||
|
+ ADMIN_HANGUP = 5,
|
||
|
+};
|
||
|
+
|
||
|
+struct admin_conn_info {
|
||
|
+ struct client_info client_info;
|
||
|
+ enum admin_state state;
|
||
|
+ struct event_info *event;
|
||
|
+};
|
||
|
+
|
||
|
+#ifdef WITH_LIBUDEV
|
||
|
+struct udev_mon {
|
||
|
+ struct udev *udev;
|
||
|
+ struct udev_monitor *mon;
|
||
|
+ int socket;
|
||
|
+ struct epoll_info ep_info;
|
||
|
+ struct event_info *delayed_event;
|
||
|
+};
|
||
|
+#endif
|
||
|
+
|
||
|
+struct event_info {
|
||
|
+ event_msg_t event;
|
||
|
+ char *payload;
|
||
|
+ event_reply_t reply;
|
||
|
+ unsigned long proc_ref_count; /* # of processes using this event */
|
||
|
+ struct admin_conn_info *admin_ref; /* Admin connection to send reply back */
|
||
|
+};
|
||
|
+
|
||
|
+static int epoll_fd = -1;
|
||
|
+static struct listener_info proc_listener;
|
||
|
+static DL_NODE *proc_connections = NULL;
|
||
|
+static struct listener_info admin_listener;
|
||
|
+static DL_NODE *admin_connections = NULL;
|
||
|
+#ifdef WITH_LIBUDEV
|
||
|
+static struct udev_mon udev_mon;
|
||
|
+#endif
|
||
|
+static DL_NODE *pending_events = NULL;
|
||
|
+static unsigned long pending_events_count = 0;
|
||
|
+
|
||
|
+#define MAX_PENDING_EVENTS 1024
|
||
|
+
|
||
|
+/*
|
||
|
+ * Iterate over all connections in a safe way. Before actually iterating,
|
||
|
+ * increment the ref count of ALL connections, because any processing may
|
||
|
+ * cause any of the connections to be hang-up, and thus freed and removed
|
||
|
+ * from the list. We need to make sure that while we are iterating over the
|
||
|
+ * connections, none of them gets removed from the list.
|
||
|
+ */
|
||
|
+#define FOR_EACH_CONN_SAFE_BEGIN(list, conn) { \
|
||
|
+ DL_NODE *_node, *_next; \
|
||
|
+ _node = dlist_get_first(list); \
|
||
|
+ while (_node != NULL) { \
|
||
|
+ conn = _node->data; \
|
||
|
+ _next = dlist_next(_node); \
|
||
|
+ client_socket_get(&(conn)->client_info); \
|
||
|
+ _node = _next; \
|
||
|
+ } \
|
||
|
+ _node = dlist_get_first(list); \
|
||
|
+ while (_node != NULL) { \
|
||
|
+ conn = _node->data; \
|
||
|
+ _next = dlist_next(_node);
|
||
|
+
|
||
|
+#define FOR_EACH_CONN_SAFE_END(list, conn) \
|
||
|
+ _node = _next; \
|
||
|
+ } \
|
||
|
+ _node = dlist_get_first(list); \
|
||
|
+ while (_node != NULL) { \
|
||
|
+ conn = _node->data; \
|
||
|
+ _next = dlist_next(_node); \
|
||
|
+ client_socket_put(&(conn)->client_info); \
|
||
|
+ _node = _next; \
|
||
|
+ } \
|
||
|
+ }
|
||
|
+
|
||
|
+
|
||
|
+
|
||
|
+static void listener_socket_close(int socketfd, const char *file_path);
|
||
|
+static int listener_client_hangup(struct listener_info *listener);
|
||
|
+static void event_delivered(struct event_info *event);
|
||
|
+static int client_socket_notify(int events, void *private);
|
||
|
+static void client_socket_free(void *private);
|
||
|
+static int proc_xfer_complete(void *client);
|
||
|
+static int proc_start_deliver_event(struct proc_conn_info *conn);
|
||
|
+static int proc_deliver_event(struct proc_conn_info *conn,
|
||
|
+ struct event_info *event);
|
||
|
+static int proc_event_delivered(struct proc_conn_info *conn,
|
||
|
+ struct event_info *event);
|
||
|
+static inline void proc_get(struct proc_conn_info *conn);
|
||
|
+static inline void proc_put(struct proc_conn_info *conn);
|
||
|
+static void proc_hangup(void *client);
|
||
|
+static void proc_free(void *client);
|
||
|
+static int admin_xfer_complete(void *client);
|
||
|
+static void admin_event_limit_underrun(struct admin_conn_info *conn);
|
||
|
+static int admin_event_delivered(struct admin_conn_info *conn,
|
||
|
+ struct event_info *event);
|
||
|
+static inline void admin_get(struct admin_conn_info *conn);
|
||
|
+static inline void admin_put(struct admin_conn_info *conn);
|
||
|
+static void admin_hangup(void *client);
|
||
|
+static void admin_free(void *client);
|
||
|
+#ifdef WITH_LIBUDEV
|
||
|
+static void udev_mon_term(struct udev_mon *udev_mon);
|
||
|
+static int udev_mon_notify(int events, void *private);
|
||
|
+#endif
|
||
|
+
|
||
|
+static void epoll_info_init(struct epoll_info *epoll_info,
|
||
|
+ int (* notify)(int events, void *private),
|
||
|
+ void (* free_cb)(void *private),
|
||
|
+ void *private)
|
||
|
+{
|
||
|
+ epoll_info->ref_count = 1;
|
||
|
+ epoll_info->notify = notify;
|
||
|
+ epoll_info->free = free_cb;
|
||
|
+ epoll_info->private = private;
|
||
|
+}
|
||
|
+
|
||
|
+static void epoll_info_get(struct epoll_info *epoll_info)
|
||
|
+{
|
||
|
+ epoll_info->ref_count++;
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: private: %p, ref_count: %lu", __func__,
|
||
|
+ epoll_info->private, epoll_info->ref_count);
|
||
|
+}
|
||
|
+
|
||
|
+static void epoll_info_put(struct epoll_info *epoll_info)
|
||
|
+{
|
||
|
+ if (epoll_info->ref_count > 0)
|
||
|
+ epoll_info->ref_count--;
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: private: %p, ref_count: %lu", __func__,
|
||
|
+ epoll_info->private, epoll_info->ref_count);
|
||
|
+
|
||
|
+ if (epoll_info->ref_count == 0 && epoll_info->free != NULL)
|
||
|
+ epoll_info->free(epoll_info->private);
|
||
|
+}
|
||
|
+
|
||
|
+static int client_socket_init(int socket, int (* xfer_complete)(void *client),
|
||
|
+ void (* hangup)(void *client),
|
||
|
+ void (* free_cb)(void *client), void *client,
|
||
|
+ struct client_info *client_info)
|
||
|
+{
|
||
|
+ struct epoll_event evt;
|
||
|
+ int rc, err;
|
||
|
+
|
||
|
+ if (xfer_complete == NULL || hangup == NULL)
|
||
|
+ return -EINVAL;
|
||
|
+
|
||
|
+ epoll_info_init(&client_info->ep_info, client_socket_notify,
|
||
|
+ client_socket_free, client_info);
|
||
|
+ client_info->socket = socket;
|
||
|
+ client_info->xfer_complete = xfer_complete;
|
||
|
+ client_info->hangup = hangup;
|
||
|
+ client_info->free = free_cb;
|
||
|
+ client_info->client = client;
|
||
|
+ client_info->xfer_state = XFER_IDLE;
|
||
|
+
|
||
|
+ rc = fcntl(socket, F_SETFL, O_NONBLOCK);
|
||
|
+ if (rc < 0) {
|
||
|
+ err = errno;
|
||
|
+ InfoLog("%s: Failed to set client socket %d to non-blocking, errno "
|
||
|
+ "%d (%s).", __func__, socket, err, strerror(err));
|
||
|
+ return -err;
|
||
|
+ }
|
||
|
+
|
||
|
+ evt.events = EPOLLIN | EPOLLOUT | EPOLLRDHUP | EPOLLERR | EPOLLET;
|
||
|
+ evt.data.ptr = &client_info->ep_info;
|
||
|
+ rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket, &evt);
|
||
|
+ if (rc != 0) {
|
||
|
+ err = errno;
|
||
|
+ InfoLog("%s: Failed to add client socket %d to epoll, errno %d (%s).",
|
||
|
+ __func__, socket, err, strerror(err));
|
||
|
+ close(socket);
|
||
|
+ return -err;
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static inline void client_socket_get(struct client_info *client_info)
|
||
|
+{
|
||
|
+ epoll_info_get(&client_info->ep_info);
|
||
|
+}
|
||
|
+
|
||
|
+static inline void client_socket_put(struct client_info *client_info)
|
||
|
+{
|
||
|
+ epoll_info_put(&client_info->ep_info);
|
||
|
+}
|
||
|
+
|
||
|
+static void client_socket_term(struct client_info *client_info)
|
||
|
+{
|
||
|
+ epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_info->socket, NULL);
|
||
|
+ close(client_info->socket);
|
||
|
+ client_info->socket = -1;
|
||
|
+}
|
||
|
+
|
||
|
+static int client_socket_notify(int events, void *private)
|
||
|
+{
|
||
|
+ struct client_info *client_info = private;
|
||
|
+ ssize_t num;
|
||
|
+ int rc, err, socket = client_info->socket;
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: Epoll event on client %p socket %d: events: 0x%x xfer: %d",
|
||
|
+ __func__, client_info, socket, events, client_info->xfer_state);
|
||
|
+
|
||
|
+ if (socket < 0)
|
||
|
+ return -ENOTCONN;
|
||
|
+
|
||
|
+ if (events & (EPOLLHUP | EPOLLERR)) {
|
||
|
+ DbgLog(DL3, "EPOLLHUP | EPOLLERR");
|
||
|
+
|
||
|
+ client_info->hangup(client_info->client);
|
||
|
+ client_info = NULL; /* client_info may have been freed by now */
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (client_info->xfer_state == XFER_RECEIVE && (events & EPOLLIN)) {
|
||
|
+ DbgLog(DL3, "%s: EPOLLIN: buffer: %p size: %lu ofs: %lu", __func__,
|
||
|
+ client_info->xfer_buffer, client_info->xfer_size,
|
||
|
+ client_info->xfer_offset);
|
||
|
+
|
||
|
+ num = read(client_info->socket,
|
||
|
+ client_info->xfer_buffer + client_info->xfer_offset,
|
||
|
+ client_info->xfer_size - client_info->xfer_offset);
|
||
|
+ if (num <= 0) {
|
||
|
+ err = errno;
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: read failed with: num: %d errno: %d (%s)",
|
||
|
+ __func__, num, num < 0 ? err : 0,
|
||
|
+ num < 0 ? strerror(err) : "none");
|
||
|
+
|
||
|
+ if (num < 0 && err == EWOULDBLOCK)
|
||
|
+ return 0; /* Will be continued when socket becomes readable */
|
||
|
+
|
||
|
+ /* assume connection closed by peer */
|
||
|
+ client_info->hangup(client_info->client);
|
||
|
+ client_info = NULL; /* client_info may have been freed by now */
|
||
|
+ return 0;
|
||
|
+ } else {
|
||
|
+ DbgLog(DL3, "%s: %lu bytes received", __func__, num);
|
||
|
+
|
||
|
+ client_info->xfer_offset += num;
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: %lu bytes left", __func__,
|
||
|
+ client_info->xfer_size - client_info->xfer_offset);
|
||
|
+
|
||
|
+ if (client_info->xfer_offset >= client_info->xfer_size) {
|
||
|
+ client_info->xfer_state = XFER_IDLE;
|
||
|
+ client_info->xfer_buffer = NULL;
|
||
|
+ client_info->xfer_size = 0;
|
||
|
+ client_info->xfer_offset = 0;
|
||
|
+
|
||
|
+ client_socket_get(client_info);
|
||
|
+ rc = client_info->xfer_complete(client_info->client);
|
||
|
+ if (rc != 0) {
|
||
|
+ InfoLog("%s: xfer_complete callback failed for client "
|
||
|
+ "socket %d, rc: %d", __func__, socket,
|
||
|
+ rc);
|
||
|
+ client_info->hangup(client_info->client);
|
||
|
+ }
|
||
|
+ client_socket_put(client_info);
|
||
|
+ client_info = NULL; /* client_info may have been freed by now */
|
||
|
+ return rc;
|
||
|
+ }
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (client_info->xfer_state == XFER_SEND && (events & EPOLLOUT)) {
|
||
|
+ DbgLog(DL3, "%s: EPOLLOUT: buffer: %p size: %lu ofs: %lu", __func__,
|
||
|
+ client_info->xfer_buffer, client_info->xfer_size,
|
||
|
+ client_info->xfer_offset);
|
||
|
+
|
||
|
+ num = write(client_info->socket,
|
||
|
+ client_info->xfer_buffer + client_info->xfer_offset,
|
||
|
+ client_info->xfer_size - client_info->xfer_offset);
|
||
|
+ if (num < 0) {
|
||
|
+ err = errno;
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: write failed with: errno: %d (%s)", __func__, err,
|
||
|
+ strerror(err));
|
||
|
+
|
||
|
+ if (err == EWOULDBLOCK)
|
||
|
+ return 0; /* Will be continued when socket becomes writable */
|
||
|
+
|
||
|
+ /* assume connection closed by peer */
|
||
|
+ client_info->hangup(client_info->client);
|
||
|
+ client_info = NULL; /* client_info may have been freed by now */
|
||
|
+ return 0;
|
||
|
+ } else {
|
||
|
+ DbgLog(DL3, "%s: %lu bytes sent", __func__, num);
|
||
|
+
|
||
|
+ client_info->xfer_offset += num;
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: %lu bytes left", __func__,
|
||
|
+ client_info->xfer_size - client_info->xfer_offset);
|
||
|
+
|
||
|
+ if (client_info->xfer_offset >= client_info->xfer_size) {
|
||
|
+ client_info->xfer_state = XFER_IDLE;
|
||
|
+ client_info->xfer_buffer = NULL;
|
||
|
+ client_info->xfer_size = 0;
|
||
|
+ client_info->xfer_offset = 0;
|
||
|
+
|
||
|
+ client_socket_get(client_info);
|
||
|
+ rc = client_info->xfer_complete(client_info->client);
|
||
|
+ if (rc != 0) {
|
||
|
+ InfoLog("%s: xfer_complete callback failed for client "
|
||
|
+ "socket %d, rc: %d", __func__, socket,
|
||
|
+ rc);
|
||
|
+ client_info->hangup(client_info->client);
|
||
|
+ }
|
||
|
+ client_socket_put(client_info);
|
||
|
+ client_info = NULL; /* client_info may have been freed by now */
|
||
|
+ return rc;
|
||
|
+ }
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static void client_socket_free(void *private)
|
||
|
+{
|
||
|
+ struct client_info *client_info = private;
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: %p", __func__, client_info);
|
||
|
+
|
||
|
+ if (client_info->free != NULL)
|
||
|
+ client_info->free(client_info->client);
|
||
|
+}
|
||
|
+
|
||
|
+static int client_socket_receive(struct client_info *client_info,
|
||
|
+ void *buffer, size_t size)
|
||
|
+{
|
||
|
+ if (client_info->socket < 0)
|
||
|
+ return -ENOTCONN;
|
||
|
+
|
||
|
+ client_info->xfer_state = XFER_RECEIVE;
|
||
|
+ client_info->xfer_buffer = (char *)buffer;
|
||
|
+ client_info->xfer_size = size;
|
||
|
+ client_info->xfer_offset = 0;
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: Start receive on client socket %d: buffer: %p size: %lu",
|
||
|
+ __func__, client_info->socket, buffer, size);
|
||
|
+
|
||
|
+ return client_socket_notify(EPOLLIN, client_info);
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+static int client_socket_send(struct client_info *client_info,
|
||
|
+ void *buffer, size_t size)
|
||
|
+{
|
||
|
+ if (client_info->socket < 0)
|
||
|
+ return -ENOTCONN;
|
||
|
+
|
||
|
+ client_info->xfer_state = XFER_SEND;
|
||
|
+ client_info->xfer_buffer = (char *)buffer;
|
||
|
+ client_info->xfer_size = size;
|
||
|
+ client_info->xfer_offset = 0;
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: Start send on client socket %d: buffer: %p size: %lu",
|
||
|
+ __func__, client_info->socket, buffer, size);
|
||
|
+
|
||
|
+ return client_socket_notify(EPOLLOUT, client_info);
|
||
|
+}
|
||
|
+
|
||
|
+static struct event_info *event_new(unsigned int payload_len,
|
||
|
+ struct admin_conn_info *admin_conn)
|
||
|
+{
|
||
|
+ struct event_info *event;
|
||
|
+
|
||
|
+ event = calloc(1, sizeof(struct event_info));
|
||
|
+ if (event == NULL) {
|
||
|
+ ErrLog("%s: Failed to allocate the event", __func__);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ event->event.version = EVENT_VERSION_1;
|
||
|
+ event->event.payload_len = payload_len;
|
||
|
+ if (payload_len > 0) {
|
||
|
+ event->payload = malloc(payload_len);
|
||
|
+ if (event->payload == NULL) {
|
||
|
+ ErrLog("%s: Failed to allocate the event payload", __func__);
|
||
|
+ free(event);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ event->reply.version = EVENT_VERSION_1;
|
||
|
+
|
||
|
+ if (admin_conn != NULL)
|
||
|
+ admin_get(admin_conn);
|
||
|
+ event->admin_ref = admin_conn;
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: allocated event: %p", __func__, event);
|
||
|
+ return event;
|
||
|
+}
|
||
|
+
|
||
|
+static void event_limit_underrun()
|
||
|
+{
|
||
|
+ struct admin_conn_info *conn;
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: pending_events_count: %lu", __func__, pending_events_count);
|
||
|
+
|
||
|
+#ifdef WITH_LIBUDEV
|
||
|
+ /* Notify the udev monitor */
|
||
|
+ udev_mon_notify(EPOLLIN, &udev_mon);
|
||
|
+#endif
|
||
|
+
|
||
|
+ /* Notify all admin connections */
|
||
|
+ FOR_EACH_CONN_SAFE_BEGIN(admin_connections, conn) {
|
||
|
+ admin_event_limit_underrun(conn);
|
||
|
+ }
|
||
|
+ FOR_EACH_CONN_SAFE_END(admin_connections, conn)
|
||
|
+}
|
||
|
+
|
||
|
+static void event_free(struct event_info *event)
|
||
|
+{
|
||
|
+ DbgLog(DL3, "%s: free event: %p", __func__, event);
|
||
|
+
|
||
|
+ if (event->payload != NULL)
|
||
|
+ free(event->payload);
|
||
|
+ free(event);
|
||
|
+}
|
||
|
+
|
||
|
+static int event_add_to_pending_list(struct event_info *event)
|
||
|
+{
|
||
|
+ DL_NODE *list;
|
||
|
+
|
||
|
+ list = dlist_add_as_last(pending_events, event);
|
||
|
+ if (list == NULL) {
|
||
|
+ ErrLog("%s: failed add event to list of pending events", __func__);
|
||
|
+ return -ENOMEM;
|
||
|
+ }
|
||
|
+ pending_events = list;
|
||
|
+
|
||
|
+ pending_events_count++;
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static void event_remove_from_pending_list(struct event_info *event)
|
||
|
+{
|
||
|
+ DL_NODE *node;
|
||
|
+ int trigger = 0;
|
||
|
+
|
||
|
+ node = dlist_find(pending_events, event);
|
||
|
+ if (node != NULL) {
|
||
|
+ pending_events = dlist_remove_node(pending_events, node);
|
||
|
+
|
||
|
+ if (pending_events_count >= MAX_PENDING_EVENTS)
|
||
|
+ trigger = 1;
|
||
|
+
|
||
|
+ if (pending_events_count > 0)
|
||
|
+ pending_events_count--;
|
||
|
+
|
||
|
+ if (trigger)
|
||
|
+ event_limit_underrun();
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static int event_start_deliver(struct event_info *event)
|
||
|
+{
|
||
|
+ struct proc_conn_info *conn;
|
||
|
+ int rc;
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: event: %p", __func__, event);
|
||
|
+
|
||
|
+ if (pending_events_count >= MAX_PENDING_EVENTS) {
|
||
|
+ InfoLog("%s: Max pending events reached", __func__);
|
||
|
+ return -ENOSPC;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Add event of the list of pending events */
|
||
|
+ rc = event_add_to_pending_list(event);
|
||
|
+ if (rc != 0)
|
||
|
+ return rc;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Need to increment the event's ref count here, proc_deliver_event() may
|
||
|
+ * already complete the event delivery for one process, which then would
|
||
|
+ * free the event but it needs to be passed to other processes here, too.
|
||
|
+ */
|
||
|
+ event->proc_ref_count++;
|
||
|
+ FOR_EACH_CONN_SAFE_BEGIN(proc_connections, conn) {
|
||
|
+ rc = proc_deliver_event(conn, event);
|
||
|
+ if (rc != 0)
|
||
|
+ proc_hangup(conn);
|
||
|
+ }
|
||
|
+ FOR_EACH_CONN_SAFE_END(proc_connections, conn)
|
||
|
+ event->proc_ref_count--;
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: proc_ref_count: %u", __func__, event->proc_ref_count);
|
||
|
+
|
||
|
+ if (event->proc_ref_count == 0)
|
||
|
+ event_delivered(event);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static void event_delivered(struct event_info *event)
|
||
|
+{
|
||
|
+ struct admin_conn_info *conn;
|
||
|
+ int rc;
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: event: %p", __func__, event);
|
||
|
+
|
||
|
+ event_remove_from_pending_list(event);
|
||
|
+
|
||
|
+ /* Notify owning admin connection (if available), free otherwise */
|
||
|
+ if (event->admin_ref != NULL) {
|
||
|
+ conn = event->admin_ref;
|
||
|
+ admin_get(conn);
|
||
|
+ rc = admin_event_delivered(conn, event);
|
||
|
+ if (rc != 0) {
|
||
|
+ admin_hangup(conn);
|
||
|
+ event_free(event);
|
||
|
+ }
|
||
|
+ admin_put(conn);
|
||
|
+ } else {
|
||
|
+ event_free(event);
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static int proc_new_conn(int socket, struct listener_info *listener)
|
||
|
+{
|
||
|
+ struct proc_conn_info *conn;
|
||
|
+ struct event_info *event;
|
||
|
+ DL_NODE *list, *node;
|
||
|
+ int rc = 0;
|
||
|
+
|
||
|
+ UNUSED(listener);
|
||
|
+
|
||
|
+ DbgLog(DL0, "%s: Accepted connection from process: socket: %d", __func__,
|
||
|
+ socket);
|
||
|
+
|
||
|
+ conn = calloc(1, sizeof(struct proc_conn_info));
|
||
|
+ if (conn == NULL) {
|
||
|
+ ErrLog("%s: Failed to to allocate memory for the process connection",
|
||
|
+ __func__);
|
||
|
+ return -ENOMEM;
|
||
|
+ /* Caller will close socket */
|
||
|
+ }
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: process conn: %p", __func__, conn);
|
||
|
+
|
||
|
+ /* Add currently pending events to this connection */
|
||
|
+ node = dlist_get_first(pending_events);
|
||
|
+ while (node != NULL) {
|
||
|
+ event = (struct event_info *)node->data;
|
||
|
+ DbgLog(DL3, "%s: event: %p", __func__, event);
|
||
|
+
|
||
|
+ list = dlist_add_as_last(conn->events, event);
|
||
|
+ if (list == NULL) {
|
||
|
+ ErrLog("%s: failed add event to list of process's pending events",
|
||
|
+ __func__);
|
||
|
+ rc = -ENOMEM;
|
||
|
+ goto out;
|
||
|
+ }
|
||
|
+ conn->events = list;
|
||
|
+
|
||
|
+ event->proc_ref_count++;
|
||
|
+
|
||
|
+ node = dlist_next(node);
|
||
|
+ }
|
||
|
+
|
||
|
+ conn->state = PROC_INITIAL_SEND;
|
||
|
+
|
||
|
+ rc = client_socket_init(socket, proc_xfer_complete, proc_hangup, proc_free,
|
||
|
+ conn, &conn->client_info);
|
||
|
+ if (rc != 0)
|
||
|
+ goto out;
|
||
|
+
|
||
|
+ /* Add it to the process connections list */
|
||
|
+ list = dlist_add_as_first(proc_connections, conn);
|
||
|
+ if (list == NULL) {
|
||
|
+ rc = -ENOMEM;
|
||
|
+ goto out;
|
||
|
+ }
|
||
|
+ proc_connections = list;
|
||
|
+
|
||
|
+ proc_get(conn);
|
||
|
+ rc = client_socket_send(&conn->client_info, &socketData,
|
||
|
+ sizeof(socketData));
|
||
|
+ proc_put(conn);
|
||
|
+ conn = NULL; /* conn may have been freed by now */
|
||
|
+
|
||
|
+out:
|
||
|
+ if (rc != 0 && conn != NULL) {
|
||
|
+ proc_hangup(conn);
|
||
|
+ rc = 0; /* Don't return an error, we have already handled it */
|
||
|
+ }
|
||
|
+
|
||
|
+ return rc;
|
||
|
+}
|
||
|
+
|
||
|
+static int proc_xfer_complete(void *client)
|
||
|
+{
|
||
|
+ struct proc_conn_info *conn = client;
|
||
|
+ int rc;
|
||
|
+
|
||
|
+ DbgLog(DL0, "%s: Xfer completed: process: %p socket: %d state: %d",
|
||
|
+ __func__, conn, conn->client_info.socket, conn->state);
|
||
|
+
|
||
|
+ /*
|
||
|
+ * A non-zero return code returned by this function causes the caller to
|
||
|
+ * call proc_hangup(). Thus, no need to call proc_hangup() ourselves.
|
||
|
+ */
|
||
|
+
|
||
|
+ switch (conn->state) {
|
||
|
+ case PROC_INITIAL_SEND:
|
||
|
+ conn->state = PROC_WAIT_FOR_EVENT;
|
||
|
+ rc = proc_start_deliver_event(conn);
|
||
|
+ conn = NULL; /* conn may have been freed by now */
|
||
|
+ return rc;
|
||
|
+
|
||
|
+ case PROC_WAIT_FOR_EVENT:
|
||
|
+ /* handled in proc_start_deliver_event */
|
||
|
+ break;
|
||
|
+
|
||
|
+ case PROC_SEND_EVENT:
|
||
|
+ if (conn->event == NULL) {
|
||
|
+ TraceLog("%s: No current event to handle", __func__);
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (conn->event->event.payload_len > 0) {
|
||
|
+ conn->state = PROC_SEND_PAYLOAD;
|
||
|
+ rc = client_socket_send(&conn->client_info, conn->event->payload,
|
||
|
+ conn->event->event.payload_len);
|
||
|
+ conn = NULL; /* conn may have been freed by now */
|
||
|
+ return rc;
|
||
|
+ }
|
||
|
+ FALL_THROUGH;
|
||
|
+ /* fall through */
|
||
|
+
|
||
|
+ case PROC_SEND_PAYLOAD:
|
||
|
+ if (conn->event == NULL) {
|
||
|
+ TraceLog("%s: No current event to handle", __func__);
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (conn->event->event.flags & EVENT_FLAGS_REPLY_REQ) {
|
||
|
+ conn->state = PROC_RECEIVE_REPLY;
|
||
|
+ rc = client_socket_receive(&conn->client_info, &conn->reply,
|
||
|
+ sizeof(conn->reply));
|
||
|
+ conn = NULL; /* conn may have been freed by now */
|
||
|
+ return rc;
|
||
|
+ }
|
||
|
+ FALL_THROUGH;
|
||
|
+ /* fall through */
|
||
|
+
|
||
|
+ case PROC_RECEIVE_REPLY:
|
||
|
+ if (conn->event == NULL) {
|
||
|
+ TraceLog("%s: No current event to handle", __func__);
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (conn->event->event.flags & EVENT_FLAGS_REPLY_REQ) {
|
||
|
+ if (conn->reply.version != EVENT_VERSION_1) {
|
||
|
+ InfoLog("%s: Reply has a wrong version: %u", __func__,
|
||
|
+ conn->reply.version);
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Update reply counters in event */
|
||
|
+ conn->event->reply.positive_replies += conn->reply.positive_replies;
|
||
|
+ conn->event->reply.negative_replies += conn->reply.negative_replies;
|
||
|
+ conn->event->reply.nothandled_replies +=
|
||
|
+ conn->reply.nothandled_replies;
|
||
|
+ }
|
||
|
+
|
||
|
+ conn->state = PROC_WAIT_FOR_EVENT;
|
||
|
+
|
||
|
+ rc = proc_event_delivered(conn, conn->event);
|
||
|
+ conn = NULL; /* conn may have been freed by now */
|
||
|
+ return rc;
|
||
|
+
|
||
|
+ case PROC_HANGUP:
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int proc_start_deliver_event(struct proc_conn_info *conn)
|
||
|
+{
|
||
|
+ DL_NODE *node;
|
||
|
+ int rc;
|
||
|
+
|
||
|
+ if (conn->state != PROC_WAIT_FOR_EVENT)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ node = dlist_get_first(conn->events);
|
||
|
+ if (node == NULL)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ conn->event = node->data;
|
||
|
+ memset(&conn->reply, 0, sizeof(conn->reply));
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: process: %p event: %p", __func__, conn, conn->event);
|
||
|
+
|
||
|
+ conn->state = PROC_SEND_EVENT;
|
||
|
+ rc = client_socket_send(&conn->client_info, &conn->event->event,
|
||
|
+ sizeof(conn->event->event));
|
||
|
+ conn = NULL; /* conn may have been freed by now */
|
||
|
+ return rc;
|
||
|
+}
|
||
|
+
|
||
|
+static int proc_deliver_event(struct proc_conn_info *conn,
|
||
|
+ struct event_info *event)
|
||
|
+{
|
||
|
+ DL_NODE *list;
|
||
|
+ int rc;
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: process: %p event: %p", __func__, conn, event);
|
||
|
+
|
||
|
+ if (conn->state == PROC_HANGUP)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ /* Add to process's event list and incr. reference count */
|
||
|
+ list = dlist_add_as_last(conn->events, event);
|
||
|
+ if (list == NULL) {
|
||
|
+ ErrLog("%s: failed add event to list of process's pending events",
|
||
|
+ __func__);
|
||
|
+ return -ENOMEM;
|
||
|
+ }
|
||
|
+ conn->events = list;
|
||
|
+
|
||
|
+ event->proc_ref_count++;
|
||
|
+
|
||
|
+ rc = proc_start_deliver_event(conn);
|
||
|
+ return rc;
|
||
|
+}
|
||
|
+
|
||
|
+static int proc_event_delivered(struct proc_conn_info *conn,
|
||
|
+ struct event_info *event)
|
||
|
+{
|
||
|
+ DL_NODE *node;
|
||
|
+ int rc;
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: process: %p event: %p", __func__, conn, event);
|
||
|
+
|
||
|
+ conn->event = NULL;
|
||
|
+
|
||
|
+ /* Remove from process's event list and decr. reference count */
|
||
|
+ node = dlist_find(conn->events, event);
|
||
|
+ if (node != NULL) {
|
||
|
+ conn->events = dlist_remove_node(conn->events, node);
|
||
|
+ event->proc_ref_count--;
|
||
|
+ }
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: proc_ref_count: %u", __func__, event->proc_ref_count);
|
||
|
+
|
||
|
+ if (event->proc_ref_count == 0)
|
||
|
+ event_delivered(event);
|
||
|
+
|
||
|
+ /* Deliver further pending events, if any */
|
||
|
+ rc = proc_start_deliver_event(conn);
|
||
|
+ conn = NULL; /* conn may have been freed by now */
|
||
|
+ return rc;
|
||
|
+}
|
||
|
+
|
||
|
+static inline void proc_get(struct proc_conn_info *conn)
|
||
|
+{
|
||
|
+ client_socket_get(&conn->client_info);
|
||
|
+}
|
||
|
+
|
||
|
+static inline void proc_put(struct proc_conn_info *conn)
|
||
|
+{
|
||
|
+ client_socket_put(&conn->client_info);
|
||
|
+}
|
||
|
+
|
||
|
+static void proc_hangup(void *client)
|
||
|
+{
|
||
|
+ struct proc_conn_info *conn = client;
|
||
|
+ struct event_info *event;
|
||
|
+ DL_NODE *node;
|
||
|
+
|
||
|
+ DbgLog(DL0, "%s: process: %p socket: %d state: %d", __func__, conn,
|
||
|
+ conn->client_info.socket, conn->state);
|
||
|
+
|
||
|
+ if (conn->state == PROC_HANGUP)
|
||
|
+ return;
|
||
|
+ conn->state = PROC_HANGUP;
|
||
|
+
|
||
|
+ /* Unlink all pending events */
|
||
|
+ while ((node = dlist_get_first(conn->events)) != NULL) {
|
||
|
+ event = node->data;
|
||
|
+ /* We did not handle this event */
|
||
|
+ event->reply.nothandled_replies++;
|
||
|
+ proc_event_delivered(conn, event);
|
||
|
+ }
|
||
|
+
|
||
|
+ client_socket_term(&conn->client_info);
|
||
|
+ proc_put(conn);
|
||
|
+}
|
||
|
+
|
||
|
+static void proc_free(void *client)
|
||
|
+{
|
||
|
+ struct proc_conn_info *conn = client;
|
||
|
+ DL_NODE *node;
|
||
|
+
|
||
|
+ /* Remove it from the process connections list */
|
||
|
+ node = dlist_find(proc_connections, conn);
|
||
|
+ if (node != NULL) {
|
||
|
+ proc_connections = dlist_remove_node(proc_connections, node);
|
||
|
+ listener_client_hangup(&proc_listener);
|
||
|
+ }
|
||
|
+
|
||
|
+ DbgLog(DL0, "%s: process: %p", __func__, conn);
|
||
|
+ free(conn);
|
||
|
+}
|
||
|
+
|
||
|
+static int admin_new_conn(int socket, struct listener_info *listener)
|
||
|
+{
|
||
|
+ struct admin_conn_info *conn;
|
||
|
+ DL_NODE *list;
|
||
|
+ int rc = 0;
|
||
|
+
|
||
|
+ UNUSED(listener);
|
||
|
+
|
||
|
+ DbgLog(DL0, "%s: Accepted connection from admin: socket: %d", __func__,
|
||
|
+ socket);
|
||
|
+
|
||
|
+ conn = calloc(1, sizeof(struct admin_conn_info));
|
||
|
+ if (conn == NULL) {
|
||
|
+ ErrLog("%s: Failed to to allocate memory for the admin connection",
|
||
|
+ __func__);
|
||
|
+ return -ENOMEM;
|
||
|
+ /* Caller will close socket */
|
||
|
+ }
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: admin conn: %p", __func__, conn);
|
||
|
+
|
||
|
+ conn->state = ADMIN_RECEIVE_EVENT;
|
||
|
+
|
||
|
+ rc = client_socket_init(socket, admin_xfer_complete, admin_hangup,
|
||
|
+ admin_free, conn, &conn->client_info);
|
||
|
+ if (rc != 0)
|
||
|
+ goto out;
|
||
|
+
|
||
|
+ conn->event = event_new(0, conn);
|
||
|
+ if (conn->event == NULL) {
|
||
|
+ ErrLog("%s: Failed to allocate a new event", __func__);
|
||
|
+ rc = -ENOMEM;
|
||
|
+ goto out;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Add it to the admin connections list */
|
||
|
+ list = dlist_add_as_first(admin_connections, conn);
|
||
|
+ if (list == NULL) {
|
||
|
+ ErrLog("%s: Failed to add connection to list of admin connections",
|
||
|
+ __func__);
|
||
|
+ rc = -ENOMEM;
|
||
|
+ goto out;
|
||
|
+ }
|
||
|
+ admin_connections = list;
|
||
|
+
|
||
|
+ admin_get(conn);
|
||
|
+ rc = client_socket_receive(&conn->client_info, &conn->event->event,
|
||
|
+ sizeof(conn->event->event));
|
||
|
+ admin_put(conn);
|
||
|
+ conn = NULL; /* conn may have been freed by now */
|
||
|
+
|
||
|
+out:
|
||
|
+ if (rc != 0 && conn != NULL) {
|
||
|
+ admin_hangup(conn);
|
||
|
+ rc = 0; /* Don't return an error, we have already handled it */
|
||
|
+ }
|
||
|
+
|
||
|
+ return rc;
|
||
|
+}
|
||
|
+
|
||
|
+static int admin_xfer_complete(void *client)
|
||
|
+{
|
||
|
+ struct admin_conn_info *conn = client;
|
||
|
+ int rc;
|
||
|
+
|
||
|
+ DbgLog(DL0, "%s: Xfer completed: admin: %p socket: %d state: %d",
|
||
|
+ __func__, conn, conn->client_info.socket, conn->state);
|
||
|
+
|
||
|
+ /*
|
||
|
+ * A non-zero return code returned by this function causes the caller to
|
||
|
+ * call admin_hangup(). Thus, no need to call admin_hangup() ourselves.
|
||
|
+ */
|
||
|
+
|
||
|
+ if (conn->event == NULL) {
|
||
|
+ TraceLog("%s: No current event", __func__);
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ switch (conn->state) {
|
||
|
+ case ADMIN_RECEIVE_EVENT:
|
||
|
+ /* We have received the event from the admin */
|
||
|
+ DbgLog(DL3, "%s: Event version: %u", __func__,
|
||
|
+ conn->event->event.version);
|
||
|
+ DbgLog(DL3, "%s: Event type: 0x%08x", __func__,
|
||
|
+ conn->event->event.type);
|
||
|
+ DbgLog(DL3, "%s: Event flags: 0x%08x", __func__,
|
||
|
+ conn->event->event.flags);
|
||
|
+ DbgLog(DL3, "%s: Event token_type: 0x%08x", __func__,
|
||
|
+ conn->event->event.token_type);
|
||
|
+ DbgLog(DL3, "%s: Event token_name: '%.32s'", __func__,
|
||
|
+ conn->event->event.token_label);
|
||
|
+ DbgLog(DL3, "%s: Event process_id: %u", __func__,
|
||
|
+ conn->event->event.process_id);
|
||
|
+ DbgLog(DL3, "%s: Event payload_len: %u", __func__,
|
||
|
+ conn->event->event.payload_len);
|
||
|
+
|
||
|
+ if (conn->event->event.version != EVENT_VERSION_1) {
|
||
|
+ InfoLog("%s: Admin event has invalid version: %d", __func__,
|
||
|
+ conn->event->event.version);
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+ if (conn->event->event.payload_len > EVENT_MAX_PAYLOAD_LENGTH) {
|
||
|
+ InfoLog("%s: Admin event payload is too large: %u", __func__,
|
||
|
+ conn->event->event.payload_len);
|
||
|
+ return -EMSGSIZE;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (conn->event->event.payload_len > 0) {
|
||
|
+ conn->event->payload = malloc(conn->event->event.payload_len);
|
||
|
+ if (conn->event->payload == NULL) {
|
||
|
+ ErrLog("%s: Failed to allocate the payload buffer", __func__);
|
||
|
+ return -ENOMEM;
|
||
|
+ }
|
||
|
+
|
||
|
+ conn->state = ADMIN_RECEIVE_PAYLOAD;
|
||
|
+ rc = client_socket_receive(&conn->client_info, conn->event->payload,
|
||
|
+ conn->event->event.payload_len);
|
||
|
+ conn = NULL; /* conn may have been freed by now */
|
||
|
+ return rc;
|
||
|
+ }
|
||
|
+ FALL_THROUGH;
|
||
|
+ /* fall through */
|
||
|
+
|
||
|
+ case ADMIN_RECEIVE_PAYLOAD:
|
||
|
+ /* We have received the payload (if any) from the admin */
|
||
|
+ conn->state = ADMIN_EVENT_DELIVERED;
|
||
|
+ rc = event_start_deliver(conn->event);
|
||
|
+ if (rc != 0) {
|
||
|
+ if (rc == -ENOSPC) {
|
||
|
+ /* Event limit reached, delay */
|
||
|
+ conn->state = ADMIN_WAIT_FOR_EVENT_LIMIT;
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+ return rc;
|
||
|
+ }
|
||
|
+ break;
|
||
|
+
|
||
|
+ case ADMIN_WAIT_FOR_EVENT_LIMIT:
|
||
|
+ /* This state is handled in admin_event_limit_underrun() */
|
||
|
+ break;
|
||
|
+
|
||
|
+ case ADMIN_EVENT_DELIVERED:
|
||
|
+ /* This state is handled in admin_event_delivered() */
|
||
|
+ break;
|
||
|
+
|
||
|
+ case ADMIN_SEND_REPLY:
|
||
|
+ /* The reply has been sent to the admin */
|
||
|
+ if (conn->event->admin_ref != NULL)
|
||
|
+ admin_put(conn->event->admin_ref);
|
||
|
+ conn->event->admin_ref = NULL;
|
||
|
+ event_free(conn->event);
|
||
|
+
|
||
|
+ conn->event = event_new(0, conn);
|
||
|
+ if (conn->event == NULL) {
|
||
|
+ ErrLog("%s: Failed to allocate a new event", __func__);
|
||
|
+ return -ENOMEM;
|
||
|
+ }
|
||
|
+
|
||
|
+ conn->state = ADMIN_RECEIVE_EVENT;
|
||
|
+ rc = client_socket_receive(&conn->client_info, &conn->event->event,
|
||
|
+ sizeof(conn->event->event));
|
||
|
+ conn = NULL; /* conn may have been freed by now */
|
||
|
+ return rc;
|
||
|
+
|
||
|
+ case ADMIN_HANGUP:
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static void admin_event_limit_underrun(struct admin_conn_info *conn)
|
||
|
+{
|
||
|
+ int rc;
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: admin: %p state: %d", __func__, conn, conn->state);
|
||
|
+
|
||
|
+ if (conn->state != ADMIN_WAIT_FOR_EVENT_LIMIT)
|
||
|
+ return;
|
||
|
+
|
||
|
+ conn->state = ADMIN_EVENT_DELIVERED;
|
||
|
+
|
||
|
+ rc = event_start_deliver(conn->event);
|
||
|
+ if (rc != 0) {
|
||
|
+ if (rc == -ENOSPC) {
|
||
|
+ /* Event limit reached, delay */
|
||
|
+ conn->state = ADMIN_WAIT_FOR_EVENT_LIMIT;
|
||
|
+ return;
|
||
|
+ }
|
||
|
+ admin_hangup(conn);
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static int admin_event_delivered(struct admin_conn_info *conn,
|
||
|
+ struct event_info *event)
|
||
|
+{
|
||
|
+ int rc;
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: admin: %p event: %p", __func__, conn, event);
|
||
|
+
|
||
|
+ /*
|
||
|
+ * A non-zero return code returned by this function causes the caller to
|
||
|
+ * call admin_hangup(). Thus, no need to call admin_hangup() ourselves.
|
||
|
+ */
|
||
|
+
|
||
|
+ if (conn->state != ADMIN_EVENT_DELIVERED) {
|
||
|
+ TraceLog("%s: wrong state: %d", __func__, conn->state);
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (event->event.flags & EVENT_FLAGS_REPLY_REQ) {
|
||
|
+ if (conn->event != event) {
|
||
|
+ TraceLog("%s: event not the current event", __func__);
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: Reply version: %u", __func__,
|
||
|
+ event->reply.version);
|
||
|
+ DbgLog(DL3, "%s: Reply positive: %lu", __func__,
|
||
|
+ event->reply.positive_replies);
|
||
|
+ DbgLog(DL3, "%s: Reply negative: %lu", __func__,
|
||
|
+ event->reply.negative_replies);
|
||
|
+ DbgLog(DL3, "%s: Reply not-handled: %lu", __func__,
|
||
|
+ event->reply.nothandled_replies);
|
||
|
+
|
||
|
+ conn->state = ADMIN_SEND_REPLY;
|
||
|
+ rc = client_socket_send(&conn->client_info, &event->reply,
|
||
|
+ sizeof(event->reply));
|
||
|
+ return rc;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* No reply required, free the event, and receive the next one */
|
||
|
+ if (event->admin_ref != NULL)
|
||
|
+ admin_put(event->admin_ref);
|
||
|
+ event->admin_ref = NULL;
|
||
|
+ event_free(event);
|
||
|
+
|
||
|
+ conn->event = event_new(0, conn);
|
||
|
+ if (conn->event == NULL) {
|
||
|
+ ErrLog("%s: Failed to allocate a new event", __func__);
|
||
|
+ return -ENOMEM;
|
||
|
+ }
|
||
|
+
|
||
|
+ conn->state = ADMIN_RECEIVE_EVENT;
|
||
|
+ rc = client_socket_receive(&conn->client_info, &conn->event->event,
|
||
|
+ sizeof(conn->event->event));
|
||
|
+ return rc;
|
||
|
+}
|
||
|
+
|
||
|
+static inline void admin_get(struct admin_conn_info *conn)
|
||
|
+{
|
||
|
+ client_socket_get(&conn->client_info);
|
||
|
+}
|
||
|
+
|
||
|
+static inline void admin_put(struct admin_conn_info *conn)
|
||
|
+{
|
||
|
+ client_socket_put(&conn->client_info);
|
||
|
+}
|
||
|
+
|
||
|
+static void admin_hangup(void *client)
|
||
|
+{
|
||
|
+ struct admin_conn_info *conn = client;
|
||
|
+
|
||
|
+ DbgLog(DL0, "%s: admin: %p socket: %d state: %d", __func__, conn,
|
||
|
+ conn->client_info.socket, conn->state);
|
||
|
+
|
||
|
+ if (conn->state == ADMIN_HANGUP)
|
||
|
+ return;
|
||
|
+ conn->state = ADMIN_HANGUP;
|
||
|
+
|
||
|
+ /* Unlink pending event (if any) */
|
||
|
+ if (conn->event != NULL) {
|
||
|
+ if (conn->event->admin_ref != NULL)
|
||
|
+ admin_put(conn->event->admin_ref);
|
||
|
+ conn->event->admin_ref = NULL;
|
||
|
+ if (conn->event->proc_ref_count == 0) {
|
||
|
+ event_remove_from_pending_list(conn->event);
|
||
|
+ event_free(conn->event);
|
||
|
+ }
|
||
|
+ conn->event = NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ client_socket_term(&conn->client_info);
|
||
|
+ admin_put(conn);
|
||
|
+}
|
||
|
+
|
||
|
+static void admin_free(void *client)
|
||
|
+{
|
||
|
+ struct admin_conn_info *conn = client;
|
||
|
+ DL_NODE *node;
|
||
|
|
||
|
-int proc_listener_socket = -1;
|
||
|
+ /* Remove it from the admin connections list */
|
||
|
+ node = dlist_find(admin_connections, conn);
|
||
|
+ if (node != NULL) {
|
||
|
+ admin_connections = dlist_remove_node(admin_connections, node);
|
||
|
+ listener_client_hangup(&admin_listener);
|
||
|
+ }
|
||
|
|
||
|
-static void close_listener_socket(int socketfd, const char *file_path);
|
||
|
+ DbgLog(DL0, "%s: admin: %p", __func__, conn);
|
||
|
+ free(conn);
|
||
|
+}
|
||
|
|
||
|
-// Creates the daemon's listener socket, to which clients will connect and
|
||
|
-// retrieve slot information through. Returns the file descriptor of the
|
||
|
-// created socket.
|
||
|
-static int create_listener_socket(const char *file_path)
|
||
|
+static int listener_socket_create(const char *file_path)
|
||
|
{
|
||
|
struct sockaddr_un address;
|
||
|
struct group *grp;
|
||
|
- int socketfd;
|
||
|
+ int listener_socket, err;
|
||
|
|
||
|
- socketfd = socket(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
|
||
|
- if (socketfd < 0) {
|
||
|
- ErrLog("Failed to create listener socket, errno 0x%X.", errno);
|
||
|
+ listener_socket = socket(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
|
||
|
+ if (listener_socket < 0) {
|
||
|
+ err = errno;
|
||
|
+ ErrLog("%s: Failed to create listener socket, errno %d (%s).",
|
||
|
+ __func__, err, strerror(err));
|
||
|
return -1;
|
||
|
}
|
||
|
if (unlink(file_path) && errno != ENOENT) {
|
||
|
- ErrLog("Failed to unlink socket file, errno 0x%X.", errno);
|
||
|
+ err = errno;
|
||
|
+ ErrLog("%s: Failed to unlink socket file, errno %d (%s).", __func__,
|
||
|
+ err, strerror(err));
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
@@ -52,50 +1249,389 @@ static int create_listener_socket(const char *file_path)
|
||
|
address.sun_family = AF_UNIX;
|
||
|
strcpy(address.sun_path, file_path);
|
||
|
|
||
|
- if (bind(socketfd,
|
||
|
+ if (bind(listener_socket,
|
||
|
(struct sockaddr *) &address, sizeof(struct sockaddr_un)) != 0) {
|
||
|
- ErrLog("Failed to bind to socket, errno 0x%X.", errno);
|
||
|
+ err = errno;
|
||
|
+ ErrLog("%s: Failed to bind to socket, errno %d (%s).", __func__, err,
|
||
|
+ strerror(err));
|
||
|
goto error;
|
||
|
}
|
||
|
// make socket file part of the pkcs11 group, and write accessable
|
||
|
// for that group
|
||
|
grp = getgrnam("pkcs11");
|
||
|
if (!grp) {
|
||
|
- ErrLog("Group PKCS#11 does not exist");
|
||
|
+ ErrLog("%s: Group PKCS#11 does not exist", __func__);
|
||
|
goto error;
|
||
|
}
|
||
|
if (chown(file_path, 0, grp->gr_gid)) {
|
||
|
- ErrLog("Could not change file group on socket, errno 0x%X.", errno);
|
||
|
+ err = errno;
|
||
|
+ ErrLog("%s: Could not change file group on socket, errno %d (%s).",
|
||
|
+ __func__, err, strerror(err));
|
||
|
goto error;
|
||
|
}
|
||
|
if (chmod(file_path,
|
||
|
- S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP | S_IXUSR | S_IXGRP)) {
|
||
|
- ErrLog("Could not change file permissions on socket, errno 0x%X.",
|
||
|
- errno);
|
||
|
+ S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP)) {
|
||
|
+ err = errno;
|
||
|
+ ErrLog("%s: Could not change file permissions on socket, errno %d (%s).",
|
||
|
+ __func__, err, strerror(err));
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
- if (listen(socketfd, 20) != 0) {
|
||
|
- ErrLog("Failed to listen to socket, errno 0x%X.", errno);
|
||
|
+ if (listen(listener_socket, 20) != 0) {
|
||
|
+ err = errno;
|
||
|
+ ErrLog("%s: Failed to listen to socket, errno %d (%s).", __func__, err,
|
||
|
+ strerror(err));
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
- return socketfd;
|
||
|
+ return listener_socket;
|
||
|
|
||
|
error:
|
||
|
- if (socketfd >= 0)
|
||
|
- close_listener_socket(socketfd, file_path);
|
||
|
+ if (listener_socket >= 0)
|
||
|
+ listener_socket_close(listener_socket, file_path);
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
-static void close_listener_socket(int socketfd, const char *file_path)
|
||
|
+static void listener_socket_close(int listener_socket, const char *file_path)
|
||
|
{
|
||
|
- close(socketfd);
|
||
|
+ close(listener_socket);
|
||
|
unlink(file_path);
|
||
|
}
|
||
|
|
||
|
+
|
||
|
+
|
||
|
+static int listener_notify(int events, void *private)
|
||
|
+{
|
||
|
+ struct listener_info *listener = private;
|
||
|
+ struct sockaddr_un address;
|
||
|
+ socklen_t address_length = sizeof(address);
|
||
|
+ int client_socket, rc, err;
|
||
|
+
|
||
|
+ if ((events & EPOLLIN) == 0)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ /* epoll is edge triggered. We must call accept until we get EWOULDBLOCK */
|
||
|
+ while (listener->num_clients < listener->max_num_clients) {
|
||
|
+ client_socket = accept(listener->socket, (struct sockaddr *) &address,
|
||
|
+ &address_length);
|
||
|
+ if (client_socket < 0) {
|
||
|
+ err = errno;
|
||
|
+ if (err == EWOULDBLOCK)
|
||
|
+ break;
|
||
|
+ InfoLog("%s: Failed to accept connection on socket %d, errno %d (%s).",
|
||
|
+ __func__, listener->socket, err, strerror(err));
|
||
|
+ return -err;
|
||
|
+ }
|
||
|
+
|
||
|
+ rc = listener->new_conn(client_socket, listener);
|
||
|
+ if (rc != 0) {
|
||
|
+ TraceLog("%s: new_conn callback failed for client socket %d, rc: %d",
|
||
|
+ __func__, client_socket, rc);
|
||
|
+ close(client_socket);
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+
|
||
|
+ listener->num_clients++;
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int listener_client_hangup(struct listener_info *listener)
|
||
|
+{
|
||
|
+ int rc, trigger = 0;
|
||
|
+
|
||
|
+ if (listener->num_clients >= listener->max_num_clients)
|
||
|
+ trigger = 1; /* We were at max clients, trigger accept now */
|
||
|
+
|
||
|
+ if (listener->num_clients > 0)
|
||
|
+ listener->num_clients--;
|
||
|
+
|
||
|
+ if (trigger && listener->num_clients < listener->max_num_clients) {
|
||
|
+ rc = listener_notify(EPOLLIN, listener);
|
||
|
+ if (rc != 0)
|
||
|
+ return rc;
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int listener_create(const char *file_path,
|
||
|
+ struct listener_info *listener,
|
||
|
+ int (* new_conn)(int socket,
|
||
|
+ struct listener_info *listener),
|
||
|
+ unsigned long max_num_clients)
|
||
|
+{
|
||
|
+ struct epoll_event evt;
|
||
|
+ int rc, err;
|
||
|
+
|
||
|
+ if (listener == NULL || new_conn == NULL)
|
||
|
+ return FALSE;
|
||
|
+
|
||
|
+ memset(listener, 0, sizeof(*listener));
|
||
|
+ epoll_info_init(&listener->ep_info, listener_notify, NULL, listener);
|
||
|
+ listener->file_path = file_path;
|
||
|
+ listener->new_conn = new_conn;
|
||
|
+ listener->max_num_clients = max_num_clients;
|
||
|
+
|
||
|
+ listener->socket = listener_socket_create(file_path);
|
||
|
+ if (listener->socket < 0)
|
||
|
+ return FALSE;
|
||
|
+
|
||
|
+ evt.events = EPOLLIN | EPOLLET;
|
||
|
+ evt.data.ptr = &listener->ep_info;
|
||
|
+ rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listener->socket, &evt);
|
||
|
+ if (rc != 0) {
|
||
|
+ err = errno;
|
||
|
+ TraceLog("%s: Failed add listener socket %d to epoll, errno %d (%s).",
|
||
|
+ __func__, listener->socket, err, strerror(err));
|
||
|
+ return FALSE;
|
||
|
+ }
|
||
|
+
|
||
|
+ return TRUE;
|
||
|
+}
|
||
|
+
|
||
|
+static void listener_term(struct listener_info *listener)
|
||
|
+{
|
||
|
+ if (listener == NULL || listener->socket < 0)
|
||
|
+ return;
|
||
|
+
|
||
|
+ epoll_ctl(epoll_fd, EPOLL_CTL_DEL, listener->socket, NULL);
|
||
|
+ listener_socket_close(listener->socket, listener->file_path);
|
||
|
+}
|
||
|
+
|
||
|
+#ifdef WITH_LIBUDEV
|
||
|
+
|
||
|
+static int udev_mon_init(const char *subsystem, struct udev_mon *udev_mon)
|
||
|
+{
|
||
|
+ struct epoll_event evt;
|
||
|
+ int rc, err;
|
||
|
+
|
||
|
+ if (subsystem == NULL || udev_mon == NULL)
|
||
|
+ return FALSE;
|
||
|
+
|
||
|
+ udev_mon->delayed_event = 0;
|
||
|
+
|
||
|
+ udev_mon->udev = udev_new();
|
||
|
+ if (udev_mon->udev == NULL) {
|
||
|
+ ErrLog("%s: udev_new failed", __func__);
|
||
|
+ goto error;
|
||
|
+ }
|
||
|
+
|
||
|
+ udev_mon->mon = udev_monitor_new_from_netlink(udev_mon->udev, "udev");
|
||
|
+ if (udev_mon->mon == NULL) {
|
||
|
+ ErrLog("%s: udev_monitor_new_from_netlink failed", __func__);
|
||
|
+ goto error;
|
||
|
+ }
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Try to increase the receive buffer size. This may fail if the required
|
||
|
+ * privileges are not given. Ignore if it fails.
|
||
|
+ */
|
||
|
+ udev_monitor_set_receive_buffer_size(udev_mon->mon, UDEV_RECV_BUFFFER_SIZE);
|
||
|
+
|
||
|
+ rc = udev_monitor_filter_add_match_subsystem_devtype(udev_mon->mon,
|
||
|
+ subsystem, NULL);
|
||
|
+ if (rc != 0) {
|
||
|
+ ErrLog("%s: udev_monitor_filter_add_match_subsystem_devtype failed: "
|
||
|
+ "rc=%d", __func__, rc);
|
||
|
+ goto error;
|
||
|
+ }
|
||
|
+
|
||
|
+ rc = udev_monitor_enable_receiving(udev_mon->mon);
|
||
|
+ if (rc != 0) {
|
||
|
+ ErrLog("%s: udev_monitor_enable_receiving failed: rc=%d", __func__, rc);
|
||
|
+ goto error;
|
||
|
+ }
|
||
|
+
|
||
|
+ udev_mon->socket = udev_monitor_get_fd(udev_mon->mon);
|
||
|
+ if (udev_mon->socket < 0) {
|
||
|
+ ErrLog("%s: udev_monitor_get_fd failed", __func__);
|
||
|
+ goto error;
|
||
|
+ }
|
||
|
+
|
||
|
+ epoll_info_init(&udev_mon->ep_info, udev_mon_notify, NULL, udev_mon);
|
||
|
+
|
||
|
+ evt.events = EPOLLIN | EPOLLET;
|
||
|
+ evt.data.ptr = &udev_mon->ep_info;
|
||
|
+ rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, udev_mon->socket, &evt);
|
||
|
+ if (rc != 0) {
|
||
|
+ err = errno;
|
||
|
+ ErrLog("%s: Failed add udev_mon socket %d to epoll, errno %d (%s).",
|
||
|
+ __func__, udev_mon->socket, err, strerror(err));
|
||
|
+ goto error;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* Epoll is edge triggered, thus try to receive once */
|
||
|
+ rc = udev_mon_notify(EPOLLIN, udev_mon);
|
||
|
+ if (rc != 0)
|
||
|
+ goto error;
|
||
|
+
|
||
|
+ return TRUE;
|
||
|
+
|
||
|
+error:
|
||
|
+ udev_mon_term(udev_mon);
|
||
|
+ return FALSE;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+static int udev_mon_handle_device(struct udev_mon *udev_mon,
|
||
|
+ struct udev_device *dev)
|
||
|
+{
|
||
|
+ const char *action, *devname, *devpath, *devtype, *dev_type_prop;
|
||
|
+ unsigned int card, domain, dev_type;
|
||
|
+ struct event_info *event;
|
||
|
+ event_udev_apqn_data_t *apqn_data;
|
||
|
+ int rc;
|
||
|
+
|
||
|
+ UNUSED(udev_mon);
|
||
|
+
|
||
|
+ action = udev_device_get_action(dev);
|
||
|
+ devname = udev_device_get_sysname(dev);
|
||
|
+ devpath = udev_device_get_devpath(dev);
|
||
|
+ devtype = udev_device_get_devtype(dev);
|
||
|
+ dev_type_prop = udev_device_get_property_value(dev, UDEV_PROERTY_DEVTYPE);
|
||
|
+
|
||
|
+ if (action == NULL || devname == NULL || devpath == NULL ||
|
||
|
+ devtype == NULL || dev_type_prop == NULL)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: Uevent: ACTION=%s DEVNAME=%s DEVPATH=%s DEVTYPE=%s "
|
||
|
+ "DEV_TYPE=%s", __func__, action, devname, devpath, devtype,
|
||
|
+ dev_type_prop);
|
||
|
+
|
||
|
+ /* We are only interested in bind and unbind events ... */
|
||
|
+ if (strcmp(action, UDEV_ACTION_BIND) != 0 &&
|
||
|
+ strcmp(action, UDEV_ACTION_UNBIND) != 0)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ /* ... for an APQN device */
|
||
|
+ if (strcmp(devtype, UDEV_ACTION_DEVTYPE_APQN) != 0)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ if (sscanf(devname, "%x.%x", &card, &domain) != 2) {
|
||
|
+ TraceLog("%s: failed to parse APQN from DEVNAME: %s", __func__, devname);
|
||
|
+ return -EIO;
|
||
|
+ }
|
||
|
+ if (sscanf(dev_type_prop, "%x", &dev_type) != 1) {
|
||
|
+ TraceLog("%s: failed to parse DEV_TYPE: %s", __func__, dev_type_prop);
|
||
|
+ return -EIO;
|
||
|
+ }
|
||
|
+
|
||
|
+ event = event_new(sizeof(event_udev_apqn_data_t), NULL);
|
||
|
+ if (event == NULL) {
|
||
|
+ ErrLog("%s: failed to allocate an event", __func__);
|
||
|
+ return -ENOMEM;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (strcmp(udev_device_get_action(dev), UDEV_ACTION_BIND) == 0)
|
||
|
+ event->event.type = EVENT_TYPE_APQN_ADD;
|
||
|
+ else
|
||
|
+ event->event.type = EVENT_TYPE_APQN_REMOVE;
|
||
|
+ event->event.flags = EVENT_FLAGS_NONE;
|
||
|
+ event->event.token_type = EVENT_TOK_TYPE_ALL;
|
||
|
+ memset(event->event.token_label, ' ',
|
||
|
+ sizeof(event->event.token_label));
|
||
|
+
|
||
|
+ apqn_data = (event_udev_apqn_data_t *)event->payload;
|
||
|
+ apqn_data->card = card;
|
||
|
+ apqn_data->domain = domain;
|
||
|
+ apqn_data->device_type = dev_type;
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: Event version: %u", __func__, event->event.version);
|
||
|
+ DbgLog(DL3, "%s: Event type: 0x%08x", __func__, event->event.type);
|
||
|
+ DbgLog(DL3, "%s: Event flags: 0x%08x", __func__, event->event.flags);
|
||
|
+ DbgLog(DL3, "%s: Event token_type: 0x%08x", __func__,
|
||
|
+ event->event.token_type);
|
||
|
+ DbgLog(DL3, "%s: Event token_name: '%.32s'", __func__,
|
||
|
+ event->event.token_label);
|
||
|
+ DbgLog(DL3, "%s: Event process_id: %u", __func__, event->event.process_id);
|
||
|
+ DbgLog(DL3, "%s: Event payload_len: %u", __func__,
|
||
|
+ event->event.payload_len);
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: Payload: card: %u", __func__, apqn_data->card);
|
||
|
+ DbgLog(DL3, "%s: Payload: domain: %u", __func__, apqn_data->domain);
|
||
|
+ DbgLog(DL3, "%s: Payload: dev.type: %u", __func__, apqn_data->device_type);
|
||
|
+
|
||
|
+ rc = event_start_deliver(event);
|
||
|
+ if (rc != 0) {
|
||
|
+ if (rc == -ENOSPC) {
|
||
|
+ /* Event limit reached, delay event delivery */
|
||
|
+ udev_mon->delayed_event = event;
|
||
|
+ return -ENOSPC;
|
||
|
+ }
|
||
|
+ event_free(event);
|
||
|
+ return rc;
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int udev_mon_notify(int events, void *private)
|
||
|
+{
|
||
|
+ struct udev_mon *udev_mon = private;
|
||
|
+ struct udev_device *dev;
|
||
|
+ struct event_info *event;
|
||
|
+ int rc;
|
||
|
+
|
||
|
+ DbgLog(DL3, "%s: Epoll event on udev_mon socket %d: events: 0x%x",
|
||
|
+ __func__, udev_mon->socket, events);
|
||
|
+
|
||
|
+ if (udev_mon->delayed_event != NULL) {
|
||
|
+ /* Deliver delayed event first */
|
||
|
+ event = udev_mon->delayed_event;
|
||
|
+ udev_mon->delayed_event = NULL;
|
||
|
+
|
||
|
+ rc = event_start_deliver(event);
|
||
|
+ if (rc != 0) {
|
||
|
+ if (rc == -ENOSPC) {
|
||
|
+ /* Event limit reached, delay event delivery */
|
||
|
+ udev_mon->delayed_event = event;
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+ event_free(event);
|
||
|
+ return rc;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ while (1) {
|
||
|
+ dev = udev_monitor_receive_device(udev_mon->mon);
|
||
|
+ if (dev == NULL)
|
||
|
+ break; /* this is just like EWOULDBLOCK */
|
||
|
+
|
||
|
+ rc = udev_mon_handle_device(udev_mon, dev);
|
||
|
+ if (rc != 0)
|
||
|
+ TraceLog("%s: udev_mon_handle_device failed, rc: %d", __func__, rc);
|
||
|
+
|
||
|
+ udev_device_unref(dev);
|
||
|
+
|
||
|
+ /* If event limit reached, stop receiving more events */
|
||
|
+ if (rc == -ENOSPC)
|
||
|
+ break;
|
||
|
+ };
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static void udev_mon_term(struct udev_mon *udev_mon)
|
||
|
+{
|
||
|
+ if (udev_mon == NULL)
|
||
|
+ return;
|
||
|
+
|
||
|
+ epoll_ctl(epoll_fd, EPOLL_CTL_DEL, udev_mon->socket, NULL);
|
||
|
+ if (udev_mon->udev != NULL)
|
||
|
+ udev_unref(udev_mon->udev);
|
||
|
+ if (udev_mon->mon != NULL)
|
||
|
+ udev_monitor_unref(udev_mon->mon);
|
||
|
+
|
||
|
+ if (udev_mon->delayed_event != NULL)
|
||
|
+ event_free(udev_mon->delayed_event);
|
||
|
+}
|
||
|
+
|
||
|
+#endif
|
||
|
+
|
||
|
int init_socket_data(Slot_Mgr_Socket_t *socketData)
|
||
|
{
|
||
|
unsigned int processed = 0;
|
||
|
@@ -106,7 +1642,7 @@ int init_socket_data(Slot_Mgr_Socket_t *socketData)
|
||
|
|
||
|
/* check that we read in correct amount of slots */
|
||
|
if (processed != NumberSlotsInDB) {
|
||
|
- ErrLog("Failed to populate slot info.\n");
|
||
|
+ ErrLog("%s: Failed to populate slot info.", __func__);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
@@ -115,72 +1651,277 @@ int init_socket_data(Slot_Mgr_Socket_t *socketData)
|
||
|
|
||
|
int socket_connection_handler(int timeout_secs)
|
||
|
{
|
||
|
- int returnVal;
|
||
|
- fd_set set;
|
||
|
- struct timeval timeout;
|
||
|
-
|
||
|
- FD_ZERO(&set);
|
||
|
- FD_SET(proc_listener_socket, &set);
|
||
|
+ struct epoll_event events[MAX_EPOLL_EVENTS];
|
||
|
+ int num_events, i, rc, err;
|
||
|
+ struct epoll_info *info;
|
||
|
|
||
|
- timeout.tv_sec = timeout_secs;
|
||
|
- timeout.tv_usec = 0;
|
||
|
-
|
||
|
- returnVal = select(proc_listener_socket + 1, &set, NULL, NULL, &timeout);
|
||
|
- if (returnVal == -1) {
|
||
|
- ErrLog("select failed on socket connection, errno 0x%X.", errno);
|
||
|
- return FALSE;
|
||
|
- } else if (returnVal == 0) {
|
||
|
- // select call timed out, return
|
||
|
- return FALSE;
|
||
|
- } else {
|
||
|
- struct sockaddr_un address;
|
||
|
- socklen_t address_length = sizeof(address);
|
||
|
-
|
||
|
- int connectionfd = accept(proc_listener_socket,
|
||
|
- (struct sockaddr *) &address,
|
||
|
- &address_length);
|
||
|
- if (connectionfd < 0) {
|
||
|
- if (errno != EAGAIN && errno != EWOULDBLOCK) {
|
||
|
- /* These errors are allowed since
|
||
|
- * socket is non-blocking
|
||
|
- */
|
||
|
- ErrLog("Failed to accept socket connection, errno 0x%X.",
|
||
|
- errno);
|
||
|
- }
|
||
|
+ do {
|
||
|
+ num_events = epoll_wait(epoll_fd, events, MAX_EPOLL_EVENTS,
|
||
|
+ timeout_secs * 1000);
|
||
|
+ if (num_events < 0) {
|
||
|
+ err = errno;
|
||
|
+ if (err == EINTR)
|
||
|
+ continue;
|
||
|
+ ErrLog("%s: epoll_wait failed, errno %d (%s).", __func__, err,
|
||
|
+ strerror(err));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
- DbgLog(DL0, "Accepted connection from process: socket: %d",
|
||
|
- connectionfd);
|
||
|
+ /*
|
||
|
+ * Inc ref count of all epoll_infos returned by epoll before handling
|
||
|
+ * any of them via notify. The notify callback may hangup any of
|
||
|
+ * the connections associated with the returned epoll_infos, and we
|
||
|
+ * need to avoid them getting freed before we all handled them.
|
||
|
+ */
|
||
|
+ for (i = 0; i < num_events; i++)
|
||
|
+ epoll_info_get(events[i].data.ptr);
|
||
|
|
||
|
- if (write(connectionfd, &socketData, sizeof(socketData)) !=
|
||
|
- sizeof(socketData)) {
|
||
|
- ErrLog("Failed to write socket data, errno 0x%X.", errno);
|
||
|
- close(connectionfd);
|
||
|
- return FALSE;
|
||
|
+ for (i = 0; i < num_events; i++) {
|
||
|
+ info = events[i].data.ptr;
|
||
|
+ if (info == NULL || info->notify == NULL)
|
||
|
+ continue;
|
||
|
+
|
||
|
+ rc = info->notify(events[i].events, info->private);
|
||
|
+ if (rc != 0)
|
||
|
+ TraceLog("%s: notify callback failed, rc: %d", __func__, rc);
|
||
|
+
|
||
|
+ epoll_info_put(info);
|
||
|
}
|
||
|
- close(connectionfd);
|
||
|
- return TRUE;
|
||
|
- }
|
||
|
+ } while (num_events > 0 && rc == 0); /* num_events = 0: timeout */
|
||
|
+
|
||
|
+ return TRUE;
|
||
|
}
|
||
|
|
||
|
int init_socket_server()
|
||
|
{
|
||
|
- proc_listener_socket = create_listener_socket(PROC_SOCKET_FILE_PATH);
|
||
|
- if (proc_listener_socket < 0)
|
||
|
+ int err;
|
||
|
+
|
||
|
+ epoll_fd = epoll_create1(0);
|
||
|
+ if (epoll_fd < 0) {
|
||
|
+ err = errno;
|
||
|
+ ErrLog("%s: Failed to open epoll socket, errno %d (%s).", __func__, err,
|
||
|
+ strerror(err));
|
||
|
+ return FALSE;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!listener_create(PROC_SOCKET_FILE_PATH, &proc_listener,
|
||
|
+ proc_new_conn, NUMBER_PROCESSES_ALLOWED)) {
|
||
|
+ term_socket_server();
|
||
|
+ return FALSE;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!listener_create(ADMIN_SOCKET_FILE_PATH, &admin_listener,
|
||
|
+ admin_new_conn, NUMBER_ADMINS_ALLOWED)) {
|
||
|
+ term_socket_server();
|
||
|
return FALSE;
|
||
|
+ }
|
||
|
+
|
||
|
+#ifdef WITH_LIBUDEV
|
||
|
+ if (!udev_mon_init(UDEV_SUBSYSTEM_AP, &udev_mon)) {
|
||
|
+ term_socket_server();
|
||
|
+ return FALSE;
|
||
|
+ }
|
||
|
+#endif
|
||
|
|
||
|
- DbgLog(DL0, "Socket server started");
|
||
|
+ DbgLog(DL0, "%s: Socket server started", __func__);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
int term_socket_server()
|
||
|
{
|
||
|
- if (proc_listener_socket >= 0)
|
||
|
- close_listener_socket(proc_listener_socket, PROC_SOCKET_FILE_PATH);
|
||
|
+ DL_NODE *node, *next;
|
||
|
+
|
||
|
+#ifdef WITH_LIBUDEV
|
||
|
+ udev_mon_term(&udev_mon);
|
||
|
+#endif
|
||
|
+
|
||
|
+ listener_term(&proc_listener);
|
||
|
+ listener_term(&admin_listener);
|
||
|
+
|
||
|
+ node = dlist_get_first(proc_connections);
|
||
|
+ while (node != NULL) {
|
||
|
+ next = dlist_next(node);
|
||
|
+ proc_hangup(node->data);
|
||
|
+ node = next;
|
||
|
+ }
|
||
|
+ dlist_purge(proc_connections);
|
||
|
|
||
|
- DbgLog(DL0, "Socket server stopped");
|
||
|
+ node = dlist_get_first(admin_connections);
|
||
|
+ while (node != NULL) {
|
||
|
+ next = dlist_next(node);
|
||
|
+ admin_hangup(node->data);
|
||
|
+ node = next;
|
||
|
+ }
|
||
|
+ dlist_purge(admin_connections);
|
||
|
+
|
||
|
+ node = dlist_get_first(pending_events);
|
||
|
+ while (node != NULL) {
|
||
|
+ next = dlist_next(node);
|
||
|
+ event_free((struct event_info *)node->data);
|
||
|
+ node = next;
|
||
|
+ }
|
||
|
+ dlist_purge(pending_events);
|
||
|
+
|
||
|
+ if (epoll_fd >= 0)
|
||
|
+ close(epoll_fd);
|
||
|
+ epoll_fd = -1;
|
||
|
+
|
||
|
+ DbgLog(DL0, "%s: Socket server stopped", __func__);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
+
|
||
|
+#ifdef DEV
|
||
|
+
|
||
|
+static void dump_listener(struct listener_info *listener)
|
||
|
+{
|
||
|
+ DbgLog(DL0, " socket: %d", listener->socket);
|
||
|
+ DbgLog(DL0, " file_path: %s", listener->file_path);
|
||
|
+ DbgLog(DL0, " ep_info.ref_count: %lu", listener->ep_info.ref_count);
|
||
|
+ DbgLog(DL0, " num_clients: %lu", listener->num_clients);
|
||
|
+ DbgLog(DL0, " max_num_clients: %lu", listener->max_num_clients);
|
||
|
+}
|
||
|
+
|
||
|
+static void dump_event_msg(event_msg_t *event, int indent)
|
||
|
+{
|
||
|
+ DbgLog(DL0, "%*sevent version: %u", indent, "", event->version);
|
||
|
+ DbgLog(DL0, "%*sevent type: %08x", indent, "", event->type);
|
||
|
+ DbgLog(DL0, "%*sevent flags: %08x", indent, "", event->flags);
|
||
|
+ DbgLog(DL0, "%*sevent token_type: %08x", indent, "", event->token_type);
|
||
|
+ DbgLog(DL0, "%*sevent token_label: '%.32s'", indent, "", event->token_label);
|
||
|
+ DbgLog(DL0, "%*sevent process_id: %lu", indent, "", event->process_id);
|
||
|
+ DbgLog(DL0, "%*sevent payload_len: %u", indent, "", event->payload_len);
|
||
|
+}
|
||
|
+
|
||
|
+static void dump_event_reply(event_reply_t *reply, int indent)
|
||
|
+{
|
||
|
+ DbgLog(DL0, "%*sreply version: %u", indent, "", reply->version);
|
||
|
+ DbgLog(DL0, "%*sreply positive_replies: %u", indent, "", reply->positive_replies);
|
||
|
+ DbgLog(DL0, "%*sreply negative_replies: %u", indent, "", reply->negative_replies);
|
||
|
+ DbgLog(DL0, "%*sreply nothandled_replies: %u", indent, "", reply->nothandled_replies);
|
||
|
+}
|
||
|
+
|
||
|
+static void dump_event_info(struct event_info *event, int indent)
|
||
|
+{
|
||
|
+ dump_event_msg(&event->event, indent);
|
||
|
+ dump_event_reply(&event->reply, indent);
|
||
|
+ DbgLog(DL0, "%*sproc_ref_count: %lu", indent, "", event->proc_ref_count);
|
||
|
+ if (event->admin_ref != NULL)
|
||
|
+ DbgLog(DL0, "%*sadmin_ref: %p", indent, "", event->admin_ref);
|
||
|
+ else
|
||
|
+ DbgLog(DL0, "%*sadmin_ref: None", indent, "");
|
||
|
+}
|
||
|
+
|
||
|
+static void dump_proc_conn(struct proc_conn_info *proc_conn)
|
||
|
+{
|
||
|
+ DL_NODE *node;
|
||
|
+ unsigned long i;
|
||
|
+
|
||
|
+ DbgLog(DL0, " socket: %d", proc_conn->client_info.socket);
|
||
|
+ DbgLog(DL0, " state: %d", proc_conn->state);
|
||
|
+ DbgLog(DL0, " ref-count: %lu", proc_conn->client_info.ep_info.ref_count);
|
||
|
+ DbgLog(DL0, " xfer state: %d", proc_conn->client_info.xfer_state);
|
||
|
+ DbgLog(DL0, " xfer size: %d", proc_conn->client_info.xfer_size);
|
||
|
+ DbgLog(DL0, " xfer offset: %d", proc_conn->client_info.xfer_offset);
|
||
|
+ DbgLog(DL0, " pending events:");
|
||
|
+ node = dlist_get_first(proc_conn->events);
|
||
|
+ i = 1;
|
||
|
+ while (node != NULL) {
|
||
|
+ DbgLog(DL0, " event %lu (%p):", i, node->data);
|
||
|
+ dump_event_info(node->data, 10);
|
||
|
+ node = dlist_next(node);
|
||
|
+ i++;
|
||
|
+ }
|
||
|
+ if (proc_conn->event != NULL) {
|
||
|
+ DbgLog(DL0, " current event:");
|
||
|
+ dump_event_info(proc_conn->event, 8);
|
||
|
+ DbgLog(DL0, " current reply:");
|
||
|
+ dump_event_reply(&proc_conn->reply, 8);
|
||
|
+ } else {
|
||
|
+ DbgLog(DL0, " current event: none");
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+static void dump_admin_conn(struct admin_conn_info *admin_conn)
|
||
|
+{
|
||
|
+ DbgLog(DL0, " socket: %d", admin_conn->client_info.socket);
|
||
|
+ DbgLog(DL0, " state: %d", admin_conn->state);
|
||
|
+ DbgLog(DL0, " ref-count: %lu", admin_conn->client_info.ep_info.ref_count);
|
||
|
+ DbgLog(DL0, " xfer state: %d", admin_conn->client_info.xfer_state);
|
||
|
+ DbgLog(DL0, " xfer size: %d", admin_conn->client_info.xfer_size);
|
||
|
+ DbgLog(DL0, " xfer offset: %d", admin_conn->client_info.xfer_offset);
|
||
|
+ if (admin_conn->event != NULL) {
|
||
|
+ DbgLog(DL0, " current event (%p):", admin_conn->event);
|
||
|
+ dump_event_info(admin_conn->event, 8);
|
||
|
+ } else {
|
||
|
+ DbgLog(DL0, " current event: none");
|
||
|
+ }
|
||
|
+}
|
||
|
+
|
||
|
+#ifdef WITH_LIBUDEV
|
||
|
+void dump_udev_mon(struct udev_mon *udev_mon)
|
||
|
+{
|
||
|
+ DbgLog(DL0, " socket: %d", udev_mon->socket);
|
||
|
+ DbgLog(DL0, " udev: %p", udev_mon->udev);
|
||
|
+ DbgLog(DL0, " mon: %p", udev_mon->mon);
|
||
|
+ DbgLog(DL0, " ep_info.ref_count: %lu", udev_mon->ep_info.ref_count);
|
||
|
+ if (udev_mon->delayed_event != NULL) {
|
||
|
+ DbgLog(DL0, " delayed_event (%p):", udev_mon->delayed_event);
|
||
|
+ dump_event_info(udev_mon->delayed_event, 6);
|
||
|
+ } else {
|
||
|
+ DbgLog(DL0, " delayed_event: node");
|
||
|
+ }
|
||
|
+}
|
||
|
+#endif
|
||
|
+
|
||
|
+void dump_socket_handler()
|
||
|
+{
|
||
|
+ DL_NODE *node;
|
||
|
+ unsigned long i;
|
||
|
+
|
||
|
+ DbgLog(DL0, "%s: Dump of socket handler data:", __func__);
|
||
|
+ DbgLog(DL0, " epoll_fd: %d", epoll_fd);
|
||
|
+
|
||
|
+ DbgLog(DL0, " proc_listener (%p): ", &proc_listener);
|
||
|
+ dump_listener(&proc_listener);
|
||
|
+
|
||
|
+ DbgLog(DL0, " proc_connections: ");
|
||
|
+ node = dlist_get_first(proc_connections);
|
||
|
+ i = 1;
|
||
|
+ while (node != NULL) {
|
||
|
+ DbgLog(DL0, " proc_connection %lu (%p): ", i, node->data);
|
||
|
+ dump_proc_conn(node->data);
|
||
|
+ i++;
|
||
|
+ node = dlist_next(node);
|
||
|
+ }
|
||
|
+
|
||
|
+ DbgLog(DL0, " admin_listener (%p): ", &admin_listener);
|
||
|
+ dump_listener(&admin_listener);
|
||
|
+
|
||
|
+ DbgLog(DL0, " admin_connections: ");
|
||
|
+ node = dlist_get_first(admin_connections);
|
||
|
+ i = 1;
|
||
|
+ while (node != NULL) {
|
||
|
+ DbgLog(DL0, " admin_connection %lu (%p): ", i, node->data);
|
||
|
+ dump_admin_conn(node->data);
|
||
|
+ i++;
|
||
|
+ node = dlist_next(node);
|
||
|
+ }
|
||
|
+
|
||
|
+#ifdef WITH_LIBUDEV
|
||
|
+ DbgLog(DL0, " udev_mon (%p): ", &udev_mon);
|
||
|
+ dump_udev_mon(&udev_mon);
|
||
|
+#endif
|
||
|
+
|
||
|
+ DbgLog(DL0, " pending events (%lu): ", pending_events_count);
|
||
|
+ node = dlist_get_first(pending_events);
|
||
|
+ i = 1;
|
||
|
+ while (node != NULL) {
|
||
|
+ DbgLog(DL0, " event %lu (%p): ", i, node->data);
|
||
|
+ dump_event_info(node->data, 6);
|
||
|
+ i++;
|
||
|
+ node = dlist_next(node);
|
||
|
+ }
|
||
|
+}
|
||
|
+#endif
|