chiark / gitweb /
libudev: monitor - use SOCK_NONBLOCK
[elogind.git] / libudev / libudev-monitor.c
index b3e5d5f348499bb20acb0a208fe414cfb75b4861..85ab23bdd4aba8b5e2be1dae8f8f56908aebd3d1 100644 (file)
@@ -50,6 +50,7 @@ struct udev_monitor {
        socklen_t addrlen;
        struct udev_list_node filter_subsystem_list;
        struct udev_list_node filter_tag_list;
+       bool bound;
 };
 
 enum udev_monitor_netlink_group {
@@ -116,7 +117,7 @@ static struct udev_monitor *udev_monitor_new(struct udev *udev)
  *
  * Returns: a new udev monitor, or #NULL, in case of an error
  **/
-struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char *socket_path)
+UDEV_EXPORT struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char *socket_path)
 {
        struct udev_monitor *udev_monitor;
        struct stat statbuf;
@@ -144,7 +145,7 @@ struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char
                util_strscpy(&udev_monitor->sun.sun_path[1], sizeof(udev_monitor->sun.sun_path)-1, socket_path);
                udev_monitor->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(socket_path)+1;
        }
-       udev_monitor->sock = socket(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0);
+       udev_monitor->sock = socket(AF_LOCAL, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
        if (udev_monitor->sock == -1) {
                err(udev, "error getting socket: %m\n");
                free(udev_monitor);
@@ -155,32 +156,7 @@ struct udev_monitor *udev_monitor_new_from_socket(struct udev *udev, const char
        return udev_monitor;
 }
 
-/**
- * udev_monitor_new_from_netlink:
- * @udev: udev library context
- * @name: name of event source
- *
- * Create new udev monitor and connect to a specified event
- * source. Valid sources identifiers are "udev" and "kernel".
- *
- * Applications should usually not connect directly to the
- * "kernel" events, because the devices might not be useable
- * at that time, before udev has configured them, and created
- * device nodes.
- *
- * Accessing devices at the same time as udev, might result
- * in unpredictable behavior.
- *
- * The "udev" events are sent out after udev has finished its
- * event processing, all rules have been processed, and needed
- * device nodes are created.
- *
- * The initial refcount is 1, and needs to be decremented to
- * release the resources of the udev monitor.
- *
- * Returns: a new udev monitor, or #NULL, in case of an error
- **/
-struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name)
+struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const char *name, int fd)
 {
        struct udev_monitor *udev_monitor;
        unsigned int group;
@@ -201,11 +177,16 @@ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char
        if (udev_monitor == NULL)
                return NULL;
 
-       udev_monitor->sock = socket(PF_NETLINK, SOCK_DGRAM|SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
-       if (udev_monitor->sock == -1) {
-               err(udev, "error getting socket: %m\n");
-               free(udev_monitor);
-               return NULL;
+       if (fd < 0) {
+               udev_monitor->sock = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT);
+               if (udev_monitor->sock == -1) {
+                       err(udev, "error getting socket: %m\n");
+                       free(udev_monitor);
+                       return NULL;
+               }
+       } else {
+               udev_monitor->bound = true;
+               udev_monitor->sock = fd;
        }
 
        udev_monitor->snl.nl_family = AF_NETLINK;
@@ -219,6 +200,36 @@ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char
        return udev_monitor;
 }
 
+/**
+ * udev_monitor_new_from_netlink:
+ * @udev: udev library context
+ * @name: name of event source
+ *
+ * Create new udev monitor and connect to a specified event
+ * source. Valid sources identifiers are "udev" and "kernel".
+ *
+ * Applications should usually not connect directly to the
+ * "kernel" events, because the devices might not be useable
+ * at that time, before udev has configured them, and created
+ * device nodes.
+ *
+ * Accessing devices at the same time as udev, might result
+ * in unpredictable behavior.
+ *
+ * The "udev" events are sent out after udev has finished its
+ * event processing, all rules have been processed, and needed
+ * device nodes are created.
+ *
+ * The initial refcount is 1, and needs to be decremented to
+ * release the resources of the udev monitor.
+ *
+ * Returns: a new udev monitor, or #NULL, in case of an error
+ **/
+UDEV_EXPORT struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name)
+{
+       return udev_monitor_new_from_netlink_fd(udev, name, -1);
+}
+
 static inline void bpf_stmt(struct sock_filter *inss, unsigned int *i,
                            unsigned short code, unsigned int data)
 {
@@ -250,7 +261,7 @@ static inline void bpf_jmp(struct sock_filter *inss, unsigned int *i,
  *
  * Returns: 0 on success, otherwise a negative error value.
  */
-int udev_monitor_filter_update(struct udev_monitor *udev_monitor)
+UDEV_EXPORT int udev_monitor_filter_update(struct udev_monitor *udev_monitor)
 {
        struct sock_filter ins[512];
        struct sock_fprog filter;
@@ -342,6 +353,7 @@ int udev_monitor_filter_update(struct udev_monitor *udev_monitor)
        bpf_stmt(ins, &i, BPF_RET|BPF_K, 0xffffffff);
 
        /* install filter */
+       memset(&filter, 0x00, sizeof(filter));
        filter.len = i;
        filter.filter = ins;
        err = setsockopt(udev_monitor->sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter));
@@ -361,18 +373,26 @@ int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct
  *
  * Returns: 0 on success, otherwise a negative error value.
  */
-int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor)
+UDEV_EXPORT int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor)
 {
-       int err;
+       int err = 0;
        const int on = 1;
 
        if (udev_monitor->sun.sun_family != 0) {
-               err = bind(udev_monitor->sock,
-                          (struct sockaddr *)&udev_monitor->sun, udev_monitor->addrlen);
+               if (!udev_monitor->bound) {
+                       err = bind(udev_monitor->sock,
+                                  (struct sockaddr *)&udev_monitor->sun, udev_monitor->addrlen);
+                       if (err == 0)
+                               udev_monitor->bound = true;
+               }
        } else if (udev_monitor->snl.nl_family != 0) {
                udev_monitor_filter_update(udev_monitor);
-               err = bind(udev_monitor->sock,
-                          (struct sockaddr *)&udev_monitor->snl, sizeof(struct sockaddr_nl));
+               if (!udev_monitor->bound) {
+                       err = bind(udev_monitor->sock,
+                                  (struct sockaddr *)&udev_monitor->snl, sizeof(struct sockaddr_nl));
+                       if (err == 0)
+                               udev_monitor->bound = true;
+               }
                if (err == 0) {
                        struct sockaddr_nl snl;
                        socklen_t addrlen;
@@ -410,7 +430,7 @@ int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor)
  *
  * Returns: 0 on success, otherwise -1 on error.
  */
-int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size)
+UDEV_EXPORT int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size)
 {
        if (udev_monitor == NULL)
                return -1;
@@ -434,7 +454,7 @@ int udev_monitor_disconnect(struct udev_monitor *udev_monitor)
  *
  * Returns: the passed udev monitor
  **/
-struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor)
+UDEV_EXPORT struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor)
 {
        if (udev_monitor == NULL)
                return NULL;
@@ -451,7 +471,7 @@ struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor)
  * will be released.
  *
  **/
