diff --git a/lib/freedive/api/service/cli.ex b/lib/freedive/api/service/cli.ex index 804ab14..f75c16e 100644 --- a/lib/freedive/api/service/cli.ex +++ b/lib/freedive/api/service/cli.ex @@ -4,70 +4,72 @@ defmodule Freedive.Api.Service.Cli do """ require Logger import Freedive.Api.Command + import Freedive.Api.Service.Icons @service_bin "/usr/sbin/service" + @skip_service_names ["DAEMON", "FILESYSTEMS", "LOGIN", "NETWORKING", "SERVERS"] - def list_services() do + def list_services!() do + all_service_names = all_service_names() + enabled_service_names = enabled_service_names() + + all_service_names + |> Enum.map(fn name -> service_details(name, Enum.member?(enabled_service_names, name)) end) + |> Enum.into(%{}, &{&1[:name], &1}) + end + + def all_service_names() do case execute(@service_bin, ["-l"]) do {:ok, stdout} -> - service_names = - stdout - |> String.split("\n") - |> Enum.map(&String.trim/1) - |> Enum.reject( - &Enum.member?(["DAEMON", "FILESYSTEMS", "LOGIN", "NETWORKING", "SERVERS"], &1) - ) + stdout + |> String.split("\n") + |> Enum.map(&String.trim/1) + |> Enum.reject(&Enum.member?(@skip_service_names, &1)) - case execute(@service_bin, ["-e"]) do - {:ok, stdout} -> - enabled_service_names = - stdout - |> String.split("\n") - |> Enum.map(&String.trim/1) - |> Enum.map(&Path.basename/1) - |> Enum.into(%{}, &{&1, true}) - - services = - service_names - |> Enum.map(fn name -> - %{ - name: name, - icon: "puzzle", - enabled: Map.has_key?(enabled_service_names, name), - running: - if Map.has_key?(enabled_service_names, name) do - service_is_running?(name) - else - nil - end, - description: - if Map.has_key?(enabled_service_names, name) do - case service_description(name) do - {:ok, desc} -> desc - _ -> nil - end - else - nil - end, - commands: nil, - rcvars: nil - } - end) - |> Enum.into(%{}, &{&1[:name], &1}) - - {:ok, services} - - {:error, {stderr, _code}} -> - Logger.error("list_services enabled, log: #{inspect(stderr)}") - {:error, stderr} - end - - {:error, {stderr, _code}} -> - Logger.error("list_services, log: #{inspect(stderr)}") - {:error, stderr} + {:error, reason} -> + Logger.error("List services: #{reason}") + raise "Failed to list services." end end + def enabled_service_names() do + case execute(@service_bin, ["-e"]) do + {:ok, stdout} -> + stdout + |> String.split("\n") + |> Enum.map(&String.trim/1) + |> Enum.map(&Path.basename/1) + + {:error, reason} -> + Logger.error("List enabled services: #{reason}") + raise "Failed to list enabled services." + end + end + + def service_details(name, enabled \\ nil) do + enabled = if(enabled != nil, do: enabled, else: service_is_enabled?(name)) + + %{ + name: name, + icon: icon_for_service(name), + enabled: enabled, + running: if(enabled, do: service_is_running?(name), else: nil), + description: if(enabled, do: service_description!(name), else: nil), + commands: if(enabled, do: service_commands!(name), else: nil), + log: [] + } + end + + def refresh(service) do + name = service.name + + %{ + service + | running: service_is_running?(name), + enabled: service_is_enabled?(name) + } + end + def service_is_running?(name, args \\ []) do case service(name, "onestatus", args) do {:ok, _stdout} -> @@ -88,14 +90,33 @@ defmodule Freedive.Api.Service.Cli do end end - def service_description(name, args \\ []) do + def service_status!(name, args \\ []) do + case service(name, "onestatus", args) do + {:ok, stdout} -> + stdout |> String.trim() + + error -> + error + end + end + + def service_description!(name, args \\ []) do case service(name, "onedescribe", args) do {:ok, stdout} -> - stdout = String.trim(stdout) - {:ok, stdout} + stdout |> String.trim() - {:error, {stderr, _code}} -> - {:error, stderr} + error -> + error + end + end + + def service_commands!(name, args \\ []) do + case service(name, "oneextracommands", args) do + {:ok, stdout} -> + stdout |> String.split(" ") |> Enum.map(&String.trim/1) |> Enum.reject(&(&1 == "")) + + error -> + error end end @@ -106,6 +127,7 @@ defmodule Freedive.Api.Service.Cli do {:ok, stdout} {:error, {stderr, code}} -> + # Logger.warning("service #{name} #{action}: #{String.trim(stderr)}") {:error, {stderr, code}} end end diff --git a/lib/freedive/api/service/server.ex b/lib/freedive/api/service/server.ex index de60995..1988ee8 100644 --- a/lib/freedive/api/service/server.ex +++ b/lib/freedive/api/service/server.ex @@ -5,16 +5,42 @@ defmodule Freedive.Api.Service do use GenServer require Logger import Freedive.Api.Service.Cli - import Freedive.Api.Service.Icons + + @topic "system:service" + @events [:started, :stopped, :restarted] def start_link(opts) do GenServer.start_link(__MODULE__, opts, name: __MODULE__) end + def topic do + @topic + end + + def events do + @events + end + + def subscribe do + Phoenix.PubSub.subscribe(Freedive.PubSub, @topic) + end + def list() do GenServer.call(__MODULE__, {:list}) end + def start(name) do + GenServer.cast(__MODULE__, {:start, name}) + end + + def stop(name) do + GenServer.cast(__MODULE__, {:stop, name}) + end + + def restart(name) do + GenServer.cast(__MODULE__, {:restart, name}) + end + @impl true def init(opts) do state = %{opts: opts, services: []} @@ -24,13 +50,7 @@ defmodule Freedive.Api.Service do @impl true def handle_continue(_opts, state) do - {:ok, services} = list_services() - services = services - |> Enum.map(fn {name, service} -> - {name, Map.put(service, :icon, icon_for_service(name))} - end) - |> Enum.into(%{}) - state = %{state | services: services} + state = %{state | services: list_services!()} {:noreply, state} end @@ -39,4 +59,13 @@ defmodule Freedive.Api.Service do services = state[:services] {:reply, services, state} end + + def broadcast(event, service) do + if event in @events do + service = refresh(service) + Phoenix.PubSub.broadcast(Freedive.PubSub, @topic, {event, service}) + else + Logger.error("Service.Server broadcast: unknown event: #{event}") + end + end end