/dev/ttyACM not working on reboot

Using Openhab 4.1.0 on Ubuntu with a couple of USB sticks, Aeotech Z-stick gen5 and a Conbee.
Openhab is running on docker and is restarted always and on boot. Also using Portainer.

On power outages or other reboots Openhab restarts but I’m not able to use the usb ports until I manually run this:

sudo docker exec \
    -d \
    openhab \
    /bin/chmod o+rw /dev/ttyACM0

I have before this command verified that the /dev/ttyACM0 exists in the docker container (it is configured this way with portainer):

crw------- 1 root dialout 166, 0 Feb  5 14:35 ttyACM0
crw------- 1 root dialout 166, 1 Feb  5 14:56 ttyACM1

After running the chmod command the devices gets these rights:

crw-rw-rw- 1 root dialout 166, 0 Feb  5 14:35 ttyACM0
crw-rw-rw- 1 root dialout 166, 1 Feb  5 14:56 ttyACM1

If i remove the rw rights from docker bash with chmod o-rw on the devices the rw permission is removed but the communication with the devices keep working.

I have this inside docker:

uid=998(openhab) gid=997(openhab) groups=997(openhab)

And this outside docker:

uid=998(openhab) gid=997(openhab) groups=997(openhab),20(dialout)

The user and group does not seem to be the issue because they are not altered with the command that solves the problem. Or ?

Can I get some advice on how to implement this automatically and avoid having to manually get the devices on usb to work properly on reboot ?

the openhab user inside the container needs to be in the dialout group too. It’s inside the container that the file permissions are enforced.

That’s only half the problem though. What are the permissions on /dev/ttyACM0 outside the container? If the permissions of the file outside the container only allow root to read/write I’m not sure if you can override that inside the container. You might need something (e.g. a cron job that runs at system start) to add permissions to the file on the host so the group can read/write.

Finally, what’s your docker command? Are you passing rwm permissions when mounting the device?

You’ve given “other” full permissions on the file meaning you’ve taken the user and the group out of the picture. Any user now has permission to read and write to the file. If you want to test if the user and group work, use /bin/chmod g+rw /dev/ttyACM0. That will only add rw permissions for members of the group, not everyone.

this is outside of the container (on the host…when working):

crw-rw---- 1 root dialout 166, 0 Feb  5 16:20 /dev/ttyACM0

This is the command when starting the container:

/entrypoint gosu openhab tini -s ./start.sh

I’m a bit puzzled by the fact that openhab user does not get the group dialout from the host and that when i remove the rw rights from “o” - others the usb sticks keep working.

The container has it’s own passwds file meaning it has it’s own users. It doesn’t inherit the users from the host. It’s like having the same user on two totally different machines. They can even have the same UID but they are managed separately on each machine.

This sort of isolation is the whole point of containers.

OH already has the file open for read/write. Changing the permissions on the file changes who can open that file from this point forward but it doesn’t kick any services who already have the file open off. You need to restart the container and then only add permissions for the group. Then, assuming the the openhab user inside the container is not a member of dialout, you’ll find OH cannot open that file any more.

Good pointers! Thankyou.

Is there a way with the official Openhab image to set two groups to the openhab users on creation of the container ? I did find the dialout group already on the image/container with GID 20. I see that userID and groupID is specified as env variables. I was unable to set 997 and 20.

As far I remember linux ids are mapped as is between host and container. While you might not have enough of hooks on the container side, you can prepare host by using uids expected by container. I know it is a bit hacky.
Other way is doing UID mapping with existing tools: Align user IDs inside and outside Docker with subuser mapping - Linux-natives, which looks a bit more “rounded”.

yeah my host has the same group ID (20) for dialout as the Openhab image is prepared. Do I have to use something called hooks to get the 998 openhab user to have the dialout group assigned ?

I tend to do it the lazy way. I just mount /etc/passwd into the container and make sure the user on the host matches the user running in the container. It’s not the safest of things to do if you are trying to have a secure container (no reason to expose all the host users to the container) but I’m not as worried about that sort of thing so accept the risk.

I also usually pass in localtime and timezone to ensure the container matches the time of the host.

Note this approach doesn’t work for all containers, but it works for enough that I usually try it by default.

I’m a little surprised it’s not already which made me think maybe you are not using the official image or something else is going on.

I’ve not seen it mentioned since I asked to I’ll ask again, does the docker command that starts the container pass in the device with RWM permissions? Devices work the same as volumes. They get mounted into the container and you can set the permissions on what the container can do. I’m not certain the m option is required for a serial device or not but the rw is required.

