使用 dbus 关闭 docker 容器和 python 中的树莓派

Posted

技术标签:

【中文标题】使用 dbus 关闭 docker 容器和 python 中的树莓派【英文标题】:Using dbus to poweroff Raspberry Pi inside docker container and python 【发布时间】:2020-04-13 11:49:28 【问题描述】:

我正在使用 BalenaOS 并使用一些 python 构建一个 docker 容器,该容器可以感知何时按下按钮,然后应该发送此命令:

dbus-send --system \
    --print-reply=literal \
    --dest=org.freedesktop.login1 \
    /org/freedesktop/login1 \
   "org.freedesktop.login1.Manager.PowerOff" boolean:true

我可以在这个容器内打开一个终端,运行这个命令,它确实按预期工作。但是,按下按钮只会导致出现错误消息(显示在帖子底部)

这是我的设置:

码头文件

FROM balenalib/%%BALENA_MACHINE_NAME%%-debian-python:3.7.4

# Enable systemd init system
ENV INITSYSTEM off

# Set the working directory
WORKDIR /usr/src/app

RUN install_packages git dbus gnome-common

RUN apt-get update
RUN apt install python3-gi python3-gi-cairo gir1.2-gtk-3.0

# Upgrade pip
RUN pip install --upgrade pip
COPY requirements.txt .
RUN pip install --user -r requirements.txt --no-cache-dir --disable-pip-version-check \
                --index-url https://www.piwheels.org/simple

# Copy everything into the container
COPY . ./
#Make sure scripts in .local are usable:
ENV PATH=/root/.local/bin:$PATH
ENV DBUS_SYSTEM_BUS_ADDRESS=unix:path=/host/run/dbus/system_bus_socket
# Start application
CMD ["bash", "start.sh"]

requirements.txt

RPi.Gpio
dbus-python

start.sh

#!/usr/bin/env bash

## connect to the host's system bus from the application container
export DBUS_SYSTEM_BUS_ADDRESS=unix:path=/host/run/dbus/system_bus_socket

编辑

为了pydbus,我放弃了dbus-python 它似乎产生了更好的结果,并且似乎简化了问题,但仍然出现错误。更多关于两者区别的信息可以在这里找到https://wiki.python.org/moin/DbusExamples

这是编辑后的python

button.py

​​>
import RPi.GPIO as GPIO
import time
import pydbus
import gi

# Set GPIO mode: GPIO.BCM or GPIO.BOARD
GPIO.setmode(GPIO.BOARD)

# Set pin 5 an an input, and enable the internal pull-up resistor
GPIO.setup(5, GPIO.IN, pull_up_down=GPIO.PUD_UP)

oldButtonState1 = True

while True:
    buttonState1 = GPIO.input(5)

    if buttonState1 != oldButtonState1 and buttonState1 == False :
        bus = pydbus.SystemBus()
        logind = bus.get('.login1')['.Manager']
        logind.PowerOff()

    oldButtonState1 = buttonState1

time.sleep(1)

现在的输出是:

21.12.19 11:33:53 (-0800)  button      logind.PowerOff()
21.12.19 11:33:53 (-0800)  button    File "/root/.local/lib/python3.7/site-packages/pydbus/proxy_method.py", line 62, in __call__
21.12.19 11:33:53 (-0800)  button      raise TypeError(self.__qualname__ + " missing  required positional argument(s)".format(-argdiff))
21.12.19 11:33:53 (-0800)  button  TypeError: org.freedesktop.login1.Manager.PowerOff missing 1 required positional argument(s)
21.12.19 11:33:59 (-0800)  button  button.py:12: RuntimeWarning: A physical pull up resistor is fitted on this channel!
21.12.19 11:33:59 (-0800)  button    GPIO.setup(5, GPIO.IN, pull_up_down=GPIO.PUD_UP)

如果我不得不猜测我会说我在 python 脚本中做错了什么。没有正确使用 dbus。

【问题讨论】:

