]> git.proxmox.com Git - mirror_corosync-qdevice.git/blobdiff - qdevices/qdevice-net-socket.c
qdevice: Port qdevice to use pr-poll-loop
[mirror_corosync-qdevice.git] / qdevices / qdevice-net-socket.c
index be407239b8884b8da1c0b496c8d44c54d634a17b..1b3d31ecea2e81f35bea96e03e461f9d4be47cf2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2019 Red Hat, Inc.
+ * Copyright (c) 2015-2020 Red Hat, Inc.
  *
  * All rights reserved.
  *
 #include "qdevice-net-send.h"
 #include "qdevice-net-socket.h"
 
+/*
+ * Socket callbacks
+ */
+static int
+socket_set_events_cb(PRFileDesc *prfd, short *events, void *user_data1, void *user_data2)
+{
+       struct qdevice_net_instance *instance = (struct qdevice_net_instance *)user_data1;
+
+       if (!send_buffer_list_empty(&instance->send_buffer_list)) {
+               *events |= POLLOUT;
+       }
+
+       return (0);
+}
+
+static int
+socket_read_cb(PRFileDesc *prfd, const PRPollDesc *pd, void *user_data1, void *user_data2)
+{
+       struct qdevice_net_instance *instance = (struct qdevice_net_instance *)user_data1;
+
+       if (qdevice_net_socket_read(instance) == -1) {
+               instance->schedule_disconnect = 1;
+
+               return (-1);
+       }
+
+       return (0);
+}
+
+static int
+socket_write_cb(PRFileDesc *prfd, const PRPollDesc *pd, void *user_data1, void *user_data2)
+{
+       struct qdevice_net_instance *instance = (struct qdevice_net_instance *)user_data1;
+
+       if (qdevice_net_socket_write(instance) == -1) {
+               instance->schedule_disconnect = 1;
+
+               return (-1);
+       }
+
+       return (0);
+}
+
+static int
+non_blocking_client_socket_write_cb(PRFileDesc *prfd, const PRPollDesc *pd, void *user_data1,
+    void *user_data2)
+{
+       int res;
+
+       struct qdevice_net_instance *instance = (struct qdevice_net_instance *)user_data1;
+
+       res = nss_sock_non_blocking_client_succeeded(pd);
+       if (res == -1) {
+               /*
+                * Connect failed -> remove this fd from main loop and try next
+                */
+               res = qdevice_net_socket_del_from_main_poll_loop(instance);
+               if (res == -1) {
+                       return (-1);
+               }
+
+               res = nss_sock_non_blocking_client_try_next(&instance->non_blocking_client);
+               if (res == -1) {
+                       log_nss(LOG_ERR, "Can't connect to qnetd host.");
+                       nss_sock_non_blocking_client_destroy(&instance->non_blocking_client);
+               }
+
+               res = qdevice_net_socket_add_to_main_poll_loop(instance);
+               if (res == -1) {
+                       return (-1);
+               }
+       } else if (res == 0) {
+               /*
+                * Poll again
+                */
+       } else if (res == 1) {
+               /*
+                * Connect success -> delete socket from main loop and add final one
+                */
+               res = qdevice_net_socket_del_from_main_poll_loop(instance);
+               if (res == -1) {
+                       return (-1);
+               }
+
+               instance->socket = instance->non_blocking_client.socket;
+               nss_sock_non_blocking_client_destroy(&instance->non_blocking_client);
+               instance->non_blocking_client.socket = NULL;
+
+               instance->state = QDEVICE_NET_INSTANCE_STATE_SENDING_PREINIT_REPLY;
+
+               res = qdevice_net_socket_add_to_main_poll_loop(instance);
+               if (res == -1) {
+                       return (-1);
+               }
+
+               log(LOG_DEBUG, "Sending preinit msg to qnetd");
+               if (qdevice_net_send_preinit(instance) != 0) {
+                       instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_CANT_ALLOCATE_MSG_BUFFER;
+                       return (-1);
+               }
+       } else {
+               log(LOG_CRIT, "Unhandled nss_sock_non_blocking_client_succeeded");
+               exit(EXIT_FAILURE);
+       }
+
+       return (0);
+}
+
+static int
+socket_err_cb(PRFileDesc *prfd, short revents, const PRPollDesc *pd, void *user_data1, void *user_data2)
+{
+       struct qdevice_net_instance *instance = (struct qdevice_net_instance *)user_data1;
+
+       log(LOG_ERR, "POLL_ERR (%u) on main socket", revents);
+
+       instance->schedule_disconnect = 1;
+       instance->disconnect_reason = QDEVICE_NET_DISCONNECT_REASON_SERVER_CLOSED_CONNECTION;
+
+       return (-1);
+}
+
+static int
+non_blocking_client_socket_err_cb(PRFileDesc *prfd, short revents, const PRPollDesc *pd,
+    void *user_data1, void *user_data2)
+{
+       struct qdevice_net_instance *instance = (struct qdevice_net_instance *)user_data1;
+
+       /*
+        * Workaround for RHEL<7. Pollout is never set for nonblocking connect (doesn't work
+        * only with poll, select works as expected!???).
+        * So test if client is still valid and if pollout was not already called (ensured
+        * by default because of order in PR_Poll).
+        * If both applies it's possible to emulate pollout set by calling poll_write.
+        */
+       if (!instance->non_blocking_client.destroyed) {
+               return (non_blocking_client_socket_write_cb(prfd, pd, user_data1, user_data2));
+       }
+
+       return (0);
+}
+/*
+ * Exported functions
+ */
+
 /*
  * -1 means end of connection (EOF) or some other unhandled error. 0 = success
  */
