defmodule Systemd.Install do @moduledoc """ Helpers for installing generated unit files. File writes are intentionally local filesystem operations. Reloading systemd is still performed through the D-Bus manager API, not through `systemctl`. """ alias Systemd.{Error, Manager, UnitFile} @system_unit_dir "/etc/systemd/system" @user_unit_dir Path.join(System.user_home!(), ".config/systemd/user") @type target :: :system | :user | {:directory, Path.t()} @doc """ Returns the target path for a unit name. """ @spec unit_path(String.t(), target()) :: Path.t() def unit_path(name, target \\ :system) def unit_path(name, :system), do: Path.join(@system_unit_dir, name) def unit_path(name, :user), do: Path.join(@user_unit_dir, name) def unit_path(name, {:directory, directory}), do: Path.join(directory, name) @doc """ Writes a unit file to the selected target directory. """ @spec write_unit(String.t(), UnitFile.t() | String.t(), keyword()) :: {:ok, Path.t()} | {:error, Error.t()} def write_unit(name, unit_file_or_text, opts \\ []) do target = Keyword.get(opts, :target, :system) path = unit_path(name, target) text = render(unit_file_or_text) with :ok <- File.mkdir_p(Path.dirname(path)), :ok <- File.write(path, text) do {:ok, path} else {:error, reason} -> {:error, Error.connection_error(reason)} end end @doc """ Writes a unit file and reloads systemd manager configuration over D-Bus. """ @spec install_unit(String.t(), UnitFile.t() | String.t(), keyword()) :: {:ok, Path.t()} | {:error, Error.t()} def install_unit(name, unit_file_or_text, opts \\ []) do with {:ok, path} <- write_unit(name, unit_file_or_text, opts), {:ok, conn} <- Manager.connect(bus: Keyword.get(opts, :bus, :system)), :ok <- Manager.reload(conn) do {:ok, path} end end defp render(%UnitFile{} = unit_file), do: UnitFile.to_string(unit_file) defp render(text) when is_binary(text), do: text end