What’s odd though is that the device should have rwm permissions by default. Without the docker run command it’s impossible to say what’s going on.

Sorry. This is the full command.

sudo docker run
  --name openhab
  --net=host 
  -v /etc/localtime:/etc/localtime:ro         
  -v /etc/timezone:/etc/timezone:ro         
  -v /opt/openhab/conf:/openhab/conf         
  -v /opt/openhab/userdata:/openhab/userdata         
  -v /opt/openhab/addons:/openhab/addons         
  --device=/dev/ttyACM0         
  --device=/dev/ttyACM1        
  -d         
  -e USER_ID=998         
  -e GROUP_ID=997      
  -e CRYPTO_POLICY=unlimited     
  --restart=always         
  openhab/openhab:4.1.0

Seems like I’m at least passing in the devices, maybe not the RWM permissions correctly ? This is mainly copied from the Openhab docker documentation. And as far as I know I am using the official image like this:

sudo docker pull openhab/openhab:4.1.0-debian

I use the following. I don’t know if it will make a difference at least on the file permissions.

--device=/dev/ttyACM0:/dev/ttyACM0:rwm

Problem comes from missing group assignment to openhab user in the container. Looking at docker entrypoint I think it should be there if you specify USER_ID environment variable.

Have a look on line 32 of entrypoint script:

If USER_ID is set there is entire block of group assignments. Note if I read script properly - the NEW_GROUP_ID seem to be optional (?). Also there is number of groups assigned later on.

Another point - later part of entrypoint script attempts to load some scripts from /etc/cont-init.d/ directory if its available. So you might actually have a “hook” to start with, if you mount your script under this location.

Aha, I did see the /etc/cont-init.d/ being mentioned in the docs but was not able to understand it fully.

Correct. If it’s not defined UID of 9001 is used IIRC.

And that’s where I’m a little confused with the situation because just about every “standard” GUI for dialout gets added to the user. So it’s a mystery why the user doesn’t have it.

There should be a ton of groups on the openhab user inside the container.

It’s all a little weird.

So I added this bash script to /etc/cont-init.d/:

#!/bin/bash

# Check if the group dialout exists
if grep -q "^dialout:" /etc/group; then
    echo "Group dialout exists."
    # Add the user openhab to the group dialout with GID 20
    usermod -aG dialout -g 20 openhab
else
    echo "Group dialout does not exist. Please create the group manually first."
fi

and added this -v /etc/cont-init.d:/etc/cont-init.d to my startup script as per this documentation: https://github.com/openhab/openhab-docker/blob/main/README.md#executing-shell-scripts-before-openhab-is-started

When started, container then have this on user openhab
uid=998(openhab) gid=20(dialout) groups=20(dialout)

And it now seems to work.

Question, shouldn’t it be both openhab group and dialout group added to the openhab user ?

Usually. I’m wondering if something more fundamental is going on.

After all, the entrypoint.sh script also added user openhab to dialout (20).

I wonder if user 998 already exists inside the container.

if ! id -u openhab >/dev/null 2>&1; then

This line means the user will only have all those different versions of the dialout groups (and others) added to it if it doesn’t already exist. If UID 998 already exists inside the container it wouldn’t be a member of dialout and this block of code wouldn’t run.

What happens if you run the container without setting the user ID environment variable?

After doing so, what do you get when running id -u openhab inside the container?

