2

I have five ttyACM devices that change order on bootup. I also run a (poorly written) program that runs on mono, that will only accept a device of format ttyACM# (where # is a number in sequential order, starting at 0, based on ttyACM devices in the system).

I already already created a rule like this:

SUBSYSTEM=="usb", ATTRS{idProduct}=="0200", ATTRS{idVendor}=="0658", SYMLINK+="ttyACMradio" 

which creates a symlink with a name not accepted by the app (due to template name mismatch). I also tried calling the symlink ttyACM9, but that isn't accepted by the app (due to ttyACM numbers not contiguous).

Is it possible to force the NAME of the device (not a symlink) so I can force a consistant order of ttyACM devices? How do I do so? The only references to NAME I can find in udev documentation refer to ethernet devices.

I'm sure someone will say fix the app, but it's not mine and the vendor has no interest in helping linux users. I may also add/remove ACM devices in the future, so I don't want to hard code ACM numbers starting right after the last real one). BTW, running AlmaLinux 9

3 Answers 3

2

You cannot change the names of /dev/ttyACM0 devices. One solution, if your binary app allows it, is to interpose your own C routine which is called instead of the real open() in the C library. Your routine can check if the file to open is "/dev/ttyACM0" and if so it cancall the real open with the symlink filename. Here's such an interposer, called shim_open.c:

/* * https://unix.stackexchange.com/q/789103/119298 * capture calls to a routine and replace with your code * gcc -Wall -O2 -fpic -shared -ldl -o shim_open.so shim_open.c * LD_PRELOAD=/.../shim_open.so ./test */ #define _FCNTL_H 1 /* hack for open() prototype */ #define _GNU_SOURCE /* needed to get RTLD_NEXT defined in dlfcn.h */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <dlfcn.h> /* make open(f1,) do open(f2,0) using names from env $OLDNAME $NEWNAME */ int open(const char *pathname, int flags, mode_t mode){ static int (*real_open)(const char *pathname, int flags, mode_t mode) = NULL; static char *oldname, *newname; if (!real_open) { real_open = dlsym(RTLD_NEXT, "open"); char *error = dlerror(); if (error != NULL) { fprintf(stderr, "%s\n", error); exit(1); } oldname = getenv("OLDNAME"); newname = getenv("NEWNAME"); if(oldname==0 || newname == 0){ fprintf(stderr, "need to set in env OLDNAME NEWNAME\n"); exit(2); } } if (strcmp(pathname,oldname)==0) pathname = newname; fprintf(stderr, "opening: %s\n", pathname); return real_open(pathname, flags, mode); } 

Compile it with gcc -Wall -O2 -fpic -shared -ldl -o shim_open.so shim_open.c and use it with

LD_PRELOAD=/.../shim_open.so OLDNAME=/dev/ttyACM0 NEWNAME=/dev/ttyACMradio ./myprogram 

Note, however, that there are several versions of open(2) these days, so if this doesn't work you might need to run strace ./myprogram to find what variety is being called. Also, the program might fstat() the file descriptor to see if it is a device node and so on, so this may just be a first step.

1
  • Wow...cool answer. I was hoping for something way simpler, but still a very cool workaround. If no one has a magic bullet I may experiment with this. Commented Jan 5 at 20:21
2

The udev(7) man page says this about assigning values to the NAME= key (emphasis mine):

NAME

The name to use for a network interface. See systemd.link(5) for a higher-level mechanism for setting the interface name. The name of a device node cannot be changed by udev, only additional symlinks can be created.

Older versions of udev used to allow changing the actual name of any device node at creation time using the NAME= key, but it turned out that was an exploitable security vulnerability. (As usual, that's why we cannot have nice things.)

In current implementations, setting the NAME= key is restricted to network interface names only, to still allow the most widely-used non-abusable use case the old implementation had.

0

You cannot change device names, but you can create your own devices in /dev, and duplicate the major/minor numbers to effectively make a clone. You can do this with mknod or simply with a hardlink ln. If you want to invert the order of ttyACM0 and ttyACM1 for example, you can do

cd /dev ln ttyACM0 ttyACMx0 ln ttyACM1 ttyACMx1 rm ttyACM0 ttyACM1 ln ttyACMx1 ttyACM0 ln ttyACMx0 ttyACM1 rm ttyACMx0 ttyACMx1 

or even just rename them:

cd /dev mv ttyACM0 ttyACMx mv ttyACM1 ttyACM0 mv ttyACMx ttyACM1 

This may lead to some confusion when the devices are unplugged, if they are not done as a group, of course.

3
  • But the app won't accept devices name ttyACMx0. Bad app... Commented Jan 5 at 20:20
  • 1
    You don't use the name ttyACMx0. It is merely a temporary name for the swap. In this example, after the renaming you use name ttyACM0, but in the kernel this is now connected to device ttyACM1. Though you cannot change the internal kernel name, you can change this name in the filesystem; the filesystem name is tied to an inode which holds the major and minor device number that the kernel uses to find the right device. You can use mv to rename any device in /dev. Commented Jan 6 at 7:37
  • OK I see what you mean. Since I would have to script this (to run on reboot), and update the script when adding more devices, these seems like a tough solution. But it should work Commented Jan 6 at 12:38

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.