martinpitt / umockdev

Mock hardware devices for creating unit tests and bug reporting
https://launchpad.net/umockdev
GNU Lesser General Public License v2.1
308 stars 54 forks source link

System properties getting used for mocked device #213

Closed hadess closed 1 year ago

hadess commented 1 year ago

Using systemd-udev-253.5-1.fc38.x86_64, current git main umockdev, libgudev and upower master branches

Running the test_headset_detection test in upower, I get a failure on my desktop system because the headset device it detects shows up as an internal audio device, instead of a headset. The problem doesn't happen in the CI.

The properties dump for the device shows:

CURRENT_TAGS = :seat:
DEVPATH = /devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/sound/card0
ID_BUS = pci
ID_FOR_SEAT = sound-pci-0000_00_1f_3
ID_ID = usb-Logitech_G935_Gaming_Headset-00
ID_MODEL = G935_Gaming_Headset
ID_MODEL_ENC = G935\x20Gaming\x20Headset
ID_MODEL_FROM_DATABASE = Cannon Lake PCH cAVS
ID_MODEL_ID = 0xa348
ID_PATH = pci-0000:00:1f.3
ID_PATH_TAG = pci-0000_00_1f_3
ID_PCI_CLASS_FROM_DATABASE = Multimedia controller
ID_PCI_SUBCLASS_FROM_DATABASE = Audio device
ID_REVISION = 0112
ID_SERIAL = Logitech_G935_Gaming_Headset
ID_TYPE = audio
ID_USB_DRIVER = snd-usb-audio
ID_USB_INTERFACES = :010100:010200:030000:
ID_USB_INTERFACE_NUM = 00
ID_USB_MODEL = G935_Gaming_Headset
ID_USB_MODEL_ENC = G935\x20Gaming\x20Headset
ID_USB_MODEL_ID = 0a87
ID_USB_REVISION = 0112
ID_USB_SERIAL = Logitech_G935_Gaming_Headset
ID_USB_TYPE = audio
ID_USB_VENDOR = Logitech
ID_USB_VENDOR_ENC = Logitech
ID_USB_VENDOR_ID = 046d
ID_VENDOR = Logitech
ID_VENDOR_ENC = Logitech
ID_VENDOR_FROM_DATABASE = Intel Corporation
ID_VENDOR_ID = 0x8086
NVME_HOST_IFACE = none
SOUND_FORM_FACTOR = internal
SOUND_INITIALIZED = 1
SUBSYSTEM = sound
TAGS = :seat:
USEC_INITIALIZED = 12076650

Which looks suspiciously like something is reading from the real sound card0 on my system!

P: /devices/pci0000:00/0000:00:1f.3/sound/card0
M: card0
R: 0
U: sound
E: DEVPATH=/devices/pci0000:00/0000:00:1f.3/sound/card0
E: SUBSYSTEM=sound
E: USEC_INITIALIZED=12076650
E: NVME_HOST_IFACE=none
E: ID_PATH=pci-0000:00:1f.3
E: ID_PATH_TAG=pci-0000_00_1f_3
E: ID_FOR_SEAT=sound-pci-0000_00_1f_3
E: SOUND_INITIALIZED=1
E: ID_PCI_CLASS_FROM_DATABASE=Multimedia controller
E: ID_PCI_SUBCLASS_FROM_DATABASE=Audio device
E: ID_VENDOR_FROM_DATABASE=Intel Corporation
E: ID_MODEL_FROM_DATABASE=Cannon Lake PCH cAVS
E: ID_BUS=pci
E: ID_VENDOR_ID=0x8086
E: ID_MODEL_ID=0xa348
E: SOUND_FORM_FACTOR=internal
E: TAGS=:seat:
E: CURRENT_TAGS=:seat:
hadess commented 1 year ago

This would explain why it's reading data from the system rather than the mock dir:

openat(AT_FDCWD, "/run/udev/data/+sound:card0", O_RDONLY|O_CLOEXEC) = 10

My "fix" is:

diff --git a/src/libumockdev-preload.c b/src/libumockdev-preload.c
index db6e310cb445..5ede952b6e47 100644
--- a/src/libumockdev-preload.c
+++ b/src/libumockdev-preload.c
@@ -185,6 +185,8 @@ trap_path(const char *path)

     if (strncmp(abspath, "/dev/", 5) == 0 || strcmp(abspath, "/dev") == 0 || strncmp(abspath, "/proc/", 6) == 0)
        check_exist = 1;
+    else if (strncmp(abspath, "/run/", 5) == 0)
+       check_exist = 0;
     else if (strncmp(abspath, "/sys/", 5) != 0 && strcmp(abspath, "/sys") != 0)
        return path;

I straced this systemd test executable to double-check that trying to access a /run file got redirected to the mock directory:

/* SPDX-License-Identifier: LGPL-2.1-or-later */                                                              

#include "device-internal.h"                                                                                  
#include "device-private.h"                                                                                   
#include "fd-util.h"                                                                                          
#include "fs-util.h"                                                                                          
#include "tmpfile-util.h"                                                                                     

int main(int argc, char **argv) {                                                                             
        char *filename = argv[1];                                                                             
        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;                                                    
        printf("filename: %s\n", filename);                                                                   
        assert_se(device_new_aux(&dev) >= 0);                                                                 
        (void) device_read_db_internal_filename(dev, filename);                                               
        return 0;                                                                                             
}