8

I have two (and possibly in the future, more) USB serial devices which are identical (down to the serial number, unfortunately) - they're actually BTC miners. Currently they end up as ttyUSBX where X is 0, 1 or 2, as there's another unrelated USB serial device as well (which need not be worried about here).

I would like to write a udev rule which will assign them predictable names within /dev, like /dev/miner0 where the zero is an incrementing integer. I don't care which of them ends up as which, but I do need them to be within a predictable range which won't change.

Currently I have this:

SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", SYMLINK+="miner%n"

This is pretty close, as I end up with names like I want. The only problem is, since the miners and the third device can sometimes appear in a random order, I might end up with two of miner0, miner1 and miner2, but I never know which two (without manually looking). If I add any more non-miner usb serial devices (which is a possibility), it'll exacerbate the problem.

I had found reference to %e which looked like it did exactly what I wanted, but it doesn't seem to exist anymore.

How might I give these devices predictable names? I'd prefer not to tie them to their location on the USB bus as well if possible.


Further information/background

It's worth mentioning that I'm not that bothered what the names are, just that they be known and unchanging even if/when the device gets plugged into a different USB socket. I would simply forget the whole udev thing and use entries in /dev/serial/by-id, but as they have the same serial number, there's only one of them in there!

It's also worth mentioning that the reason for doing this is that the mining software needs to be told a list of devices to probe and find. I can just have it do all (it basically just finds all valid miners in the ttyUSB* range), but that annoys the non-miner device. So I need the names of the miners known ahead of time so I can configure it to use just those. Sadly it won't accept a wildcard (so just telling it to use /dev/miner* seems to be out of the question), hence this problem.

  • 2
    (commenting from a phone, so I can't look): Take a look at your distro's code that handles /dev/cdrom links, etc. You should be able to swipe that approach. It uses a file to persist changes, but can likely strip that out, or use one cleared on boot. – derobert Dec 14 '13 at 15:26
  • Thanks, that was a good thought that didn't occur to me. I've just looked into it though and it seems really rather complex. I think it might be too much effort given the current problem, but perhaps something I'd look at if I were dealing with tens of devices rather than single-figures. – Mark Embling Dec 14 '13 at 15:50
  • Had a break and taken another look and it's baffling. I see it's persisting to a file and you're right in that I wouldn't want that. I can't figure out the logic of how its figuring out the next available though, as it seems to be reading the file before its written and then writing to it after its used the output. Utterly baffling. And bash scripts aren't the easiest to follow at the best of times. – Mark Embling Dec 14 '13 at 19:43
  • Most Unix software does not take wild cards, the shell is used to expand them. Can you use another tool to expand the wildcards. – ctrl-alt-delor Dec 15 '13 at 13:50
  • @richard I'm not sure. Given what I've got from the below answer, it isn't essential as I've got a sensible sequence which I manually add the flags for in the script which launches the software. However ideally to solve this part too, I'd need to get from the wildcard (which would look like /dev/btcminer/*) to a list like this: -S /dev/btcminer/0 -S /dev/btcminer/1 <and so on if present> which are the parameters the software takes. – Mark Embling Dec 15 '13 at 14:18
4

This is untested in combination:

Add a udev rule to IMPORT{program}="/usr/local/sbin/unique-num /run/miner-counter 0 MINER_NUM" for your miners.

Then you could use a simple shell script, something like this somewhat tested program:

#!/bin/sh

if [ $# -ne 3 ]; then
    echo "Usage: $0 data-file initial var-name" >&2
    exit 1
fi

datfile="$1"
lockfile="$1.lck"
initial=$2
key="$3"

(
    flock -x 9
    num=$initial
    if [ -e "$datfile" ]; then
        read -r num < "$datfile"
    fi

    next=`expr $num + 1`;
    echo $next > "$datfile"

    echo "$key=$num"
) 9> "$lockfile"

Then you can use that udev environment variable to name your miners.

  • This helped immensely, thanks. I tweaked it a bit so the program returns just the number (and takes just the first two args) and then used it from within the PROGRAM option in my udev rule, whose output is them used to make the symlink name. I now have /dev/btcminer/0 and /dev/btcminer/1 which is (a slightly tweaked version of) what I was after. Thanks for this! :-) – Mark Embling Dec 15 '13 at 13:36
  • I should also mention that I had to change the shebang to #!/bin/bash as well. For some reason, sh claimed there was a syntax error ("unexpected word"). Dunno why or what sh is under Ubuntu, but that solved it. – Mark Embling Dec 15 '13 at 13:40
  • 1
    @MarkEmbling Turns out that SUS only requires numbers up to 9 to be supported. So if you change those two 16s to 9s, then it'll work in /bin/sh. At least with dash. (/bin/sh on this system is bash for some reason) – derobert Dec 15 '13 at 17:28
  • That's perfect, it now works perfectly with /bin/sh. Thanks again for this, I've learnt a lot in the last couple of days. Never touched udev at all before yesterday :-) – Mark Embling Dec 15 '13 at 18:43
2

The question already has an accepted answer, but I decided to share my variation of the solution provided by derobert.

My requirements were slightly different - in addition to providing "incrementing" index numbers to new devices - I wanted reacquire index numbers that were given up by devices that have been removed from the system.

The udev rule for setting up the environment variable would look something like this:

IMPORT{program}="/usr/local/sbin/unique-num /dev miner MINER_NUM"

In my solution, I do not use a file to keep track of the index, I simply loop over the existing and find the first available index:

/usr/local/sbin/unique-num script:

#!/bin/bash

if [ $# -ne 3 ]; then
    echo "Usage: $0 location prefix var-name" >&2
    exit 1
fi

location="$1"
prefix="$2"
key="$3"

needindex=1
index=0

while [ $needindex -eq 1 ]
do
        if [ ! -e $location/$prefix$index ]; then
                needindex=0
                echo "$key=$index"
        else
                (( index++ ))
        fi
done

This will, of course, print out the var-name with the first available index, for example if these already exist:

miner0
miner1
miner2

and then miner1 is detached from the system - we are left with:

miner0
miner2

Running the script will return:

MINER_NUM=1

...as this is now the first available index.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.