diff --git a/src/systemd_socket.erl b/src/systemd_socket.erl index 5357b74..0e31089 100644 --- a/src/systemd_socket.erl +++ b/src/systemd_socket.erl @@ -5,14 +5,12 @@ -behaviour(gen_server). -define(NAME, ?MODULE). --define(NOTIFY_SOCKET, "NOTIFY_SOCKET"). -include_lib("kernel/include/logger.hrl"). --include_lib("kernel/include/file.hrl"). -export([send/1]). --export([start_link/0, +-export([start_link/1, init/1, handle_call/3, handle_cast/2, @@ -25,30 +23,13 @@ send(Message) -> % # Behaviour implementation -start_link() -> - gen_server:start_link({local, ?NAME}, ?MODULE, [], []). +start_link(Address) -> + gen_server:start_link({local, ?NAME}, ?MODULE, Address, []). -init(_Arg) -> - State = case has_env(?NOTIFY_SOCKET) of - false -> - []; - [$@ | AbstractPath] -> - Address = {local, [0 | AbstractPath]}, - {ok, Socket} = gen_udp:open(0, [local]), - {Socket, Address}; - Path -> - case file:read_file_info(Path) of - {error, _Error} -> - []; - {ok, #file_info{access=Access}} - when Access =:= write; Access =:= read_write -> - Address = {local, Path}, - {ok, Socket} = gen_udp:open(0, [local]), - {Socket, Address} - end - end, - os:unsetenv(?NOTIFY_SOCKET), - {ok, State}. +init([]) -> {ok, []}; +init(Address) -> + {ok, Socket} = gen_udp:open(0, [local]), + {ok, {Socket, Address}}. handle_call({send, Message}, _Ref, {Socket, Address}=State) -> ok = gen_udp:send(Socket, Address, 0, [Message, $\n]), @@ -61,10 +42,3 @@ handle_cast(_Msg, State) -> handle_info(_Msg, State) -> {noreply, State}. - -has_env(Name) -> - case os:getenv(Name) of - false -> false; - "" -> false; - Value -> Value - end. diff --git a/src/systemd_sup.erl b/src/systemd_sup.erl index 9c014a8..a4aae32 100644 --- a/src/systemd_sup.erl +++ b/src/systemd_sup.erl @@ -6,6 +6,12 @@ -export([start_link/1, init/1]). +-define(PID, "WATCHDOG_PID"). +-define(TIMEOUT, "WATCHDOG_USEC"). +-define(NOTIFY_SOCKET, "NOTIFY_SOCKET"). + +-include_lib("kernel/include/file.hrl"). + start_link(Opts) -> supervisor:start_link(?MODULE, Opts). @@ -13,11 +19,61 @@ init(_Opts) -> SupFlags = #{ strategy => one_for_one }, + Pid = os:getpid(), + SocketServer = #{id => socket, - start => {systemd_socket, start_link, []} + start => {systemd_socket, start_link, [notify_socket()]} }, - Watchdog = #{id => watchdog, - start => {systemd_watchdog, start_link, []} - }, + Watchdog = case {watchdog_pid(), watchdog_timeout()} of + {Pid, TimeoutUS} when TimeoutUS > 0 -> + Timeout = erlang:convert_time_unit(TimeoutUS, + microsecond, + millisecond), + #{id => watchdog, + start => {systemd_watchdog, start_link, [Timeout]} + }; + _ -> + #{id => watchdog, + start => {systemd_watchdog, start_link, [infinity]} + } + end, {ok, {SupFlags, [SocketServer, Watchdog]}}. + +watchdog_pid() -> + Return = case os:getenv(?PID) of + false -> os:getpid(); + Env -> Env + end, + os:unsetenv(?PID), + Return. + +watchdog_timeout() -> + Return = case os:getenv(?TIMEOUT) of + false -> -1; + Env -> + case string:to_integer(Env) of + {Timeout, ""} -> Timeout; + _ -> -1 + end + end, + os:unsetenv(?TIMEOUT), + Return. + +notify_socket() -> + State = case os:getenv(?NOTIFY_SOCKET) of + false -> + []; + [$@ | AbstractPath] -> + {local, [0 | AbstractPath]}; + Path -> + case file:read_file_info(Path) of + {error, _Error} -> + []; + {ok, #file_info{access=Access}} + when Access =:= write; Access =:= read_write -> + {local, Path} + end + end, + os:unsetenv(?NOTIFY_SOCKET), + State. diff --git a/src/systemd_watchdog.erl b/src/systemd_watchdog.erl index 2be4108..20f2954 100644 --- a/src/systemd_watchdog.erl +++ b/src/systemd_watchdog.erl @@ -7,10 +7,7 @@ -include_lib("kernel/include/logger.hrl"). -include("systemd.hrl"). --define(PID, "WATCHDOG_PID"). --define(TIMEOUT, "WATCHDOG_USEC"). - --export([start_link/0, +-export([start_link/1, init/1, handle_call/3, handle_cast/2, @@ -18,17 +15,8 @@ -record(state, {timeout, enabled=true}). -start_link() -> - Pid = os:getpid(), - case {watchdog_pid(), watchdog_timeout()} of - {Pid, TimeoutUS} when TimeoutUS > 0 -> - Timeout = erlang:convert_time_unit(TimeoutUS, - microsecond, - millisecond), - gen_server:start_link({local, ?WATCHDOG}, ?MODULE, Timeout, []); - _ -> - gen_server:start_link({local, ?WATCHDOG}, ?MODULE, infinity, []) - end. +start_link(Timeout) -> + gen_server:start_link({local, ?WATCHDOG}, ?MODULE, Timeout, []). init(Timeout) -> State = #state{timeout=Timeout}, @@ -70,23 +58,3 @@ notify(#state{enabled=true, timeout=Timeout}) when is_integer(Timeout) andalso T erlang:send_after(Timeout div Scale, self(), keepalive); notify(_State) -> ok. - -watchdog_pid() -> - Return = case os:getenv(?PID) of - false -> os:getpid(); - Env -> Env - end, - os:unsetenv(?PID), - Return. - -watchdog_timeout() -> - Return = case os:getenv(?TIMEOUT) of - false -> -1; - Env -> - case string:to_integer(Env) of - {Timeout, ""} -> Timeout; - _ -> -1 - end - end, - os:unsetenv(?TIMEOUT), - Return. diff --git a/test/systemd_SUITE.erl b/test/systemd_SUITE.erl index 30666c3..9b8fd75 100644 --- a/test/systemd_SUITE.erl +++ b/test/systemd_SUITE.erl @@ -35,6 +35,8 @@ notify(init, Config) -> notify(_, Config) -> Config. notify(Config) -> + Pid = ?config(mock_pid, Config), + systemd:notify(ready), systemd:notify(stopping), systemd:notify(reloading), @@ -46,8 +48,6 @@ notify(Config) -> ct:sleep(10), - Messages = mock_systemd:messages(?config(mock_pid, Config)), - ?assertEqual(["READY=1\n", "STOPPING=1\n", "RELOADING=1\n", @@ -55,7 +55,14 @@ notify(Config) -> "ERRNO=10\n", "BUSERROR=test.example.bus.service.Error\n", "CUSTOM=message\n", - "FORMATTED=deadbeef\n"], Messages), + "FORMATTED=deadbeef\n"], mock_systemd:messages(Pid)), + + ct:log("Connection address persists between process restarts"), + gen_server:stop(systemd_socket, error, 100), + ct:sleep(10), + systemd:notify(ready), + ct:sleep(10), + ?assertEqual(["READY=1\n"], mock_systemd:messages(Pid)), ok. watchdog(init, Config) -> Config; @@ -179,6 +186,20 @@ watchdog(Config) -> ok = stop(Config), + % ------------------------------------------------------------------------- + ct:log("Watchdog process send messages after restart"), + os:putenv("WATCHDOG_PID", os:getpid()), + os:putenv("WATCHDOG_USEC", TimeoutList), + ok = start_with_socket(Socket), + ct:sleep(10), + + ?assertEqual(["WATCHDOG=1\n"], mock_systemd:messages(Pid)), + gen_server:stop(systemd_watchdog, error, 100), + ct:sleep(10), + ?assertEqual(["WATCHDOG=1\n"], mock_systemd:messages(Pid)), + + ok = stop(Config), + ok. listen_fds(init, Config) -> Config;