@@ -114,7 +258,7 @@ qdevice_net_socket_read(struct qdevice_net_instance *instance)
                        }
                } else {
                        log(LOG_CRIT, "net_socket_read in skipping msg state");
-                       exit(1);
+                       exit(EXIT_FAILURE);
                }
 
                instance->skipping_msg = 0;
@@ -123,7 +267,7 @@ qdevice_net_socket_read(struct qdevice_net_instance *instance)
                break;
        default:
                log(LOG_CRIT, "qdevice_net_socket_read unhandled error %d", res);
-               exit(1);
+               exit(EXIT_FAILURE);
                break;
        }
 
@@ -211,3 +355,63 @@ qdevice_net_socket_write(struct qdevice_net_instance *instance)
 
        return (0);
 }
+
+int
+qdevice_net_socket_add_to_main_poll_loop(struct qdevice_net_instance *instance)
+{
+
+       if (instance->state != QDEVICE_NET_INSTANCE_STATE_WAITING_CONNECT ||
+           !instance->non_blocking_client.destroyed) {
+               if (instance->state == QDEVICE_NET_INSTANCE_STATE_WAITING_CONNECT) {
+                       if (pr_poll_loop_add_prfd(&instance->qdevice_instance_ptr->main_poll_loop,
+                           instance->non_blocking_client.socket,
+                           POLLOUT|POLLPRI,
+                           NULL, NULL, non_blocking_client_socket_write_cb,
+                           non_blocking_client_socket_err_cb,
+                           instance, NULL) != 0) {
+                               log(LOG_ERR, "Can't add net socket (non_blocking_client) "
+                                   "fd to main poll loop");
+
+                               return (-1);
+                       }
+               } else {
+                       if (pr_poll_loop_add_prfd(&instance->qdevice_instance_ptr->main_poll_loop,
+                           instance->socket,
+                           POLLIN,
+                           socket_set_events_cb, socket_read_cb, socket_write_cb, socket_err_cb,
+                           instance, NULL) != 0) {
+                               log(LOG_ERR, "Can't add net socket fd to main poll loop");
+
+                               return (-1);
+                       }
+               }
+       }
+
+       return (0);
+}
+
+int
+qdevice_net_socket_del_from_main_poll_loop(struct qdevice_net_instance *instance)
+{
+
+       if (!instance->non_blocking_client.destroyed) {
+               if (pr_poll_loop_del_prfd(&instance->qdevice_instance_ptr->main_poll_loop,
+                   instance->non_blocking_client.socket) != 0) {
+                       log(LOG_ERR, "Can't remove net socket (non_blocking_client) "
+                           "fd from main poll loop");
+
+                       return (-1);
+               }
+       }
+
+       if (instance->socket != NULL) {
+               if (pr_poll_loop_del_prfd(&instance->qdevice_instance_ptr->main_poll_loop,
+                   instance->socket) != 0) {
+                       log(LOG_ERR, "Can't remove net socket fd from main poll loop");
+
+                       return (-1);
+               }
+       }
+
+       return (0);
+}