]> granicus.if.org Git - libnl/commitdiff
add socket nl_connect_fd() & nl_create_fd()
authorSagi Lowenhardt <sagil@infinidat.com>
Tue, 17 Feb 2015 13:22:27 +0000 (15:22 +0200)
committerThomas Haller <thaller@redhat.com>
Thu, 5 Mar 2015 10:27:27 +0000 (11:27 +0100)
-    Added option to create socket (fd) without bind.
     It is now possible to forward the socket fd to another child process...
     ...later use nl_connect_fd() to connect to socket from the child process.

-    Added option to disable CLOEXEC even if defined (in socket.h)
     'nl_socket_enable_cloexec' & 'nl_socket_disable_cloexec'
     No change to current default behavior.

Signed-off-by: Sagi Lowenhardt <sagil@infinidat.com>
include/netlink-private/types.h
include/netlink/netlink.h
include/netlink/socket.h
lib/nl.c
lib/socket.c

index 54f06b575799f653f5cf541330cf1772e39dbc33..7d044f10d356572fd7a0549b0dc512154caca3c3 100644 (file)
@@ -77,6 +77,7 @@ struct nl_sock
        int                     s_flags;
        struct nl_cb *          s_cb;
        size_t                  s_bufsize;
+       int                     s_cloexec;
 };
 
 struct nl_cache
index f8f2082f282f11d85cb8920b26936c6808108b26..7b6cb60cf6ade352db1c170895f51ab70712b7d3 100644 (file)
@@ -49,6 +49,8 @@ extern struct nl_dump_params nl_debug_dp;
 
 /* Connection Management */
 extern int                     nl_connect(struct nl_sock *, int);
+extern int                     nl_create_fd(struct nl_sock *, int);
+extern int                     nl_connect_fd(struct nl_sock *, int, int);
 extern void                    nl_close(struct nl_sock *);
 
 /* Send */
index 1007eba03ad982b67402f3d4b01cfc480fdf50ec..bda0ff3509fde6ae35e96b8af675b17a018cd9ff 100644 (file)
@@ -63,6 +63,8 @@ extern int            nl_socket_get_fd(const struct nl_sock *);
 extern int             nl_socket_set_nonblocking(const struct nl_sock *);
 extern void            nl_socket_enable_msg_peek(struct nl_sock *);
 extern void            nl_socket_disable_msg_peek(struct nl_sock *);
+extern int             nl_socket_enable_cloexec(struct nl_sock *);
+extern void            nl_socket_disable_cloexec(struct nl_sock *);
 
 #ifdef __cplusplus
 }
