On Tue, Aug 04, 2017 Dmitry V. Levin wrote:
> On Tue, Aug 01, 2017 at 07:48:41AM +0800, JingPiao Chen wrote:
> > Prepare for NETLINK_KOBJECT_UEVENT decode. The messages
> > of NETLINK_KOBJECT_UEVENT do not contain nlmsghdr.
> 
> A netlink message without a netlink message header?  Ouch.
> Could you give a link to the exact place in kernel sources
> or documentation where this marvel of design is described, please?

Documentation/kobject.txt: 166
Uevents
=======

After a kobject has been registered with the kobject core, you need to
announce to the world that it has been created.  This can be done with a
call to kobject_uevent()::

    int kobject_uevent(struct kobject *kobj, enum kobject_action action);

lib/kobject_uevent.c:
/**
 * kobject_uevent - notify userspace by sending an uevent
 *
 * @kobj: struct kobject that the action is happening to
 * @action: action that is happening
 *
 * Returns 0 if kobject_uevent() is completed with success or the
 * corresponding error when it fails.
 */
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
{
        return kobject_uevent_env(kobj, action, NULL);
}
EXPORT_SYMBOL_GPL(kobject_uevent);

/**
 * kobject_uevent_env - send an uevent with environmental data
 *
 * @kobj: struct kobject that the action is happening to
 * @action: action that is happening
 * @envp_ext: pointer to environmental data
 *
 * Returns 0 if kobject_uevent_env() is completed with success or the
 * corresponding error when it fails.
 */
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
                       char *envp_ext[])
{
        ...
        /* default keys */
        retval = add_uevent_var(env, "ACTION=%s", action_string);
        if (retval)
                goto exit;
        retval = add_uevent_var(env, "DEVPATH=%s", devpath);
        if (retval)
                goto exit;
        retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
        if (retval)
                goto exit;

        /* keys passed in from the caller */
        if (envp_ext) {
                for (i = 0; envp_ext[i]; i++) {
                        retval = add_uevent_var(env, "%s", envp_ext[i]);
                        if (retval)
                                goto exit;
                }
        }

        ...
        /* send netlink message */
        list_for_each_entry(ue_sk, &uevent_sock_list, list) {
                struct sock *uevent_sock = ue_sk->sk;
                struct sk_buff *skb;
                size_t len;

                if (!netlink_has_listeners(uevent_sock, 1))
                        continue;

                /* allocate message with the maximum possible size */
                len = strlen(action_string) + strlen(devpath) + 2;
                skb = alloc_skb(len + env->buflen, GFP_KERNEL);
                if (skb) {
                        char *scratch;

                        /* add header */
                        scratch = skb_put(skb, len);
                        sprintf(scratch, "%s@%s", action_string, devpath);

                        /* copy keys to our continuous event payload buffer */
                        for (i = 0; i < env->envp_idx; i++) {
                                len = strlen(env->envp[i]) + 1;
                                scratch = skb_put(skb, len);
                                strcpy(scratch, env->envp[i]);
                        }

                        NETLINK_CB(skb).dst_group = 1;
                        retval = netlink_broadcast_filtered(uevent_sock, skb,
                                                            0, 1, GFP_KERNEL,
                                                            kobj_bcast_filter,
                                                            kobj);
                        /* ENOBUFS should be handled in userspace */
                        if (retval == -ENOBUFS || retval == -ESRCH)
                                retval = 0;
                } else
                        retval = -ENOMEM;
        }
        ...
}

/**
 * add_uevent_var - add key value string to the environment buffer
 * @env: environment buffer structure
 * @format: printf format for the key=value pair
 *
 * Returns 0 if environment variable was added successfully or -ENOMEM
 * if no space was available.
 */
int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
{
        ...
        len = vsnprintf(&env->buf[env->buflen],
                        sizeof(env->buf) - env->buflen,
                        format, args);
        ...
}

A demo of NETLINK_KOBJECT_UEVENT:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>

int
main(void)
{
        struct sockaddr_nl nl;
        size_t i, len;
        int fd;
        char buf[4096];

        if ((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT)) == -1) {
                perror("socket");
                return EXIT_FAILURE;
        }

        memset(&nl, 0, sizeof(nl));
        nl.nl_family = AF_NETLINK;
        nl.nl_pid = getpid();
        nl.nl_groups = 1;

        if (bind(fd, (struct sockaddr *)&nl, sizeof(nl)) == -1) {
                perror("bind");
                return EXIT_FAILURE;
        }

        for (;;) {
                len = recv(fd, buf, sizeof(buf), 0);
                if (!len)
                        break;
                write(STDOUT_FILENO, buf, len);
        }
        close(fd);

        return 0;
}

$ gcc uevent.c
$ ./a.out # Remove your mouse

--
JingPiao Chen

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Strace-devel mailing list
Strace-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/strace-devel

Reply via email to