-void udev_monitor_unref(struct udev_monitor *udev_monitor)
+UDEV_EXPORT void udev_monitor_unref(struct udev_monitor *udev_monitor)
 {
        if (udev_monitor == NULL)
                return;
@@ -474,7 +494,7 @@ void udev_monitor_unref(struct udev_monitor *udev_monitor)
  *
  * Returns: the udev library context
  **/
-struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor)
+UDEV_EXPORT struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor)
 {
        if (udev_monitor == NULL)
                return NULL;
@@ -489,7 +509,7 @@ struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor)
  *
  * Returns: the socket file descriptor
  **/
-int udev_monitor_get_fd(struct udev_monitor *udev_monitor)
+UDEV_EXPORT int udev_monitor_get_fd(struct udev_monitor *udev_monitor)
 {
        if (udev_monitor == NULL)
                return -1;
@@ -550,7 +570,7 @@ tag:
  *
  * Returns: a new udev device, or #NULL, in case of an error
  **/
-struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor)
+UDEV_EXPORT struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor)
 {
        struct udev_device *udev_device;
        struct msghdr smsg;
@@ -625,8 +645,11 @@ retry:
        if (memcmp(buf, "libudev", 8) == 0) {
                /* udev message needs proper version magic */
                nlh = (struct udev_monitor_netlink_header *) buf;
-               if (nlh->magic != htonl(UDEV_MONITOR_MAGIC))
+               if (nlh->magic != htonl(UDEV_MONITOR_MAGIC)) {
+                       err(udev_monitor->udev, "ignored a message from an invalid release of udevadm (%x != %x)\n",
+                           nlh->magic, htonl(UDEV_MONITOR_MAGIC));
                        return NULL;
+               }
                if (nlh->properties_off+32 > buflen)
                        return NULL;
                bufpos = nlh->properties_off;
@@ -800,14 +823,14 @@ int udev_monitor_send_device(struct udev_monitor *udev_monitor,
  *
  * Returns: 0 on success, otherwise a negative error value.
  */
-int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype)
+UDEV_EXPORT int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype)
 {
        if (udev_monitor == NULL)
                return -EINVAL;
        if (subsystem == NULL)
                return -EINVAL;
        if (udev_list_entry_add(udev_monitor->udev,
-                               &udev_monitor->filter_subsystem_list, subsystem, devtype, 0, 0) == NULL)
+                               &udev_monitor->filter_subsystem_list, subsystem, devtype, 0) == NULL)
                return -ENOMEM;
        return 0;
 }
@@ -824,14 +847,14 @@ int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_mo
  *
  * Returns: 0 on success, otherwise a negative error value.
  */
-int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag)
+UDEV_EXPORT int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag)
 {
        if (udev_monitor == NULL)
                return -EINVAL;
        if (tag == NULL)
                return -EINVAL;
        if (udev_list_entry_add(udev_monitor->udev,
-                               &udev_monitor->filter_tag_list, tag, NULL, 0, 0) == NULL)
+                               &udev_monitor->filter_tag_list, tag, NULL, 0) == NULL)
                return -ENOMEM;
        return 0;
 }
@@ -844,7 +867,7 @@ int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const c
  *
  * Returns: 0 on success, otherwise a negative error value.
  */
-int udev_monitor_filter_remove(struct udev_monitor *udev_monitor)
+UDEV_EXPORT int udev_monitor_filter_remove(struct udev_monitor *udev_monitor)
 {
        static struct sock_fprog filter = { 0, NULL };