“未正确利用 dbus”:我想知道 .Interface(..., 'org.freedesktop.login1.Manager.PowerOff) 不会失败。预计为.Interface(..., 'org.freedesktop.login1.Manager')。您必须使用interface 句柄来调用方法PowerOffinterface.PowerOff(True) 【参考方案1】:

感谢stovfl

问题是:

    使用 dbus-python,但应该使用 pydbus 库。 dbus-python 已折旧。 我的interface.PowerOff() 丢失了 True。应该是interface.PowerOff(True)

正确的dockerfile是:

FROM balenalib/%%BALENA_MACHINE_NAME%%-debian-python:3.7.4

# Enable systemd init system
ENV INITSYSTEM off

# Set the working directory
WORKDIR /usr/src/app

RUN install_packages git dbus gnome-common

RUN apt-get update
RUN apt install python3-gi python3-gi-cairo gir1.2-gtk-3.0

# Upgrade pip
RUN pip install --upgrade pip
COPY requirements.txt .
RUN pip install --user -r requirements.txt --no-cache-dir --disable-pip-version-check \
                --index-url https://www.piwheels.org/simple

# Copy everything into the container
COPY . ./
#Make sure scripts in .local are usable:
ENV PATH=/root/.local/bin:$PATH
ENV DBUS_SYSTEM_BUS_ADDRESS=unix:path=/host/run/dbus/system_bus_socket
# Start application
CMD ["python", "button.py"]

我的要求.txt

RPi.Gpio
pydbus
Pycairo
PyGObject

正确的python脚本是

#!/usr/bin/env python3
#!
import RPi.GPIO as GPIO
import time
import pydbus
import gi

# Set GPIO mode: GPIO.BCM or GPIO.BOARD
GPIO.setmode(GPIO.BOARD)

# Set pin 5 an an input, and enable the internal pull-up resistor
GPIO.setup(5, GPIO.IN, pull_up_down=GPIO.PUD_UP)

oldButtonState1 = True

while True:
    buttonState1 = GPIO.input(5)

    if buttonState1 != oldButtonState1 and buttonState1 == False :
        bus = pydbus.SystemBus()
        logind = bus.get('.login1')['.Manager']
        logind.PowerOff(True)

    oldButtonState1 = buttonState1

time.sleep(1)

帮助我找到答案的链接:

https://wiki.python.org/moin/DbusExamples

https://fhackts.wordpress.com/2019/08/08/shutting-down-or-rebooting-over-dbus-programmatically-from-a-non-root-user/

https://pygobject.readthedocs.io/en/latest/getting_started.html

【讨论】:

【参考方案2】:

Raspbian 靶心解决方案:

Sudo 编辑/usr/share/polkit-1/actions/org.freedesktop.login1.policy 以更改https://www.freedesktop.org/software/systemd/man/org.freedesktop.login1.html#Security 中所需的安全设置,例如允许“关机”

...
<action id="org.freedesktop.login1.power-off">
  <description gettext-domain="systemd">Power off the system</description>
  <message gettext-domain="systemd">Authentication is required to power off the system.</message>
  <defaults>
    <allow_any>yes</allow_any>
    <allow_inactive>yes</allow_inactive>
    <allow_active>yes</allow_active>
  </defaults>
  <annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.set-wall-message</annotate>
</action>
    
<action id="org.freedesktop.login1.power-off-multiple-sessions">
  <description gettext-domain="systemd">Power off the system while other users are logged in</description>
  <message gettext-domain="systemd">Authentication is required to power off the system while other users are logged in.</message>
  <defaults>
    <allow_any>yes</allow_any>
    <allow_inactive>yes</allow_inactive>
    <allow_active>yes</allow_active>
  </defaults>
  <annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.power-off</annotate>
</action>
...

Python:

import dbus
bus = dbus.SystemBus()
obj = bus.get_object('org.freedesktop.login1', '/org/freedesktop/login1')
iface = dbus.Interface(obj, 'org.freedesktop.login1.Manager')
iface.PowerOff(True)

对于多用户系统来说,允许不受挑战地访问关机并不理想,但对于我用遥控钥匙关闭机器人的目的来说效果很好

【讨论】:

以上是关于使用 dbus 关闭 docker 容器和 python 中的树莓派的主要内容,如果未能解决你的问题,请参考以下文章

无法在 Docker 容器中为 X11 自动启动没有 $DISPLAY 的 dbus-daemon

无法使用 SSH 连接到 docker 容器 [关闭]

无法在 ubuntu docker 容器上使用 systemd [关闭]

docker容器中的前端和后端[关闭]

需要使用远程 API 运行同一镜像的多个容器,同一 docker 服务器中的相同配置

我可以在 docker 容器中使用降级的 cuda 版本吗? [关闭]