私のプログラムがd-busを使用してsystemdにアクセスしてサービスを開始および停止する理由は、マルチユーザー組み込みシステムで許可エラーが発生する理由です。

私のプログラムがd-busを使用してsystemdにアクセスしてサービスを開始および停止する理由は、マルチユーザー組み込みシステムで許可エラーが発生する理由です。

私が作業している組み込みシステムには、「root」や「user1」など、複数のユーザーがいます。 「user1」でログインした C++ バイナリを実行していますが、特権エラーのためサービスの起動/停止に失敗します。同じバイナリは、ルートで実行するとうまく動作します。コードは次のとおりです。

#include <iostream>
#include <systemd/sd-bus.h>


static void SDCallMethodSS(
  sd_bus* bus,
  const std::string& name,
  const std::string& method)
{
  sd_bus_error err = SD_BUS_ERROR_NULL;
  sd_bus_message* msg = nullptr;
  int r;

  r = sd_bus_call_method(bus,
      "org.freedesktop.systemd1",
      "/org/freedesktop/systemd1",
      "org.freedesktop.systemd1.Manager",
      method.c_str(),
      &err,
      &msg,
      "ss",
      name.c_str(),  "replace" );

  if (r < 0)
  {
    std::string err_str("Could not send " + method +
                        " command to systemd for service: " + name +
                        ". Error: " + err.message );

    sd_bus_error_free(&err);
    sd_bus_message_unref(msg);
    throw std::runtime_error(err_str);
  }

  char* response;
  r = sd_bus_message_read(msg, "o", &response);
  if (r < 0)
  {
          std::cerr<< "Failed to parse response message: " << strerror(-r) << std::endl;;
  }

  sd_bus_error_free(&err);
  sd_bus_message_unref(msg);
}

int main() {
  int r;
  sd_bus *bus = NULL;

  r = sd_bus_open_system(&bus);
  if (r < 0) {
          std::cerr<< "Failed to connect to system bus: " << strerror(-r) << std::endl;
    return -1;
  }

  try{
    SDCallMethodSS(bus, std::string("foo-daemon.service"), std::string("StopUnit"));
  } catch (std::exception& e) {
    std::cout << "Exception in SDCallMethodSS(): " << e.what() << std::endl;
    return -2;
  }
}

Foo-daemonは仮想プログラムです。

#include <unistd.h>

int main()
{
  while(1){
    sleep(1);
  }

}

サービスファイルは簡単です。

[Unit]
Description=Foo

[Service]
ExecStart=/usr/local/bin/foo-daemon

[Install]
WantedBy=multi-user.target

サービスファイルを/etc/systemd/system 'user1'にロードすると、出力は次のようになります。

Exception in SDCallMethodSS(): Could not send StopUnit command to systemd for service: foo-daemon.service. Error: Permission denied

「user1」の権限の問題を解決する方法

ベストアンサー1

問題はここで始まります:

r = sd_bus_open_system(&bus);

これによりシステムのバスが開きます。これにより、実行時と同じ動作が発生します。

user1@machine:~$ systemctl ...

sd-bus API を使用するか systemctl を使用するかは、systemd同じ方法で認証されます。 user1デバイスを起動/停止する権限がありません。


選択肢1: - ユーザーバス

別の方法は、次のものを使用することです。

r = sd_bus_open_user(&bus);

これはusingに似ていますsystemctl --user ...が、プロセスはusingと同じ権限を持ち、バス上でのみ実行されますuser1user1


代替案 2: polkit ルール (ユーザー権限)

システムバスでデバイスを起動/停止できるsystemdように設定する必要があります。user1これを通してpolkit

Debianベースのシステム(polkit < 106)を使用している場合は、次のファイルを作成してルールを作成します*.pkla

/etc/polkit-1/localauthority/50-local.d/service-auth.pkla
---
[Allow user1 to start/stop/restart services]
Identity=unix-user:user1
Action=org.freedesktop.systemd1.manage-units
ResultActive=yes

代替案 3: polkit ルール (サービス別権限)

Redhat / Archベースのシステム(polkit> = 106)を使用している場合は、より具体的に指定できるjavascript型構文があります。この場合、すべてのユーザーがfoo-daemon.serviceファイルを管理できるようになります*.rules

/etc/polkit-1/rules.d/foo-daemon.rules
---
polkit.addRule(function(action, subject) {
    if (action.id == "org.freedesktop.systemd1.manage-units") {
        if (action.lookup("unit") == "foo-daemon.service") {
            var verb = action.lookup("verb");
            if (verb == "start" || verb == "stop" || verb == "restart") {
                return polkit.Result.YES;
            }
        }
    }
});

代替案4:polkitルール(グループ権限)

私が使用したい解決策は、グループメンバーにユニットをスナップする権限を付与することです。これにより、ユーザーがそのグループのメンバーである限り、次のことができますsystemctl {start,stop,restart} ...sd_bus_open_system(...)

これを行う方法の答えは次のとおりです。

systemdは、グループ内の権限のないユーザーで始まります。

おすすめ記事