index 8e4c4552cf4e59e1765119cf25993ec1aef20208..4997e7984491246f096952dc0430debc994433dd 100644 (file)
--- a/lib/nl.c
+++ b/lib/nl.c
  */
 
 /**
- * Create file descriptor and bind socket.
+ * Create file descriptor.
  * @arg sk             Netlink socket (required)
  * @arg protocol       Netlink protocol to use (required)
  *
+ * Creates a new Netlink socket using `socket()` . Fails if
+ * the socket is already connected.
+ */
+int nl_create_fd(struct nl_sock *sk, int protocol)
+{
+       int err, flags = 0;
+    int errsv;
+    char buf[64];
+
+
+#ifdef SOCK_CLOEXEC
+    if (sk->s_cloexec == 1)
+       flags |= SOCK_CLOEXEC;
+#endif
+
+    if (sk->s_fd != -1)
+               return -NLE_BAD_SOCK;
+
+    sk->s_fd = socket(AF_NETLINK, SOCK_RAW | flags, protocol);
+    if (sk->s_fd < 0) {
+               errsv = errno;
+               NL_DBG(4, "nl_connect(%p): socket() failed with %d (%s)\n", sk, errsv,
+                               strerror_r(errsv, buf, sizeof(buf)));
+               err = -nl_syserr2nlerr(errsv);
+               goto errout;
+       }
+
+       return 0;
+
+errout:
+               if (sk->s_fd != -1) {
+                       close(sk->s_fd);
+                       sk->s_fd = -1;
+               }
+
+       return err;
+}
+
+/**
+ * Create file descriptor and bind socket.
+ * @arg sk      Netlink socket (required)
+ * @arg protocol    Netlink protocol to use (required)
+ *
  * Creates a new Netlink socket using `socket()` and binds the socket to the
  * protocol and local port specified in the `sk` socket object. Fails if
  * the socket is already connected.
  *
- * @note If available, the `close-on-exec` (`SOCK_CLOEXEC`) feature is enabled
- *       automatically on the new file descriptor. This causes the socket to
- *       be closed automatically if any of the `exec` family functions succeed.
- *       This is essential for multi threaded programs.
- *
  * @note The local port (`nl_socket_get_local_port()`) is unspecified after
  *       creating a new socket. It only gets determined when accessing the
  *       port the first time or during `nl_connect()`. When nl_connect()
  */
 int nl_connect(struct nl_sock *sk, int protocol)
 {
-       int err, flags = 0;
+       int err = nl_create_fd(sk, protocol);
+       if (err != 0)
+               return err;
+
+       return nl_connect_fd(sk, protocol, sk->s_fd);
+}
+
+/**
+ * @arg sk      Netlink socket (required)
+ * @arg protocol    Netlink protocol to use (required)
+ * @arg fd      Socket file descriptor to use (required)
+ *
+ * @note The local port (`nl_socket_get_local_port()`) is unspecified after
+ *       creating a new socket. It only gets determined when accessing the
+ *       port the first time or during `nl_connect_fd()`. When nl_connect_fd()
+ *       fails during `bind()` due to `ADDRINUSE`, it will retry with
+ *       different ports if the port is unspecified. Unless you want to enforce
+ *       the use of a specific local port, don't access the local port (or
+ *       reset it to `unspecified` by calling `nl_socket_set_local_port(sk, 0)`).
+ *       This capability is indicated by
+ *       `%NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE`.
+ *
+ * @see nl_socket_alloc()
+ * @see nl_close()
+ *
+ * @return 0 on success or a negative error code.
+ *
+ * @retval -NLE_BAD_SOCK Socket is not connected
+ */
+int nl_connect_fd(struct nl_sock *sk, int protocol, int fd)
+{
+       int err = 0;
        int errsv;
        socklen_t addrlen;
        struct sockaddr_nl local = { 0 };
        char buf[64];
 
-#ifdef SOCK_CLOEXEC
-       flags |= SOCK_CLOEXEC;
-#endif
-
-        if (sk->s_fd != -1)
-                return -NLE_BAD_SOCK;
+    if (fd < 0)
+        return -NLE_BAD_SOCK;
 
-       sk->s_fd = socket(AF_NETLINK, SOCK_RAW | flags, protocol);
-       if (sk->s_fd < 0) {
-               errsv = errno;
-               NL_DBG(4, "nl_connect(%p): socket() failed with %d (%s)\n", sk, errsv,
-                       strerror_r(errsv, buf, sizeof(buf)));
-               err = -nl_syserr2nlerr(errsv);
-               goto errout;
-       }
+    sk->s_fd = fd;
 
        err = nl_socket_set_buffer_size(sk, 0, 0);
        if (err < 0)
index 628a96de5c7dc7badc25b6aed5455eae6e6b422f..01cc2193e64fc16e7f9e0e1ab5347068588f44cc 100644 (file)
@@ -192,6 +192,12 @@ static struct nl_sock *__alloc_socket(struct nl_cb *cb)
        sk->s_peer.nl_family = AF_NETLINK;
        sk->s_seq_expect = sk->s_seq_next = time(NULL);
 
+#ifdef SOCK_CLOEXEC
+       sk->s_cloexec  = 1;
+#else
+       sk->s_cloexec = 0;
+#endif
+
        /* the port is 0 (unspecified), meaning NL_OWN_PORT */
        sk->s_flags = NL_OWN_PORT;