Lorsqu'on fait connaissance avec l'un des systèmes héritiers du BSD d'origine, à savoir FreeBSD, NetBSD ou encore OpenBSD, tout en ayant une certaine expérience de GNU/Linux, il est relativement facile de retrouver ses petits. Certes, depuis quelques années, le système d'init et la gestion des services de GNU/Linux se sont drastiquement écartés des principes propres à la philosophie Unix, jusqu'alors respectés par GNU/Linux. Dans l'ensemble, la transition est relativement aisée, mais il y a cependant un point sur lequel les différences sont telles que quelques explications s'avèrent nécessaires : la gestion des périphériques « hotplug » (USB) et de leurs permissions.
GNU/Linux utilise depuis de nombreuses années un système appelé udev, permettant de réagir à des événements concernant les périphériques. C'est typiquement ce qu'on pourrait appeler un « gestionnaire de périphériques », même si cette terminologie n'est pas sans rappeler un système tristement populaire et propriétaire. udev a remplacé devfs il y a fort longtemps et est, depuis 2012, intégré et fortement lié à systemd (qu'est-ce qui ne l'est pas, actuellement ?). Son travail est simple puisqu'il s'agit, le plus souvent, de détecter l'apparition de périphériques et de déclencher des actions en conséquence, le tout en se basant sur un ensemble de règles généralement placées dans /etc/udev/rules.d. Là, on détermine le « quoi », le « qui », le « où » et, en fonction de ces informations, on ajuste les permissions, on crée des liens symboliques ou encore, on déclenche l'exécution de scripts (voire pire, merci, D-Bus).
Mais sous *BSD, point de systemd tentaculaire (Dieu merci) et donc, point de udev. Contrairement aux quelques distributions GNU/Linux exemptes de systemd (la documentation Gentoo détaille bien les implications [1]), il n'y a cependant ici aucune obligation de compatibilité. L'utilisateur devra s'adapter au système et non tenter de retrouver quelque chose qu'il connaît déjà, sinon à quoi bon passer de GNU/Linux à un autre système ? Et ces autres systèmes ont, sur ce point comme d'autres (l'init, les services, la configuration globale, la division entre le système et les applications tierces, etc.), une approche radicalement différente, plus orientée vers l'administration effective du système et une réelle implication de la personne en charge de sa configuration. En d'autres termes, un « BSD » repose sur la compétence, la créativité et l'envie (le besoin ?) de personnaliser le système pour le rendre plus efficace et maîtrisable.
En ce sens, FreeBSD, NetBSD et OpenBSD fournissent les bases, chacune différente mais similaire aux autres sur le principe, de quelque chose qui se rapproche d'udev, mais offre la possibilité (et implicitement l'obligation) d'adapter tout cela en fonction de ses préférences. Ma problématique première ici concerne l'accès à certains périphériques USB accessibles au travers d'outils reposant sur la LibUSB (lecteur NFC, devkit STM32, Raspberry Pi Pico via picotool, etc.), ainsi que les classiques convertisseurs USB/série permettant de dialoguer avec des systèmes embarqués et/ou des montages utilisant des microcontrôleurs. L'accès aux périphériques n'est pas un problème, mais leur identification et la gestion des permissions est un point à solutionner, car utiliser sudo ou doas systématiquement est non seulement rapidement pénible, mais également peu pertinent. J'écarterai ici, en revanche, les problématiques de montage automatique de systèmes de fichiers (sur clé USB), qui n'ont, à mon sens, que peu d'intérêt. Mais c'est une vision toute personnelle et vous adapterez ce qui suit selon vos préférences...
1. FreeBSD : devd
FreeBSD est sans doute le système le plus « avancé » à ce niveau. Je n'aime pas particulièrement ce terme, impliquant que les deux autres OS sont « en retard », mais nous avons ici que peu de choses à faire. FreeBSD utilise un daemon appelé devd, ainsi qu'un système de fichiers spécifique, devfs, donnant accès aux périphériques, classiquement via le point de montage /dev.
devd voit son comportement réglé par le fichier /etc/devd.conf, lui-même incluant les configurations présentes dans /etc/devd/*. Une rapide consultation des fichiers déjà en présence montre que devd est extrêmement générique et se chargera de réagir à tous types d'événements détectés et traités par le noyau. Le binaire en espace utilisateur utilise pour cela le périphérique devctl qui fournit des informations lorsqu'un nœud est ajouté ou supprimé dans l'arborescence de périphériques maintenue par le noyau.
Ici, nous nous intéressons en particulier aux périphériques USB et série, mais comme le montre le contenu de /etc/devd.conf, il est parfaitement possible de réagir, par exemple, au fait qu'un périphérique Ethernet voit sa liaison passer à « UP » et, de ce fait, lancer le client DHCP :
Si vous êtes coutumier d'udev, ceci devrait vous rappeler quelque chose. En effet, on retrouve ici un sous-système spécifique (IFNET), un type d'événement (LINK_UP), un type de média (ethernet) et une action à déclencher.
Un peu plus loin, on observe le même type de configuration pour, par exemple, changer le profil de gestion d'alimentation lorsque l'adaptateur secteur est connecté (sur un laptop, bien entendu) :
La page de manuel de devd.conf décrit de façon exhaustive l'ensemble des informations utilisables, qu'il s'agisse des directives, des sous-systèmes, des variables ou encore du type d'événements sur lesquels réagir (en fonction du sous-système). Elle fournit également un certain nombre d'exemples qu'on retrouve dans le devd.conf installé par défaut.
Une fois tout ceci assimilé, il est relativement simple de composer ses propres configurations visant des périphériques précis, qu'on placera dans un fichier au nom explicite et avec un suffixe .conf dans /etc/devd. Pour les adaptateurs USB/série, nous nous attachons à la création des pseudo-fichiers /dev/ttyU* et changeons simplement les permissions de manière à permettre aux membres du groupe wheel de lire et d’écrire sur le périphérique :
Nous observons devfs et réagissons sur la création d'un périphérique de caractères dont le nom débute par ttyU (notez que les expressions régulières utilisées sont implicitement entourées de ^$). En plus de changer les permissions, nous demandons l'ajout d'une entrée dans le log système (/var/log/messages), fort utile pour mettre au point les nouvelles configurations.
Le saviez-vous ?
Le nom du groupe wheel est un héritage du système d'exploitation TENEX pour PDP-10 à la fin des années 60 où un bit spécifique d'une configuration de compte utilisateur (le wheel bit) conférait des privilèges particuliers permettant de lancer certaines commandes auxquelles les utilisateurs standard n'avaient pas accès. « wheel » est une abréviation de « big wheel » désignant, en argot anglais, quelqu'un d'important. Ce qu'on pourrait traduire maladroitement en français par « une pointure » ou un « gros bonnet ». Le terme a survécu dans la culture Unix, puis BSD, lorsque les utilisateurs et développeurs provenant de TENEX ont migré en masse sous Unix dans les années 80.
Pour les périphériques USB génériques, ici deux lecteurs NFC et une Raspberry Pi Pico (en mode BOOT0), les choses se présentent plus ou moins de la même manière, si ce n'est que nous surveillons le sous-système « périphérique USB » et guettons une connexion (physique). Pour identifier les périphériques, nous nous basons naturellement sur les VendorID et ProductID :
Remarquez le notify en début de chaque bloc et dont nous n'avons pas encore parlé. Il s'agit d'une priorité d'une valeur arbitrairement choisie (0 étant la priorité plus basse) permettant de gérer une situation où deux configurations correspondraient à un même événement. Dans ce cas, seule la configuration ayant la priorité la plus élevée sera utilisée et non les autres. Ceci permet de mettre en place des configurations suffisamment génériques pour couvrir une vaste gamme de situations, tout en permettant de passer outre et d’appliquer une configuration spécifique dans les cas particuliers.
Une fois ces règles/configurations enregistrées et le service redémarré (avec sudo service devd restart), nous remarquons dans les journaux système que les $cdev pour deux lecteurs NFC sont ugenX.Y et donc que les entrées /dev/ correspondantes sont nommées identiquement. De façon similaire, une Raspberry Pi Pico connectée en mode BOOT0 (programmation) apparaît à la fois comme un périphérique de stockage USB (umass*) et comme un périphérique ugenX.Y. Ceci parce que ce périphérique USB propose deux interfaces différentes, c'est un périphérique composite.
Les entrées ugen correspondent à des périphériques USB n'ayant pas de pilotes spécifiques attachés par le noyau. On peut voir cela comme un pilote par défaut permettant à des outils en espace utilisateur de gérer directement le périphérique. C'est le cas par exemple de la quasi-totalité des lecteurs NFC qui sont alors pilotables avec la LibNFC, elle-même reposant sur la LibUSB. Dans le cas de la Pico, une des interfaces est directement prise en charge par le pilote umass et l'autre est vendor specific. Cette partie est alors utilisable avec un outil comme picotool, à condition que les permissions sur l'entrée /dev le permettent. Et c'est exactement l'objet de la règle devd que nous avons mise en place.
Notez que sous FreeBSD, les entrées /dev/ugen* sont en réalité des liens symboliques vers des pseudo-fichiers /dev/usb/*, et que c'est effectivement là que sont ajustées les permissions. Ceci deviendra important par la suite, car les deux autres systèmes ne fonctionnent pas exactement de la même manière sur ce plan.
2. NetBSD : devpubd
NetBSD utilise, lui aussi, un daemon permettant de déclencher des actions à l'apparition et à la disparition de périphériques, c'est devpubd. Le service doit être configuré pour démarrer en même temps que le système via une ligne dans /etc/rc.conf (devpubd=YES), ou être lancé manuellement avec sudo service devpubd onestart. Le principe de fonctionnement est identique à celui observé sous FreeBSD, mais sensiblement moins intégré. Lors d'un événement, le daemon lance par défaut le script /libexec/devpubd-run-hooks, qui lui-même exécute tous les scripts placés dans /libexec/devpubd-hooks, par ordre lexicographique (alphabétique, mais prenant aussi en compte les chiffres et caractères accentués) en leur passant un paramètre device-attach ou device-detach, ainsi que le nom du périphérique.
Deux scripts sont installés par défaut (NetBSD 9.3) :
- 01-makedev permettant de créer des entrées dans /dev via MAKEDEV (NetBSD n'utilise pas un système de fichiers virtuel pour /dev/) ;
- 02-wedgenames créant automatiquement des liens symboliques vers les zones d'un ou des disques (wedge) qui peuvent avoir été détectées automatiquement par le noyau (en fonction des partitions ou des disklabels) ou configurées manuellement avec dkctl. C'est un peu le /dev/disk/by-* qu'on retrouve sous GNU/Linux). Notez que les wedges eux-mêmes prennent la forme de /dev/dk*, et que ce script ne fait que leur donner des noms sous forme de liens symboliques dans /dev/wedges.
Pour ajuster nos permissions avec les deux types de périphériques qui nous intéressent, ceci est relativement simple puisqu'il nous suffit de créer deux nouveaux scripts dans /libexec/devpubd-hooks, le premier pour les ports série USB et le second pour les périphériques génériques (ugen comme sous FreeBSD). Le premier est relativement simple (03-adjustTTYU) :
Vous remarquerez que le nombre de paramètres est drastiquement inférieur à ce qui existe pour FreeBSD. Ici, nous n'avons que l'événement (un sur les deux possibles) et la désignation du périphérique. Mais comme il s'agit de shell, nous pouvons toujours faire plus ou moins ce qui nous chante. Nous déduisons donc le nom de l'entrée /dev en fonction du numéro de périphérique ucom (USB tty)) qui vient d'être détecté, et nous utilisons chmod pour attribuer les permissions en lecture/écriture pour le groupe (wheel).
Pour les périphériques ugen qui fonctionnent sur le même principe que ceux de FreeBSD (un pilote USB par défaut si aucun autre ne s'applique), les choses sont un peu plus délicates.
En effet, nous pouvons parfaitement procéder de même, mais nous n'aurons aucune distinction entre les périphériques, tout ce qui est connecté en USB et non pris en charge par un autre pilote sera donc impacté et traité, indifféremment. Ce n'est pas vraiment ce que nous voulons, même si cela ferait parfaitement le travail. C'est un peu trop brutal (même pour moi). devpubd ne remonte pas les informations pouvant nous être utiles, comme les VID:PID du périphérique. Pour cela, nous avons deux options : modifier /usr/src/sbin/devpubd/devpubd.c qui est relativement simple et utilise /dev/drvctl pour être informé des événements noyau, ou développer un outil qui va accéder au pseudo-fichier /dev/ugen* pour obtenir les informations. C'est cette seconde approche que j'ai choisie (mais maintenant que j'y pense, la première me tente bien aussi).
2.1 chkugenids à la rescousse !
ugen est bien plus qu'un pilote de remplacement pour les périphériques USB n'ayant pas de support dans le noyau. Un petit coup d’œil à la page de manuel et on se rend compte que c'est véritablement un point d'accès permettant à l'espace utilisateur de dialoguer et de commander le périphérique, et ce, directement. La classique LibUSB n'est là que pour simplifier, et unifier, les opérations via une couche d'abstraction, mais dans l'absolu, vous pourriez attaquer le pseudo-fichier sans libs. Un ensemble d'ioctl sont directement décrits dans la manpage, permettant de dialoguer avec le matériel (USB_DO_REQUEST), d'explorer les « descriptions » (périphérique, configurations, interfaces, endpoints) ou, tout simplement, d'obtenir des informations (USB_GET_DEVICEINFO).
Et c'est précisément là qu'il est possible d'agir. En effet, comme devpubd ne nous remonte pas les informations utiles, si ce n'est le nom du périphérique (et donc l'entrée /dev/ correspondante), nous pouvons tout simplement y accéder pour collecter des informations. L'ioctl nous remplira une structure usb_device_info, ressemblant à ceci :
Plus, une tripotée de macros :
Créer un outil prenant en argument quelques options et un chemin vers une entrée /dev/ugenN.EE, où X est un numéro de périphérique et EE celui d'un endpoint, et nous retournant une information bien précise, comme un vendorID et/ou un deviceID USB, n'est pas bien difficile. Toutes les informations utiles se trouvent dans la struct usb_device_info. Il nous suffit donc d'ouvrir le pseudo-fichier :
Utiliser l'ioctl :
Et explorer le contenu de la structure pour retourner à l'utilisateur la donnée demandée. On arrive ainsi à obtenir différentes sorties :
Partant de là, et après avoir intégré le binaire correctement dans le système (avec pkgsrc-wip, par exemple), nous pouvons produire un petit script :
Le script est relativement simple puisqu'à l'arrivée d'un nouveau périphérique ugen, l'entrée /dev/ correspondante est déduite puis utilisée avec chkugenids pour obtenir les informations détaillées. Celles-ci sont ensuite comparées, dans une boucle for, avec une liste d'ID et, si une correspondance est validée, les permissions sont modifiées. Ainsi, si un périphérique ugen n'est pas dans la liste, il est simplement ignoré. Nous avons maintenant le niveau de granularité nécessaire, avec un /dev statique, mais presque équivalent à ce qu'on trouve dans FreeBSD (au niveau de ugen uniquement).
Les sources de chkugenids sont disponibles, sous licence BSD 2-Clause, dans mon GitLab [2] et un port pkgsrc est présent dans pkgsrc-wip [3].
3. OpenBSD : hotplugd
La solution d'OpenBSD est assez similaire à celle de NetBSD, ce qui semble normal étant donné l'origine du système, mais un certain nombre de différences sont présentes et importantes. Ici, il ne s'agit pas de devpubd mais de hotplugd, qui sera activé d'un simple rcctl enable hotplugd (en root, bien entendu). Il s'agit, là aussi, d'un daemon qui va se charger de scruter l'activité du noyau, mais ici via le pseudo-fichier /dev/hotplug, puis utiliser les événements détectés pour appeler l'un ou l'autre des scripts, /etc/hotplug/attach ou /etc/hotplug/detach. Comme vous pouvez le constater, ici la division entre un type d'événement ou un autre est déjà faite, mais deux arguments sont cependant passés à l'un ou l'autre script :
- une classe de périphérique, correspondant à ce qui est décrit dans sys/device.h ;
- et un nom de périphérique tel qu'utilisé, par exemple, par MAKEDEV, complété d'un numéro.
On retrouve également ugen, représentant les périphériques USB n'ayant de pilotes dédiés et c'est bien ainsi qu'on retrouve, par exemple, un lecteur NFC USB. La page de manuel de hotplugd donne un exemple de script à partir duquel on pourra rapidement construire un squelette pouvant servir de base de travail :
Nous avons déjà ajouté le nécessaire pour réagir à l'apparition d'un nouveau convertisseur USB/série, mais étant donné que les /dev/ttyU* ont déjà pour groupe dialer avec des permissions 660, nous n'avons rien à faire, si ce n'est de mettre le bon utilisateur (moi) dans ce groupe. Le présent script se contentera donc d'indiquer l'événement dans /var/log/messages :
Oui, ma machine OpenBSD s'appelle « Posson », parce que c'est amusant (si, si, je vous assure, ça me fait sourire à chaque login). Ce qui l'est moins en revanche, c'est ce qui se passe lorsqu'on tente d'appliquer la même stratégie que celle utilisée avec NetBSD, où chkugenids entre en jeu. Bien sûr, le code fonctionnera de la même manière et nous permettra d'identifier le périphérique via ses ID. Les permissions sont alors bien changées sur les /dev/ugenN.* correspondants, mais :
En fouillant un peu, on se rend alors compte que donner des permissions en écriture sur /dev/ugenN.* n'est pas suffisant. Nous avons aussi /dev/usb* qui entre en jeu et la commande usbdevs nous apporte des compléments d'information intéressants :
Notre périphérique est bien là, à l'adresse 2 sur le cinquième contrôleur USB de la machine (/dev/usb4). Changer les permissions sur ce fichier, en autorisant les accès lecture/écriture au groupe wheel, dont je fais partie, règle parfaitement le problème et permet immédiatement d'utiliser nfc-list. En tout état de cause, il semblerait donc que notre script hotplugd doive également changer les permissions sur l'entrée correspondante (le problème semble venir en réalité de la LibUSB, sous-jacente à la LibNFC, qui génère des I/O nécessitant ces permissions. Ceci demandera davantage d'investigations).
La question est donc alors : comment trouver quelle entrée dans /dev correspond effectivement à notre périphérique étant apparu sous un nom ugenN.EE ? Et la réponse est un nouvel outil.
3.1 findugendev, le copain de chkugenids
En dehors du fait que je ne suis vraisemblablement pas doué pour trouver des noms à mes outils, nous n'avons ici rien de bien exceptionnel par rapport à chkugenids. En réalité, le fonctionnement est quasi identique, si ce n'est que ce n'est pas un seul fichier /dev qui est ouvert, mais tous ceux susceptibles de correspondre à notre recherche, dans une simple boucle while sur ce que retourne opendir/readdir. Là, nous filtrons sur les noms « ugen » et « usb » pour utiliser l'ioctl adéquate.
Et ici, nous avons à la fois une subtilité et une chance, car nous avons USB_GET_DEVICEINFO pour /dev/ugen* et USB_DEVICEINFO pour /dev/usb*, mais les deux nous permettent d'obtenir la même struct usb_device_info. Attention cependant, les deux ioctl ne s'utilisent pas de la même manière. Dans le cas de USB_GET_DEVICEINFO, celle-ci est remplie automatiquement, mais comme un /dev/usb* est un point d'accès vers le contrôleur, et non un périphérique, nous devons, avec USB_DEVICEINFO, spécifier à quel périphérique nous nous adressons. Ceci se fait en renseignant le membre usb_device_info.udi_addr avant l'appel à l'ioctl, ce que nous pouvons faire sous la forme d'une boucle :
Nous essayons toutes les adresses possibles (jusqu'à USB_MAX_DEVICES) et si l'ioctl fonctionne, alors nous pouvons inspecter le contenu de la struct devinfo qui aura été complétée. En dehors de ce point, le processus est donc assez simple et très similaire à chkugenids. De plus, findugendev fonctionnera également sous NetBSD (et même FreeBSD, tout comme chkugenids).
Nous pouvons alors combiner les deux outils pour composer notre script attach comme suit :
Nous n'avons même pas besoin de redémarrer hotplugd et le script sera directement utilisé. Et effectivement, la connexion d'un des périphériques listés dans le tableau permet d'appliquer les ajustements et donc de donner directement accès au matériel avec les outils complémentaires de la LibNFC :
4. Limitations, if defined, et la vraie unique solution, ou pas
Vous me demanderez sans doute « Quid des déconnexions ? », en particulier dans ce dernier cas de figure, avec OpenBSD. Et vous aurez raison, puisque ceci n'est absolument pas traité et, de ce fait, conduira forcément, tôt ou tard, à avoir des permissions en lecture/écriture pour les membres du groupe wheel sur n'importe quel bus USB où un lecteur aura été connecté par le passé. C'est un problème non négligeable et difficilement corrigible avec OpenBSD. En effet, FreeBSD et NetBSD ont, respectivement, soit une gestion plus complète, soit aucun problème d'accès à /dev/usb*. Ils peuvent, de ce fait, parfaitement rétablir les permissions lors de la déconnexion, puisque l'entrée /dev/ugen* est également précisée.
Dans le cas d'OpenBSD, le périphérique n'est plus là et nous n'aurons alors aucun moyen de savoir où il était branché. Ainsi, à moins de conserver une trace dans un fichier temporaire, ce qui pose d'autres problèmes en termes de synchronisation (je ne parle même pas de plusieurs occurrences des mêmes VIP:PID), je ne vois pas vraiment comment aborder la problématique.
Les deux outils développés, que sont chkugenids [2] et findugendev [4], sont davantage de petits hacks rapides qu'une réelle solution. Il est intéressant, cependant, de constater que la proximité technique entre les trois OS permet de n'avoir qu'un seul fichier source et de gérer les headers et les cas particuliers au besoin. Exemple :
usb_ioctl.h est le header de FreeBSD, car oui, les outils fonctionnent également avec ce système, même si usb_device_info est légèrement différent :
En gérant judicieusement les tests, nous arrivons ainsi, en prime, à obtenir une alternative à lsusb (GNU/Linux), usbdevs (OpenBSD et NetBSD) ou usbconfig (FreeBSD), pour lister les périphériques. Ceci peut éventuellement être intéressant, même sans implication des daemons. Exemple :
C'est un bonus sympathique (presque autant qu'inutile), mais en ce qui concerne NetBSD et OpenBSD, la réalité est qu'il faudrait soit porter le devd de FreeBSD (en prenant en compte le fait que /dev soit statique), soit faire évoluer devpubd et/ou hotplugd pour leur faire remonter davantage d'informations aux scripts, peut-être sous la forme d'une série clé/valeur sérialisée, changeant en fonction du type de périphérique concerné. Mais on parle ici de quelque chose de pleinement intégré au système, et qui nécessite donc une implication personnelle qui va au-delà du petit hack rapide...
5. Pour finir, est-ce bien nécessaire ?
Comprenez bien que cette approche, en particulier avec NetBSD et OpenBSD, est loin d'être raisonnablement nécessaire, si on y réfléchit un peu. En effet, ces deux systèmes utilisent un /dev entièrement statique et une politique de permissions réglera parfaitement le problème d'une façon plus simple. Il suffit de mettre les utilisateurs « méritants » dans le bon groupe et de changer les permissions de façon définitive, que ce soit sur /dev/ugen* ou /dev/usb* (dans le cas d'OpenBSD). Et rien ne vous empêche de créer un groupe usb ou usbdev, tout comme il existe déjà un groupe dialer (OpenBSD également).
Ce qui soulève alors une question d'ordre philosophique, voire psychologique : tenter d'améliorer cela en automatisant le changement de permissions (ou toutes autres manipulations qu'on peut imaginer sur la même base) n'est-elle pas simplement la manifestation d'un désir malsain, consistant à vouloir inconsciemment reproduire ce qu'on connaît déjà d'un autre système ? Dénaturant alors la réelle philosophie de l'OS, sous prétexte de vouloir corriger un problème qui n'en est peut-être pas un.
Ceci vous rappelle-t-il quelque chose ? Des histoires de systèmes d'init, peut-être ? Mais non, c'est bien plus vaste que cela. En rédigeant dernièrement un autre article, traitant de la manière d'installer ces trois OS sur une unique carte MMC à destination d'une Raspberry Pi 4, je me suis frotté aux « installeurs » de chacun d'eux. En intégrant mentalement également ceux que je connais déjà du monde GNU/Linux (Debian, Ubuntu, Mint, etc.), je pense qu'on peut réellement voir apparaître un motif assez intéressant et surtout une grande diversité dans la manière de traiter la question.
De l'installeur graphique lancé depuis une distribution live Mint 21.1 aux quelques questions très spartiatement posées par l'installeur OpenBSD, nous avons le choix. Mais l'objectif est rempli parfaitement dans tous les cas. Mieux encore, l'installeur ressemble au système et à ce sur quoi il met l'accent et à quoi il attache de l'importance.
Qu'il s'agisse de gestion de périphériques « à la udev » ou de l'installation, ceci n'est rien d'autre que l'expression de la personnalité du système. Et vouloir à tout prix la changer n'est peut-être pas la meilleure chose à faire, parce que le risque de « devenir envahissant » ou de tout changer est considérable. C'est une pente glissante que d'autres ont déjà empruntée, et on connaît le résultat (oui, là je pense à regedit^Wsystemd).
La conclusion de cette réflexion est évidente, à mon sens. Je me satisferai finalement de ce que proposent ces systèmes, sans chercher à changer (ou réimplémenter) devpubd et/ou hotplugd pour en faire des devd, ou pire, des udev. L'exploration de la mécanique de gestion, des sources existantes, des ioctl, et le plaisir d'implémenter deux utilitaires très modestes (à l'avenir incertain), est bien suffisant et permet déjà de toucher du doigt le réel but de l'opération : faire plus ample connaissance avec ces OS et en absorber la philosophie.
Références
[1] https://wiki.gentoo.org/wiki/Gentoo_Without_systemd#The_udev_situation
[2] https://gitlab.com/0xDRRB/chkugenids