socketslinux-device-driverudev

How can a program started by udev call socket()?


The program (u50/u60 USB device driver daemon) succeeds from the shell but fails if started upon boot by udev.

I tried to use sd_listen_fds() but it returned 0.

    int lonsd; // file descriptor for the new socket
    struct ifreq ifr = {0};
    struct ifaddrs *ifaddr, *ifa;
    int found = 0;
    int fd;
    int num_fds; // number of file descriptors
    // https://insujang.github.io/2018-11-27/udev-device-manager-for-the-linux-kernel-in-userspace/
    // Currently, udev is a part of systemd, therefore it uses systemd’s socket protocol.

    syslog(LOG_INFO, "%s devstr:%s, ldisc:%d\n", __func__, devstr, ldisc);

    lonsd = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
    if (lonsd < 0) {
        syslog(LOG_INFO, "%s socket() failed - must be udev\n", __func__);

        lonsd = -EBADF;
        //systemd/src/udev/udevd.c:
        //static int listen_fds(int* ret_ctrl, int* ret_netlink) {
        num_fds = sd_listen_fds(true);
        if (num_fds < 0) {
            syslog(LOG_CRIT, "%s sd_listen_fds() failed\n", __func__);
        }
        syslog(LOG_INFO, "%s num_fds:%d\n", __func__, num_fds);
        for (fd = SD_LISTEN_FDS_START; fd < num_fds + SD_LISTEN_FDS_START; fd++) {
            if (sd_is_socket(fd, AF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)) > 0) {
                if (lonsd >= 0) {
                    syslog(LOG_CRIT, "%s EINVAL: lonsd >= 0\n", __func__);
                    return -EINVAL;
                }
                lonsd = fd;
            }
        }
    }
    else syslog(LOG_INFO, "%s lonsd:%d\n", __func__, lonsd);

I am porting a driver for a LonWorks USB interface (Bus 003 Device 004: ID 0920:5550 Echelon Co. Echelon U60 FT Network Interface). The u50.ko driver was inserted. When run from the shell, "lonifd" creates a network device.

5: lon10: <BROADCAST,UP,LOWER_UP> mtu 1280 qdisc fq_codel state UNKNOWN group default qlen 10 link/[2384] inet 44.1.110.125/24 brd 44.1.110.255 scope global lon10 valid_lft forever preferred_lft forever –

But when started by udev, socket() fails.


Solution

  • Per the comments, I changed the rule to start a service. Now it runs "lonifd" upon boot if the device is found, and when the device is plugged in and stops it when it is unplugged.

    The revised rule: /etc/udev/rules.d/90-liftd.rules.

    SUBSYSTEM=="tty", ATTRS{idVendor}=="0920", ATTRS{idProduct}=="5550", ENV{SYSTEMD_WANTS}+="u60.service"
    

    /etc/systemd/system/u60.service

    [Unit]
    Description=U60 USB Lontalk Interface
    Documentation=
    
    [Service]
    ExecStart=/usr/bin/lonifd
    Type=exec
    Restart=on-failure