May 27

udev rules for multiple interfaces

I just had to write a udev rule for Linux to interpret between several different endpoints on a single cellular modem, specifically this modem.  This shows up in Linux as 5 ttyUSB ports.  In order to make sure that the ports do not change around on me, I checked the following with udevadm:

$ udevadm info -a -n /dev/ttyUSB1

Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

 looking at device '/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.1/4-1.1:1.0/ttyUSB1/tty/ttyUSB1':
 KERNEL=="ttyUSB1"
 SUBSYSTEM=="tty"
 DRIVER==""

 looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.1/4-1.1:1.0/ttyUSB1':
 KERNELS=="ttyUSB1"
 SUBSYSTEMS=="usb-serial"
 DRIVERS=="option1"
 ATTRS{port_number}=="0"

 looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.1/4-1.1:1.0':
 KERNELS=="4-1.1:1.0"
 SUBSYSTEMS=="usb"
 DRIVERS=="option"
 ATTRS{bInterfaceClass}=="ff"
 ATTRS{bInterfaceSubClass}=="ff"
 ATTRS{bInterfaceProtocol}=="ff"
 ATTRS{bNumEndpoints}=="02"
 ATTRS{supports_autosuspend}=="1"
 ATTRS{bAlternateSetting}==" 0"
 ATTRS{bInterfaceNumber}=="00"

 looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.1':
 KERNELS=="4-1.1"
 SUBSYSTEMS=="usb"
 DRIVERS=="usb"
 ATTRS{bDeviceSubClass}=="00"
 ATTRS{bDeviceProtocol}=="00"
 ATTRS{devpath}=="1.1"
 ATTRS{idVendor}=="1bc7"
 ATTRS{speed}=="480"
 ATTRS{bNumInterfaces}==" 8"
 ATTRS{bConfigurationValue}=="1"
 ATTRS{bMaxPacketSize0}=="64"
 ATTRS{busnum}=="4"
 ATTRS{devnum}=="11"
 ATTRS{configuration}==""
 ATTRS{bMaxPower}=="500mA"
 ATTRS{authorized}=="1"
 ATTRS{bmAttributes}=="80"
 ATTRS{bNumConfigurations}=="1"
 ATTRS{maxchild}=="0"
 ATTRS{bcdDevice}=="0232"
 ATTRS{avoid_reset_quirk}=="0"
 ATTRS{quirks}=="0x0"
 ATTRS{serial}=="0123456789ABCDEF"
 ATTRS{version}==" 2.00"
 ATTRS{urbnum}=="194649"
 ATTRS{ltm_capable}=="no"
 ATTRS{manufacturer}=="Android"
 ATTRS{removable}=="unknown"
 ATTRS{idProduct}=="1201"
 ATTRS{bDeviceClass}=="00"
 ATTRS{product}=="Android"

 looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb4/4-1':
 KERNELS=="4-1"
 SUBSYSTEMS=="usb"
 DRIVERS=="usb"
 ATTRS{bDeviceSubClass}=="00"
 ATTRS{bDeviceProtocol}=="01"
 ATTRS{devpath}=="1"
 ATTRS{idVendor}=="8087"
 ATTRS{speed}=="480"
 ATTRS{bNumInterfaces}==" 1"
 ATTRS{bConfigurationValue}=="1"
 ATTRS{bMaxPacketSize0}=="64"
 ATTRS{busnum}=="4"
 ATTRS{devnum}=="2"
 ATTRS{configuration}==""
 ATTRS{bMaxPower}=="0mA"
 ATTRS{authorized}=="1"
 ATTRS{bmAttributes}=="e0"
 ATTRS{bNumConfigurations}=="1"
 ATTRS{maxchild}=="8"
 ATTRS{bcdDevice}=="0000"
 ATTRS{avoid_reset_quirk}=="0"
 ATTRS{quirks}=="0x0"
 ATTRS{version}==" 2.00"
 ATTRS{urbnum}=="204"
 ATTRS{ltm_capable}=="no"
 ATTRS{removable}=="unknown"
 ATTRS{idProduct}=="0024"
 ATTRS{bDeviceClass}=="09"

 looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb4':
 KERNELS=="usb4"
 SUBSYSTEMS=="usb"
 DRIVERS=="usb"
 ATTRS{bDeviceSubClass}=="00"
 ATTRS{bDeviceProtocol}=="00"
 ATTRS{devpath}=="0"
 ATTRS{idVendor}=="1d6b"
 ATTRS{speed}=="480"
 ATTRS{bNumInterfaces}==" 1"
 ATTRS{bConfigurationValue}=="1"
 ATTRS{bMaxPacketSize0}=="64"
 ATTRS{authorized_default}=="1"
 ATTRS{busnum}=="4"
 ATTRS{devnum}=="1"
 ATTRS{configuration}==""
 ATTRS{bMaxPower}=="0mA"
 ATTRS{authorized}=="1"
 ATTRS{bmAttributes}=="e0"
 ATTRS{bNumConfigurations}=="1"
 ATTRS{maxchild}=="2"
 ATTRS{bcdDevice}=="0316"
 ATTRS{avoid_reset_quirk}=="0"
 ATTRS{quirks}=="0x0"
 ATTRS{serial}=="0000:00:1d.0"
 ATTRS{version}==" 2.00"
 ATTRS{urbnum}=="24"
 ATTRS{ltm_capable}=="no"
 ATTRS{manufacturer}=="Linux 3.16.0-4-amd64 ehci_hcd"
 ATTRS{removable}=="unknown"
 ATTRS{idProduct}=="0002"
 ATTRS{bDeviceClass}=="09"
 ATTRS{product}=="EHCI Host Controller"

 looking at parent device '/devices/pci0000:00/0000:00:1d.0':
 KERNELS=="0000:00:1d.0"
 SUBSYSTEMS=="pci"
 DRIVERS=="ehci-pci"
 ATTRS{irq}=="23"
 ATTRS{subsystem_vendor}=="0x1458"
 ATTRS{broken_parity_status}=="0"
 ATTRS{class}=="0x0c0320"
 ATTRS{companion}==""
 ATTRS{driver_override}=="(null)"
 ATTRS{consistent_dma_mask_bits}=="32"
 ATTRS{dma_mask_bits}=="32"
 ATTRS{local_cpus}=="00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,000000ff"
 ATTRS{device}=="0x1c26"
 ATTRS{uframe_periodic_max}=="100"
 ATTRS{enable}=="1"
 ATTRS{msi_bus}==""
 ATTRS{local_cpulist}=="0-7"
 ATTRS{vendor}=="0x8086"
 ATTRS{subsystem_device}=="0x5006"
 ATTRS{numa_node}=="-1"
 ATTRS{d3cold_allowed}=="1"

 looking at parent device '/devices/pci0000:00':
 KERNELS=="pci0000:00"
 SUBSYSTEMS==""
 DRIVERS==""

