Los eventos de conexión en caliente libusb-1.0 dejan de funcionar en padre después de fork (), cuando el niño llama a libusb_exit ()

He estado desarrollando una aplicación que monitorea el árbol de dispositivos USB usandolibusb_hotplug_register_callback(). Cuando se adjunta un dispositivo que coincide con algunos criterios, sefork() yexec() Una aplicación para manejar este dispositivo.

La aplicación ha estado funcionando bien desde hace algún tiempo, pero he vuelto para tratar de "arreglarla" ...

libusb abrirá una serie de descriptores de archivos (ver más abajo) que monitorea en busca de eventos, etc. El problema es que después de llamarfork() y antes de llamarexec(), Me gustaría cerrar libusb, cerrar los descriptores de archivos y dejar a los niños limpios.

Padre:
root@imx6q:~# ls -l /proc/14245/fd
total 0
lrwx------ 1 root root 64 Feb  9 18:15 0 -> /dev/pts/2
lrwx------ 1 root root 64 Feb  9 18:15 1 -> /dev/pts/2
lrwx------ 1 root root 64 Feb  9 18:15 2 -> /dev/pts/2
lrwx------ 1 root root 64 Feb  9 18:15 3 -> socket:[1681018]
lr-x------ 1 root root 64 Feb  9 18:15 4 -> pipe:[1681019]
l-wx------ 1 root root 64 Feb  9 18:15 5 -> pipe:[1681019]
lr-x------ 1 root root 64 Feb  9 18:15 6 -> pipe:[1681020]
l-wx------ 1 root root 64 Feb  9 18:15 7 -> pipe:[1681020]
lrwx------ 1 root root 64 Feb  9 18:15 8 -> anon_inode:[timerfd]
Niño:
root@imx6q:~# ls -l /proc/14248/fd
total 0
lr-x------ 1 root root 64 Feb  9 18:15 0 -> /dev/null
l-wx------ 1 root root 64 Feb  9 18:15 1 -> /dev/null
lrwx------ 1 root root 64 Feb  9 18:15 2 -> /dev/pts/2
lr-x------ 1 root root 64 Feb  9 18:15 4 -> pipe:[1681019]
l-wx------ 1 root root 64 Feb  9 18:15 5 -> pipe:[1681019]
lr-x------ 1 root root 64 Feb  9 18:15 6 -> pipe:[1681020]
l-wx------ 1 root root 64 Feb  9 18:15 7 -> pipe:[1681020]
lrwx------ 1 root root 64 Feb  9 18:15 8 -> anon_inode:[timerfd]

El problema con el que me he encontrado es que al llamarlibusb_exit() en el niño, el padre ya no vealguna eventos de conexión en caliente.

He intentado volver a registrar mi devolución de llamada después de unfork() (en el padre) sin suerte (y sin errores).

He tenido un poco de revolver en las fuentes de libusb y libudev, y me pregunto si esto es un efecto secundario de la libusblinux_udev_stop_event_monitor() vocaciónudev_monitor_unref()... Pero, por desgracia, no hay "comunicación" allí, solo una llamada aclose() cuando el recuento de ref llega a cero. Y de todos modos, el socket que falta desde arriba es muy probable que sea el socket de enlace de red que udev abre (cortésmente, conSOCK_CLOEXEC)

A continuación se muestra una aplicación de ejemplo que demuestra el problema:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <libusb-1.0/libusb.h>

libusb_context *libusb_ctx;
libusb_hotplug_callback_handle cb_handle;

int hotplug_callback(struct libusb_context *ctx, struct libusb_device *dev, libusb_hotplug_event event, void *user_data) {
    pid_t pid;
    char *cmd[] = {
        "sleep", "600", NULL
    };

    fprintf(stderr, "event! %d\n", event);

    /* fork, and return if in parent */
    pid = fork();
    assert(pid != -1);
    if (pid != 0) {
        fprintf(stderr, "intermediate child's PID is: %d\n", pid);
        return 0;
    }

    /* setsid() and re-fork() to complete daemonization */
    assert(setsid() != -1);
    pid = fork();
    assert(pid != -1);
    if (pid != 0) {
        fprintf(stderr, "child's PID is: %d\n", pid);
        exit(0);
    }

#if 1 /* toggle this */
    fprintf(stderr, "libusb is NOT shutdown in child...\n");
#else
    /* shutdown libusb */
    libusb_hotplug_deregister_callback(libusb_ctx, cb_handle);
    libusb_exit(libusb_ctx);
    fprintf(stderr, "libusb is shutdown in child...\n");
#endif

    /* now that the child has reached this point, you'll never see a hotplug event again! */

    /* exec() */
    assert(execvp(cmd[0], cmd) == 0);
    abort();
}

void main(void) {
    pid_t pid;

    /* setup libusb & hotplug callback */
    assert(libusb_init(&libusb_ctx) == 0);
    assert(libusb_hotplug_register_callback(libusb_ctx, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, LIBUSB_HOTPLUG_NO_FLAGS, LIBUSB_HOTPLUG_MATCH_ANY,
                                            LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, hotplug_callback, NULL, &cb_handle) == LIBUSB_SUCCESS);

    pid = getpid();
    fprintf(stderr, "running... parent's PID is: %d\n", pid);

    /* allow libusb to handle events */
    while (1) {
        assert(libusb_handle_events_completed(NULL, NULL) == 0);
    }
}

Con el 'alternar esto'#if ajustado a1, Veo la siguiente sesión (3x conexiones):

# ./main
running... parent's PID is: 14370
event! 1
intermediate child's PID is: 14372
child's PID is: 14373
libusb is NOT shutdown in child...
event! 1
intermediate child's PID is: 14375
child's PID is: 14376
libusb is NOT shutdown in child...
event! 1
intermediate child's PID is: 14379
child's PID is: 14380
libusb is NOT shutdown in child...
^C

Con el 'alternar esto'#if ajustado a0, Veo la siguiente sesión (3x conexiones, solo se acciona la primera):

# ./main
running... parent's PID is: 14388
event! 1
intermediate child's PID is: 14390
child's PID is: 14391
libusb is shutdown in child...
^C

Las versiones de software son:

libusb: libusb-1.0.so.0.1.0 / 1.0.20-r1libudev: libudev.so.0.13.1 / 182-r7núcleo: 3.14.38

Si alguien ha visto esto antes, o puede reproducirlo (¡o no puede reproducirlo!) Agradecería su aporte. ¡Gracias por adelantado!

Respuestas a la pregunta(1)

Su respuesta a la pregunta