1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
defmodule IRC.Plugin do
require Logger
defmodule Supervisor do
use DynamicSupervisor
require Logger
def start_link() do
DynamicSupervisor.start_link(__MODULE__, [], name: __MODULE__)
end
def start_child(module, opts \\ []) do
Logger.info("Starting #{module}")
spec = %{id: {IRC.Plugin,module}, start: {IRC.Plugin, :start_link, [module, opts]}, name: module, restart: :transient}
case DynamicSupervisor.start_child(__MODULE__, spec) do
{:ok, _} = res -> res
:ignore ->
Logger.info("Ignored #{module}")
:ignore
{:error,_} = res ->
Logger.error("Could not start #{module}: #{inspect(res, pretty: true)}")
res
end
end
@impl true
def init(_init_arg) do
DynamicSupervisor.init(
strategy: :one_for_one,
max_restarts: 10,
max_seconds: 1
)
end
end
def dets(), do: to_charlist(LSG.data_path("/plugins.dets"))
def setup() do
:dets.open_file(dets(), [])
end
def enabled() do
:dets.foldl(fn
{name, true, _}, acc -> [name | acc]
_, acc -> acc
end, [], dets())
end
def start_all() do
for mod <- enabled(), do: {mod, IRC.Plugin.Supervisor.start_child(mod)}
end
def declare(module) do
case get(module) do
:disabled -> :dets.insert(dets(), {module, true, nil})
_ -> nil
end
end
def start(module, opts \\ []) do
IRC.Plugin.Supervisor.start_child(module)
end
@doc "Enables a plugin"
def enable(name), do: switch(name, true)
@doc "Disables a plugin"
def disable(name), do: switch(name, false)
@doc "Enables or disables a plugin"
def switch(name, value) when is_boolean(value) do
last = case get(name) do
{:ok, last} -> last
_ -> nil
end
:dets.insert(dets(), {name, value, last})
end
@spec get(module()) :: {:ok, last_start :: nil | non_neg_integer()} | :disabled
def get(name) do
case :dets.lookup(dets(), name) do
[{name, enabled, last_start}] -> {:ok, enabled, last_start}
_ -> :disabled
end
end
def start_link(module, options \\ []) do
with {:disabled, {_, true, last}} <- {:disabled, get(module)},
{:throttled, false} <- {:throttled, false}
do
module.start_link()
else
{error, _} ->
Logger.info("Plugin: #{to_string(module)} ignored start: #{to_string(error)}")
:ignore
end
end
end
|