지피지기 백전백퇴

systemd service를 API로 접근하는 방법 - DBus


문제

서버에 이것저것 로컬 앱을 깔아서 쓰는데, 그중엔 상시 돌아가는 서비스가 아니라 어쩌다 한번 실행하면 되는 녀석도 있다.

처음에는 대충 하루에 한번쯤 돌아가면 되는 녀석이라 systemd 서비스로 만들고 1일 trigger를 걸었다가, 나중에 임의로 manual 실행이 조금 필요해진 상황이다.

무식한 접근법

그때그때 systemctl restart --user ... 하면 되는것 아니겠냐, 하고 해봤는데, systemctl의 경우 commandline으로 실행할 것을 가정하기 때문에 근본없는 경로로 실행하면 tty가 없잖아! 하고 실패하는 문제가 있었다.

무식하면 용감하다

그럼 그냥 tty를 만들어주면 되는 것 아니겠냐…는 생각에 이렇게 했다.

ssh localhost "sh -c 'systemctl restart --user ...'"

ssh 의존은 좀 심한 것 아닙니까

그래서 systemctl을 쓰지 않고 API로 접근할 방법을 알아봤는데, 알고보니 systemd는 dbus interface를 제공하고 있고,

여기 해당 interface가 정리되어 있다.

https://www.freedesktop.org/software/systemd/man/latest/org.freedesktop.systemd1.html

예제 코드는 다음과 같다:

use dbus::blocking::{Connection, Proxy};
use std::time::Duration;

fn main() {
    // Connect to the session bus
    let conn = Connection::new_session().unwrap();

    // Create a proxy to the systemd manager
    let proxy = Proxy::new("org.freedesktop.systemd1", "/org/freedesktop/systemd1", Duration::from_millis(5000), &conn);

    // Restart the service
    let service_name = "my-precious.service";
    let mode = "fail";
    let job: (dbus::Path<'static>,) = proxy.method_call("org.freedesktop.systemd1.Manager", "StartUnit", (service_name, mode,)).unwrap();

    println!("Restart job started for '{}': {}", service_name, job.0);
}

사실 DBus가 어떻게 생긴 녀석인지, 어떻게 쓰는건지 전혀 몰라서 실제 구현은 LLM에게 모두 떠맡겼다. 고마워요 code-supernova!