After I got the info, I noticed that the one thing that was different between all of the ttyUSB devices was ATTRS{bInterfaceNumber}.  So, I wrote a rule that looked like this:

ATTRS{idProduct}=="1201", ATTRS{idVendor}=="1bc7", ATTRS{bInterfaceNumber}=="00", SYMLINK+="cellular_1"

However, this did not produce the expected result of a new symlink.  It appears as though you can’t get the udev information for a product/vendor and the interface number in the same rule.  However, there are environment variables that are set which you can use.  If you run udevadm test, you can see what exactly the environment variables are that are set:

$ udevadm test --action=add `udevadm info -q path -n /dev/ttyUSB1`

...skip a bunch of data...
ACTION=add
DEVLINKS=/dev/serial/by-id/usb-Android_Android_0123456789ABCDEF-if00-port0 /dev/serial/by-path/pci-0000:00:1d.0-usb-0:1.1:1.0-port0
DEVNAME=/dev/ttyUSB1
DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.1/4-1.1:1.0/ttyUSB1/tty/ttyUSB1
ID_BUS=usb
ID_MODEL=Android
ID_MODEL_ENC=Android
ID_MODEL_ID=1201
ID_PATH=pci-0000:00:1d.0-usb-0:1.1:1.0
ID_PATH_TAG=pci-0000_00_1d_0-usb-0_1_1_1_0
ID_REVISION=0232
ID_SERIAL=Android_Android_0123456789ABCDEF
ID_SERIAL_SHORT=0123456789ABCDEF
ID_TYPE=generic
ID_USB_DRIVER=option
ID_USB_INTERFACES=:ffffff:ff4201:ff0000:080650:
ID_USB_INTERFACE_NUM=00
ID_VENDOR=Android
ID_VENDOR_ENC=Android
ID_VENDOR_FROM_DATABASE=Telit Wireless Solutions
ID_VENDOR_ID=1bc7
MAJOR=188
MINOR=1
SUBSYSTEM=tty
TAGS=:systemd:
USEC_INITIALIZED=91898933

