Recent changes to udev mean that it is now a requirement to have the partition containing the /usr filesystem mounted prior to system boot, requiring usr and root to be on the same partition (which is Red Hat’s preferred solution), or to mount /usr prior to booting from an initrd.
I’ve successfully run Linux systems for many years without needing this additional complication, and I don’t plan to start changing the core boot process in order to comply with Red Hat’s (non-FHS compatible) vision of what a Linux system should look like.
The best alternative right now seems to be Busybox‘ mdev – a very simple hotplug agent and /dev tree maintenance tool which provides identical core functionality to udev.
However, the default configuration files provided with mdev are somewhat outdated and there isn’t much information out there documenting how to make the transition.
Please note: I’m looking at mdev from the point of view of a headless server where (for example) USB is available, but neither essential nor heavily used. A desktop system with many esoteric devices and running a graphical environment may need much more work to duplicate udev functionality to a usable level. mdev was never explicitly designed to run a system from, only to provide an initial or recovery-disk environment.
With Gentoo’s baselayout-2 base-system files installed and a ‘sysinit’ run-level, initial mounting of /proc and /sys is performed automatically, and so the only actions needed to commence using mdev are:
rc-update del udev sysinit
rc-update add mdev sysinit
(The ‘pam‘ USE-flag is incompatible with a statically-linked build)
Whilst this will allow the system to reboot successfully, there will be some odd device-nodes below /dev, and some nodes will be mis-named or not in the correct directories. ‘lsusb‘ will not work, for example. Additionally, interface renaming did not work for me without further tweaks.
When mdev is instructed to create sub-directories, it seems to create parent directories with permissions 777. A work-around is to pre-create known required parent directories:
+++ /etc/init.d/mdev
@@ -60,7 +60,7 @@ seed_dev()
[ -e /proc/kcore ] && ln -snf /proc/kcore /dev/core
# Create problematic directories
- mkdir -p /dev/pts /dev/shm
+ mkdir -p /dev/pts /dev/shm /dev/cpu /dev/bus/usb
}
mount_it()
Update: A much more obvious way to handle this is in mdev rules, as below.
If interface renaming is required, then a new /etc/mactab is needed to map MAC addresses to names:
# MAC addresses
ef0 00:0d:b9:xx:xx:xa
ef1 00:0d:b9:xx:xx:xb
ef2 00:0d:b9:xx:xx:xc
# NB: bridges use the MAC address of their primary slave - this will cause
# "nameif" to attempt to rename the bridge rather than the interface if a
# bridge is active when invoked.
… and even though mdev is instructed to call ‘nameif‘ when a new network device appears, I also had to add this function to ‘/etc/conf.d/net‘:
[ "${IFACE}" = "lo" ] && return 0
[ ! -d /dev/.udev -a -n "$( echo "${IFACE}" | grep -v "^br[0-9]\+$" )" ] && {
einfo "Calling 'nameif' to set interface names"
/sbin/nameif
}
return 0
} # preup
The ‘usbdev‘ script provided with mdev handles old-style USB device names such as ‘/dev/usbdev1.1_ep00‘, but doesn’t handle entries such as ‘/sys/devices/pci0000:00/0000:00:0f.4/usb2/2-1‘. The following new ‘/lib/mdev/usb‘ script handles modern USB device names:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | #!/bin/sh if [ -w "kmsg" ]; then LOGFILE="kmsg" else LOGFILE="mdev_usb.log" fi echo >>"$LOGFILE" "$$ $( date +"%T.%N" ) mdev usb helper started as '$0' in '$( pwd )', MDEV '$MDEV', ACTION '$ACTION', DEVPATH '$DEVPATH', SUBSYSTEM '$SUBSYSTEM', SEQNUM '$SEQNUM'" [ -n "$MDEV" ] || exit 0 [ -n "$DEVPATH" ] || exit 0 [ "$SUBSYSTEM" = "usb" ] || exit 0 # add zeros to device or bus function add_zeros() { case "$( echo "$1" | wc -L )" in 1) echo "00$1" ;; 2) echo "0$1" ;; *) echo "$1" ;; esac return 0 } # e.g. DEVPATH=/devices/pci0000:00/0000:00:0f.4/usb2/2-1, MDEV=2-1 BUS="" USB_DEV="" if [ -d /sys/devices ]; then BUS="$( add_zeros "$( cat "/sys$DEVPATH/busnum" 2>/dev/null )" )" USB_DEV="$( add_zeros "$( cat "/sys$DEVPATH/devnum" 2>/dev/null )" )" fi if [ -z "$BUS" ]; then BUS="$( add_zeros "$( echo "$DEVPATH" | cut -d'/' -f 5 | sed 's/^usb//' )" )" fi if [ -z "$USB_DEV" ]; then USB_DEV="$( add_zeros "$( echo "$MDEV" | cut -d'-' -f 1 )" )" fi #USB_FUNC="$( add_zeros "$( echo "$MDEV" | cut -d'-' -f 2 )" )" # try to load the proper driver for usb devices case "$ACTION" in add|"") echo >>"$LOGFILE" "$$ $( date +"%T.%N" ) Performing 'add' ACTION" # move usb device file if [ ! -d "bus/usb/$BUS" ]; then echo >>"$LOGFILE" "$$ $( date +"%T.%N" ) WARNING USB bus directory 'bus/usb/$BUS' doesn't exist - is USB initialised?" mkdir -p "bus/usb/$BUS" \ && echo >>"$LOGFILE" "$$ $( date +"%T.%N" ) mkdir succeeded for 'bus/usb/$BUS'" \ || { echo >>"$LOGFILE" "$$ $( date +"%T.%N" ) mkdir failed for 'bus/usb/$BUS'" ; exit 0 ; } fi mv "$MDEV" "bus/usb/$BUS/$USB_DEV" \ && echo >>"$LOGFILE" "$$ $( date +"%T.%N" ) mv succeeded for 'bus/usb/$BUS/$USB_DEV'" \ || { echo >>"$LOGFILE" "$$ $( date +"%T.%N" ) mv failed for 'bus/usb/$BUS/$USB_DEV'" ; exit 0 ; } ;; remove) echo >>"$LOGFILE" "$$ $( date +"%T.%N" ) Performing 'remove' ACTION" # remove device file and possible empty dirs if rm -f "bus/usb/$BUS/$USB_DEV" 2>/dev/null \ && echo >>"$LOGFILE" "$$ $( date +"%T.%N" ) rm -f 'bus/usb/$BUS/$USB_DEV' succeeded" then rmdir -p "bus/usb/$BUS" 2>/dev/null \ && echo >>"$LOGFILE" "$$ $( date +"%T.%N" ) rmdir -p 'bus/usb/$BUS' succeeded" \ || { echo >>"$LOGFILE" "$$ $( date +"%T.%N" ) rmdir -p 'bus/usb/$BUS' failed" ; exit 0 ; } else echo >>"$LOGFILE" "$$ $( date +"%T.%N" ) rm -f 'bus/usb/$BUS/$USB_DEV' failed" exit 0 fi ;; esac echo >>"$LOGFILE" "$$ $( date +"%T.%N" ) usb helper completed successfully" exit 0 |
Finally, to tie this all together, I use this ‘/etc/mdev.conf‘, which adds some additional device handling and also some clarification of the available syntax based on the Busybox source:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | # Provide user, group, and mode information for devices. If a regex matches # the device name provided by sysfs, use the appropriate user:group and mode # instead of the default 0:0 660. # # Syntax: # [-]devicename_regex user:group mode [>|=path] [@|$|*cmd args...] # # Leading minus in 1st field means "don't stop on this line", otherwise # search is stopped after the matching line is encountered. # Leading @ allows specification as @major,minor[-minor2] for disambiguation # # =: move, >: move and create a symlink, !: don't create node # @|$|*: run $cmd on delete, @cmd on create, *cmd on both # support module loading on hotplug $MODALIAS=.* root:root 660 @/sbin/modprobe "$MODALIAS" # null may already exist; therefore ownership has to be changed with command null root:root 666 @/bin/chmod 666 $MDEV zero root:root 666 full root:root 666 random root:root 644 urandom root:root 644 hwrandom root:root 644 grsec root:root 660 kmem root:kmem 640 kmsg root:root 600 mem root:kmem 640 port root:kmem 640 # console may already exist; therefore ownership has to be changed with command console root:tty 600 @/bin/chmod 600 $MDEV ptmx root:tty 666 pty.* root:tty 660 -cpu.* root:root 755 @/bin/mkdir -pm 755 cpu ; /bin/rm $MDEV cpu([0-9]+) root:root 444 =cpu/%1/cpuid -msr.* root:root 755 @/bin/mkdir -pm 755 cpu ; /bin/rm $MDEV msr([0-9]+) root:root 600 =cpu/%1/msr microcode root:root 600 =cpu/ # Typical devices tty root:tty 666 tty[0-9]+ root:tty 620 vcsa?[0-9]* root:tty 660 ttyS[0-9]+ root:uucp 660 ttyprintk root:root 600 # block devices ram([0-9]+) root:disk 660 >rd/%1 loop([0-9]+) root:disk 660 >loop/%1 sd[a-z].* root:disk 660 */lib/mdev/usbdisk_link #hd[a-z][0-9]* root:disk 660 */lib/mdev/ide_links md[0-9]+ root:disk 660 @/bin/mkdir -pm 755 md ; /bin/ln -sf ../$MDEV md/${MDEV/md} #sr[0-9]+ root:cdrom 660 @/bin/ln -sf $MDEV cdrom #fd[0-9]+ root:floppy 660 bsg/.* root:root 600 =bsg/ # net devices -net/.* root:root 600 @/sbin/nameif tun[0-9]* root:root 666 =net/ tap[0-9]* root:root 666 =net/ # i2c i2c-([0-9]+) root:root 600 >i2c/%1 i2c([0-9]+) root:root 600 >i2c/%1 # usb bus devices -usb.* root:usb 755 @/bin/mkdir -pm 755 bus/usb ; /bin/chmod 755 bus ; /bin/rm $MDEV usb([0-9]) root:usb 664 =bus/usb/00%1/001 usb([1-9][0-9]) root:usb 664 =bus/usb/0%1/001 usb([1-9][0-9]{2}) root:usb 664 =bus/usb/%1/001 # usb devices ([0-9]+)-([0-9]+) root:usb 664 */lib/mdev/usb hiddev[0-9]+ root:root 600 =usb/ hidraw[0-9]+ root:root 600 # Traditionally, USB devices appeared as, e.g., '/dev/usbdev1.1_ep00' usbdev[0-9]\.[0-9] root:root 664 */lib/mdev/usbdev usbdev[0-9]\.[0-9]_.* root:root 664 # misc stuff #misc/.* nobody:nogroup 0 ! #rtc root:root 600 >misc/ rtc0 root:root 600 @/bin/ln -sf $MDEV rtc # input stuff event[0-9]+ root:root 640 =input/ mice root:root 640 =input/ mouse[0-9]+ root:root 640 =input/ ts[0-9]+ root:root 600 =input/ # Less typical devices fuse root:root 666 #ttyLTM[0-9]+ root:dialout 660 @/bin/ln -sf $MDEV modem #ttySHSF[0-9]+ root:dialout 660 @/bin/ln -sf $MDEV modem #slamr root:dialout 660 @/bin/ln -sf $MDEV slamr0 #slusb root:dialout 660 @/bin/ln -sf $MDEV slusb0 |
The other helpful aspect of this approach is that reverting to udev is simply a matter of removing ‘mdev‘ from the sysinit run-level, and re-adding ‘udev‘ in its place.
These are all the fixes needed for the PC Engines ALIX 2d13 router (now with integrated RTC – the previous 2c3 board died, and in the meantime the product was upgraded). I’ll update this post as I roll-out mdev to the Storage and Infrastructure servers. Please feel free to post your success (or otherwise ;) in the comments below!
April 25th, 2012 at 7:51 am
Update: Add ‘microcode‘ entry.
Known potential issues, compared to udev‘s /dev population:
/dev/md/* devices not created – but this is deprecated?autofs device not created, but udev shouldn’t be generating this?/dev/sg* devices not created to match /dev/sd* (and others?);Update: /dev/md/* devices are now auto-created
April 25th, 2012 at 10:35 pm
… so it appears that udev was auto-creating /dev/sg* device-nodes even though the sg module wasn’t loaded (as with autofs, presumably).
The other two items save for i2c appear to be udev-specific constructs, which I’ve not found anything else using.
Update: My original mdev-based system has no problems with i2c, so it appears to a quirk specific to the second system…
Update: Ah – the first system does actually have an i2c device under /sys whilst the second doesn’t… and MAKEDEV was missing, breaking sensors-detect.
May 5th, 2012 at 2:10 pm
Update: Updated ‘usb‘ script (above) to prefer ‘busnum‘ and ‘devnum‘ from sysfs, if available.
August 3rd, 2012 at 7:33 am
I noticed your comment at https://wiki.gentoo.org/wiki/Talk:Mdev Some of the instructions may have been redundant, but were there to cover as many cases as possible. I’ve also gone one step further, and gotten auto(un)mounting of USB devices working under mdev (no GUI required). See https://wiki.gentoo.org/wiki/Mdev/Automount_USB It’s still beta (“Work In Progress”) but has been working fine for me for a while.