1

Problem:

I'm writing a Homebridge plugin that needs access to the Pi's GPIO.

This plugin uses the pigpio javascript wrapper (https://github.com/fivdi/pigpio) of the pigpio C library (https://github.com/joan2937/pigpio), which requires root privilege. I need those libraries to send waveforms through the GPIO.

When I run sudo homebridge, everything works fine.

However when Homebridge runs as a service sudo hb-service start, I get this error:

+---------------------------------------------------------+
|Sorry, you don't have permission to run this program.    |
|Try running as root, e.g. precede the command with sudo. |
+---------------------------------------------------------+

Which comes from the pigpio C library (https://github.com/joan2937/pigpio/blob/v75/pigpio.c#L7287-L7296) and more precisely from this piece of code:

fdMem = open("/dev/mem", O_RDWR | O_SYNC)

Even if I set the user of the homebridge service (in /etc/systemd/system/homebridge.service) as root:

[Service]
Type=simple
User=root
Group=root

And I check that the related processes (hb-service starts the homebridge process) are effectively owned by root:

$ ps -u -p 1843
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root      1843  0.5  3.3 155148 66880 ?        Ssl  15:20   0:08 hb-service
$ ps -u -p 1857
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root      1857  0.1  2.0 129844 40720 ?        Sl   15:20   0:03 homebridge

And /dev/mem has normal rights:

$ ls -l /dev/mem
crw-r----- 1 root kmem 1, 1 Apr 27 20:17 /dev/mem



What i've tried:

  • Let any user read and write in /dev/mem: sudo chmod 666 /dev/mem: same error
  • Add homebridge and pi users to the kmem group and let the kmem group read and write in /dev/mem: sudo usermod -aG kmem pi, sudo usermod -aG kmem homebridge, sudo chmod g+rw /dev/mem: same error


Environment:

  • Raspberry Pi 4 B
  • Raspbian Buster Lite fully upgraded headless
  • Node.js Version: v12.16.2
  • NPM Version: 6.14.4
  • Homebridge version: 1.0.2


The error only occurs when the script is started by the service. And even if the service has root privilege, it doesn't work. I have no idea what can cause this?

Any help is welcome!

Edited 2020-04-29 13:12 CEST:

  • Result of systemctl cat homebridge.service:
# /etc/systemd/system/homebridge.service
[Unit]
Description=Homebridge
Wants=network-online.target
After=syslog.target network-online.target

[Service]
Type=simple
User=root
Group=root
PermissionsStartOnly=true
WorkingDirectory=/var/lib/homebridge
EnvironmentFile=/etc/default/homebridge
ExecStartPre=-run-parts /etc/hb-service/homebridge/prestart.d
ExecStartPre=-/usr/lib/node_modules/homebridge-config-ui-x/dist/bin/hb-service.js before-start $HOMEBRIDGE_OPTS
ExecStart=/usr/lib/node_modules/homebridge-config-ui-x/dist/bin/hb-service.js run $HOMEBRIDGE_OPTS
Restart=always
RestartSec=3
KillMode=process
CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_CHOWN CAP_FOWNER CAP_DAC_OVERRIDE CAP_AUDIT_WRITE CAP_SYS_ADMIN
AmbientCapabilities=CAP_NET_RAW CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target
  • Result of systemctl status homebridge.service:
● homebridge.service - Homebridge
   Loaded: loaded (/etc/systemd/system/homebridge.service; enabled; vendor preset: enabled)
   Active: active (running) since Tue 2020-04-28 16:16:39 BST; 19h ago
  Process: 7928 ExecStartPre=/bin/run-parts /etc/hb-service/homebridge/prestart.d (code=exited, status=0/SUCCESS)
  Process: 7931 ExecStartPre=/usr/lib/node_modules/homebridge-config-ui-x/dist/bin/hb-service.js before-start $HOMEBRIDGE_OPTS (code=exited, status=0/SUCCESS)
 Main PID: 7938 (hb-service)
    Tasks: 22 (limit: 4035)
   Memory: 94.7M
   CGroup: /system.slice/homebridge.service
           ├─7938 hb-service
           └─9107 homebridge

Apr 28 16:16:39 raspberrypi systemd[1]: Starting Homebridge...
Apr 28 16:16:39 raspberrypi run-parts[7928]: Ensuring /var/lib/homebridge is owned by root
Apr 28 16:16:39 raspberrypi systemd[1]: Started Homebridge.
Apr 28 16:16:39 raspberrypi hb-service.js[7938]: [4/28/2020, 16:16:39] [HB Supervisor] Homebridge Storage Path: /var/lib/homebridge
Apr 28 16:16:39 raspberrypi hb-service.js[7938]: [4/28/2020, 16:16:39] [HB Supervisor] Homebridge Config Path: /var/lib/homebridge/config.json
Apr 28 16:16:39 raspberrypi hb-service.js[7938]: [4/28/2020, 16:16:39] [HB Supervisor] Logging to /var/lib/homebridge/homebridge.log
2
  • Please edit your question and add the output of these commands to it: systemctl cat homebridge.service and systemctl status homebridge.service.
    – Ingo
    Apr 29 '20 at 10:41
  • @Ingo Question edited with the result of the commands. Thanks for your help!
    – Nicolas
    Apr 29 '20 at 11:19
1

I cannot follow your specific issue because lack of detailed information about your programs, setup and environment. But I will discuss the additional output you have given. I may not exactly match what you need but it could give you an idea where to look to solve your problem.

In general, systemd can start any service that you can successfully run on the commandline. The first step is, to do just that and explore what environment is needed. From systemctl status homebridge.service there cannot be seen any failing of the service. The service itself is active (running) and all pre executed programs have successful finished (code=exited, status=0/SUCCESS). So executing on the commandline should also do:

rpi ~$ sudo /bin/run-parts /etc/hb-service/homebridge/prestart.d
rpi ~$ sudo /bin/bash -c 'source /etc/default/homebridge && /usr/lib/node_modules/homebridge-config-ui-x/dist/bin/hb-service.js before-start $HOMEBRIDGE_OPTS'
rpi ~$ sudo /bin/bash -c 'source /etc/default/homebridge && /usr/lib/node_modules/homebridge-config-ui-x/dist/bin/hb-service.js run $HOMEBRIDGE_OPTS'

There is an info message from run-parts:

Apr 28 16:16:39 raspberrypi run-parts[7928]: Ensuring /var/lib/homebridge is owned by root

This has nothing to do with executing as root. Just check the ownerchip with

rpi ~$ ls -l /var/lib/homebridge

and if not set to root, I would set it (chown).

It seems that hb-service.js does not respect to log to the default systemd journal and uses its own log file:

Apr 28 16:16:39 raspberrypi hb-service.js[7938]: [4/28/2020, 16:16:39] [HB Supervisor] Logging to /var/lib/homebridge/homebridge.log

You should have a look at it.

Now lets have a look at the systemd Unit:

Type=simple
User=root
Group=root

are not needed. That is the default setting.

PermissionsStartOnly=true

This is a real issue because PermissionsStartOnly is deprecated. You will not find any documentation about it on Raspbian Buster. The old documentation of PermissionsStartOnly says:

Takes a boolean argument. If true, the permission-related execution options, as configured with User= and similar options (see systemd.exec(5) for more information), are only applied to the process started with ExecStart=, and not to the various other ExecStartPre=, ExecStartPost=, ExecReload=, ExecStop=, and ExecStopPost= commands. If false, the setting is applied to all configured commands the same way. Defaults to false.

Because you don't have a restricted User= and Group= setting (other than root) for ExecStart= this option isn't needed for my understanding.

ExecStartPre=-run-parts /etc/hb-service/homebridge/prestart.d
ExecStartPre=-/usr/lib/node_modules/homebridge-config-ui-x/dist/bin/hb-service.js before-start $HOMEBRIDGE_OPTS

You should use the full path /bin/run-parts. Both options are prefixed with -. This changes errors only to warnings and the service will not fail. But I think the preconditions are needed to successful start the main program. So I would not suppress errors and omit the dash.

Restart=always
RestartSec=3

A failing service should not always Restart. It never helps to fix a buggy service.

CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_CHOWN CAP_FOWNER CAP_DAC_OVERRIDE CAP_AUDIT_WRITE CAP_SYS_ADMIN
AmbientCapabilities=CAP_NET_RAW CAP_NET_BIND_SERVICE

These are sophisticated systemd internal settings. For my experience it is better to let systemd do such things so I would not use it. If you are unsure you can look with:

rpi ~$ systemctl show homebridge.service

when the service is running and compare what options are set.

So I would start with this Unit file:

[Unit]
Description=Homebridge
Wants=network-online.target
After=syslog.target network-online.target

[Service]
WorkingDirectory=/var/lib/homebridge
EnvironmentFile=/etc/default/homebridge
ExecStartPre=/bin/run-parts /etc/hb-service/homebridge/prestart.d
ExecStartPre=/usr/lib/node_modules/homebridge-config-ui-x/dist/bin/hb-service.js before-start $HOMEBRIDGE_OPTS
ExecStart=/usr/lib/node_modules/homebridge-config-ui-x/dist/bin/hb-service.js run $HOMEBRIDGE_OPTS
# Maybe better to use default KillMode=control-group? Then comment next line.
KillMode=process

[Install]
WantedBy=multi-user.target

This should give you more information what fails and you can modify it.

2
  • Thank you so much for your time and help! Your proposed Unit file did it without any further modification. I was stuck on this problem for a long time, and your answer was very helpful! I will take the time to check if I need to do other changes thanks all this information, but so far the plugin is working well.
    – Nicolas
    Apr 29 '20 at 15:49
  • Nice :-) Haven't expected it on the first run. From the status I just see that there in the CGroup are running two programs: 7938 hb-service and 9107 homebridge. I think when stopping the service, both programs shall stop, not only the main program. So try with a deleted (commented) line KillMode=process, so KillMode=control-group will work by default. Default settings are mostly the best.
    – Ingo
    Apr 29 '20 at 16:40

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.