What do you get when running `id -u 998?

What do you see in the logs from the container? There are some lines echoed out by the entrypoint.sh script which will appear in the docker logs. docker logs openhab. You can add the -f option to follow the logs.

How can this be ? inside the image from before the reboot ? Isn’t the container “fresh” from the start every reboot. Or do you mean that the image is faulty from the source ?

The container and Openhab starts. Devices does not seem to work.

9001

id: ‘998’: no such user

+ echo 'Starting with openhab user id: 9001 and group id: 997'
Starting with openhab user id: 9001 and group id: 997
+ id -u openhab
++ getent group 997
+ '[' -z '' ']'
Create group openhab with id 997
+ echo 'Create group openhab with id 997'
+ groupadd -g 997 openhab
+ echo 'Create user openhab with id 9001'
+ adduser -u 9001 --disabled-password --gecos '' --home /openhab --gid 997 openhab
Create user openhab with id 9001
Warning: The home dir /openhab you specified already exists.
Adding user `openhab' ...
Adding new user `openhab' (9001) with group `openhab' ...
The home directory `/openhab' already exists.  Not copying from `/etc/skel'.
adduser: Warning: The home directory `/openhab' does not belong to the user you are currently creating.
+ groupadd -g 11 audio2
+ groupadd -g 14 uucp2
+ groupadd -g 16 dialout2
+ groupadd -g 17 audio3
+ groupadd -g 18 dialout3
+ groupadd -g 32 uucp3
+ groupadd -g 63 audio4
+ groupadd -g 490 dialout4
+ groupadd -g 492 audio5
+ groupadd -g 997 gpio
groupadd: GID '997' already exists
+ IFS='
	'
++ find /usr/lib/jvm -maxdepth 1 -name '*jdk*' -type d
+ export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
+ JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
+ '[' unlimited = unlimited ']'
+ echo 'Configuring Java unlimited strength cryptography policy...'
+ sed -i 's/^crypto.policy=limited/crypto.policy=unlimited/' /usr/lib/jvm/java-17-openjdk-amd64/conf/security/java.security
Configuring Java unlimited strength cryptography policy...
+ capsh --print
+ grep -E Current:.+,cap_net_admin,cap_net_raw,.+
+ rm -f '/var/lock/LCK..*'
+ rm -f /openhab/userdata/tmp/instances/instance.properties
+ NEW_USER_ID=9001
+ NEW_GROUP_ID=997
+ echo 'Starting with openhab user id: 9001 and group id: 997'
Starting with openhab user id: 9001 and group id: 997
+ id -u openhab
+ initialize_volume /openhab/conf /openhab/dist/conf
+ volume=/openhab/conf
+ source=/openhab/dist/conf
++ ls -A /openhab/conf
+ '[' -z 'automation
html
icons
items
misc
persistence
rules
scripts
services
sitemaps
sounds
things
transform' ']'
+ initialize_volume /openhab/userdata /openhab/dist/userdata
+ volume=/openhab/userdata
+ source=/openhab/dist/userdata
++ ls -A /openhab/userdata
+ '[' -z 'backup
cache
config
etc
hs_err_pid30.log
hs_err_pid43.log
jsondb
kar
logs

openhabcloud
persistence
secrets
tmp
uuid
zwave' ']'
++ cmp /openhab/userdata/etc/version.properties /openhab/dist/userdata/etc/version.properties
+ '[' '!' -z ']'
+ chown -R openhab:openhab /openhab
+ sync
+ '[' -d /etc/cont-init.d ']'
++ wc -l
++ ls '/usr/bin/s6-*'
+ '[' 0 == 0 ']'
++ find /etc/cont-init.d -type f
++ grep -v '~'
++ sort
+ sync
+ '[' false == false ']'
++ IFS=' '
++ echo gosu openhab tini -s ./start.sh
+ '[' 'gosu openhab tini -s ./start.sh' == 'gosu openhab tini -s ./start.sh' ']'
+ command=($@ server)
+ exec gosu openhab tini -s ./start.sh server
Launching the openHAB runtime...

Figure out if before worrying about how.

Given the code of the entrypoint.sh script, the only way that user would not be a member of the dialout group (among others) is if the user already existed before that script ran.

I think that’s the problem.

You are passing gid 997 to the container to be openhab’s group. But GID is “reserved” for the gpio group. It looks like it bails at that point and never gets to the adduser commands that actually add the openhab user to all of these groups.

Use a different non reserved ID for the group (e.g. get rid of the group id environment variable too. Or at least choose a GID different from 11, 14, 16, 17, 18, 32, 63, 490, and 997.

It is unknown to me why these groups are being created in the first place but clearly you cannot try to use one of these group IDs.

Yep you are completely right. Omitting the GID of 997 makes everything work as it should and I now have the devices working and the user Openhab has all these groups added as you mentioned earlier.

So all of this just because I managed to use a reserved id for gpio ? I wonder where I got 997 from, my guess is that it was assigned to my Openhab user from a previous install on my debian system without docker, could that be ?

Maybe. If you are trying to match the host user to the container version, when you created the openhab user on the host it assigned 998 and for some reason I never understood, the gid is usually the uid-1 (997 in this case).

I’m a little surprised the gid of 997 is so high. In my experience service user ids tend to start at 999 and go down from there as you create users and login users start at 1000 and go up from there. But it’s been a very long time since I let the OS choose my uids so don’t know if I’m misremembering or if that’s still the case.

This might be something worth filing an issue on the openhab-docker repo. I’m pretty basic when it comes to bash scripting but there has to be a way to not bail if it encounters a group that already exists and finish all those adduser commands that come later no matter what.

This is definitely an edge case.

Maybe even just adding a note to the docs would be enough.

1 Like