-
-
Save maxlapshin/01773f0fca706acdcb4acb77d91d78bb to your computer and use it in GitHub Desktop.
-module(systemd). | |
% This is what you need to adopt systemd in erlang | |
% | |
% Do whatever you want license. If you want, you can take this code under terms of MIT license. | |
-export([ready/0, reloading/0, stopping/0, watchdog/0]). | |
-export([start_link/0]). | |
-export([init/1, handle_info/2, terminate/2]). | |
ready() -> call(<<"READY=1">>). | |
reloading() ->call(<<"RELOADING=1">>). | |
stopping() -> call(<<"STOPPING=1">>). | |
watchdog() -> call(<<"WATCHDOG=1">>). | |
call(Call) -> | |
case os:getenv("NOTIFY_SOCKET") of | |
false -> | |
{error, not_configured}; | |
Path -> | |
case gen_udp:open(0, [local]) of | |
{error, SocketError} -> | |
{error, SocketError}; | |
{ok, Socket} -> | |
Result = gen_udp:send(Socket, {local,Path}, 0, Call), | |
gen_udp:close(Socket), | |
Result | |
end | |
end. | |
start_link() -> | |
gen_server:start_link({local,?MODULE}, ?MODULE, [], []). | |
init([]) -> | |
erlang:send_after(60000, self(), watchdog), | |
{ok, state}. | |
handle_info(watchdog, State) -> | |
watchdog(), | |
erlang:send_after(60000, self(), watchdog), | |
{noreply, State}. | |
terminate(_,_) -> ok. |
Just putting all the useful notes from Max's email in here so its all in 1 place:
While migrating to systemd from old good SysV init, I've found that it is a very good idea to speak to this daemon and tell him that my software is alive and ready. systemd authors offer library for this (and I suppose that they want to see these calls in POSIX and all other OS). Petr Lemenkov together with other commiters have written wrapper around this C library: https://github.com/systemd/erlang-sd_notify
I was completely against this idea, because I do not want to take nif around library that makes a very simple call, so I've decided to write a code that relies on protocol in this systemd library:
https://gist.github.com/maxlapshin/01773f0fca706acdcb4acb77d91d78bb
Just drop this file to your source, add systemd as a permanent worker for keepalive to work and don't forget to call systemd:ready to commit start.
Service file may look like this:
# systemd unit
[Unit]
Description=Retroview
After=network-online.target
Wants=network-online.target
[Service]
Environment=HOME=/var/lib/retroview
Environment=PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin
Environment=LANG=C
Environment=PROCNAME=retroview
Type=notify
User=root
Group=root
LimitNOFILE=102400
ExecStartPre=/bin/mkdir -p /var/lib/retroview
ExecStartPre=/bin/mkdir -p /var/log/retroview
ExecStart=/usr/bin/erl -p /opt/retroview/ebin -noinput -name retroview@127.0.0.1 -s retroview
Restart=on-failure
TimeoutStartSec=300s
WatchdogSec=120
WorkingDirectory=/opt/retroview
NotifyAccess=main
[Install]
WantedBy=multi-user.target
+1
small change for WATCHDOG_USEC
support
-module(systemd).
-export([ready/0, reloading/0, stopping/0, watchdog/0]).
-export([start_link/0]).
-export([init/1, handle_info/2, terminate/2]).
-include_lib("kernel/include/logger.hrl").
ready() -> call(<<"READY=1">>).
reloading() ->call(<<"RELOADING=1">>).
stopping() -> call(<<"STOPPING=1">>).
watchdog() -> call(<<"WATCHDOG=1">>).
call(Call) ->
?LOG_INFO("systemd ~p", [Call]),
case os:getenv("NOTIFY_SOCKET") of
false -> {error, not_configured};
Path ->
case gen_udp:open(0, [local]) of
{error, SocketError} ->
{error, SocketError};
{ok, Socket} ->
Result = gen_udp:send(Socket, {local,Path}, 0, Call),
gen_udp:close(Socket),
Result
end
end.
start_link() ->
gen_server:start_link({local,?MODULE}, ?MODULE, [], []).
init([]) ->
WatchdogMs = case os:getenv( "WATCHDOG_USEC" ) of
false -> none;
Value ->
Part = erlang:round(0.8 * erlang:list_to_integer(Value)),
erlang:convert_time_unit(Part, microsecond, millisecond)
end,
erlang:send_after(WatchdogMs, self(), watchdog),
?LOG_INFO("watchdog ~p", [WatchdogMs]),
{ok, WatchdogMs}.
handle_info(watchdog, none) ->
{noreply, none};
handle_info(watchdog, WatchdogMs) ->
watchdog(),
erlang:send_after(WatchdogMs, self(), watchdog),
{noreply, WatchdogMs}.
terminate(_,_) -> ok.
FWIW, I created something similar, except my version keeps the socket in the gen_server
state rather than reopening it on each notification (and uses the normal gen_server
timeout for triggering watchdog notifications).
Maybe we should publish this as a proper library application …
Excellent !