These variables can also be used in udev rules to do differentiate between different devices.  The two that we are interested in are ID_VENDOR_ID and ID_MODEL_ID, as these correspond to ATTRS{idProduct} and ATTRS{idVendor}.  Our new rule becomes:

DRIVERS=="option", ATTRS{bInterfaceNumber}=="00", ENV{ID_MODEL_ID}=="1201", ENV{ID_VENDOR_ID}=="1bc7", SYMLINK+="cellular_1"

The DRIVERS part of the rule may not be strictly necessary, but it can’t hurt it.

Dec 22

Adventures in accidental chown

So earlier today I had to get Virtualbox up and running, and it was complaining about permissions not being set right.  For some reason, the permissions on my /usr directory were slightly messed up and not owned by root.  So, I did:

sudo chown root:root /usr

That didn’t fix the problem.  Going into the /usr directory, I also noticed that a few of the other folders weren’t owned by root.  So, let’s just go fix everything at once:

sudo chown -R root:root /usr

Okay, now let’s try starting Virtualbox!

Virtualbox now complains about not being setuid.  Okay, fine.  Let’s reinstall virtualbox, since it says that should fix it.

sudo dpkg -i virtualbox.deb
sudo: must be setuid root

Oops.

 

Turns out, doing the chroot dropped the setuid bit from all binaries in the /usr directory.

To fix this, you’ll have to boot from a rescue disk/CD of some kind, and change the permissions on sudo:

chmod 4755 /path/to/mounted/filesystem/usr/bin/sudo

Other programs will also be messed up at that point, but now that you have sudo you can change the permissions to what they should be, and/or reinstall. 🙂

Dec 04

Linux & Simplicity

A while ago, I posted some thoughts on Linux installing.  Just today, I came across this blog post, which covers kindof the same things.  The problem that seems to exist now is that there are multiple ways to do things.  Not that it’s a bad thing; but there’s no real standard way to do it.  Everybody wants to come up with their own way of doing things, as commented on in this article.  New tools are fine, but what should be happening is have these new tools simply provide new ways of accessing/modifying information, not as replacements.  Why is there a separate service that runs for adding users?  That utility has existed ever since UNIX came around.  It’s impossible to actually figure out what file is actually being used at any point in time.

This lack of standardization is really killing me here.  Just today, I was trying to install gnome-common for some development work, and it put it in /usr/local/share/aclocal.  Which is fine, except that the way aclocal is configured on Ubuntu 12.04 it should actually be /usr/share/aclocal.  WHY IS THIS DIFFERENT?!

This is, again, where Windows is better.  Each program has its own, special folder, typically in Program Files.  All the .exes, dlls, images, etc. go in there.  It’s essentially a self-contained unit.  You have the system registry to tell you information about programs, some variables needed, and where they are.  Granted, the system registry is abused quite often.

So, here’s basically what I think needs to happen:

  • Programs should have their own folder that they store all of their .so, executables, images, etc in.
  • Paths need to be better defined, i.e. where to put something, does it go in /usr, /etc, /local, what?  Currently, programs are scattered all over the place, and shared libs also
  • The only shared libraries that should go in the shared library folder(i.e. /usr/local/lib ) are actually shared libraries,  stuff that is used by more than one program.  If you use it only for your program, that goes in your program folder
  • There should be some kind of registry.  This would probably hold information like where to find something, and would essentially be an extension of environment variables, but for all users.  For example, let’s say that you have two different daemons that provide some service.  This registry would point at the daemon that you want to use – as long as their APIs are standardized, it shouldn’t matter which one you’re using.
  • APIs need to be standardized.  There doesn’t seem to be one “true” way to do anything.  As we can see, you can use one of several interfaces to do the same task, which all do something slightly different.

Maybe this would be a good time to split off something new from Linux.  In addition to doing the above standardization, this is also what I would like to see:

  • Get rid of X.  Sure, X is fine and all, but maybe Wayland would be better?  X is annoying to use, and it was designed for something else than what it is being used for now.  Something simpler, with built-in support for modern APIs(such as OpenGL) would be great.
  • Get rid of the SysV init interface.  Use Upstart instead, completely deprecate it.
  • Any new programs and APIs should not replace old programs, such as useradd or usermod.  They should simply provide a new way of doing it.

Well, that’s a mostly disorganized post.  Braindump complete.