Skip to content

Instantly share code, notes, and snippets.

@maxlapshin
Last active December 24, 2022 17:28
Show Gist options
  • Star 31 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save maxlapshin/01773f0fca706acdcb4acb77d91d78bb to your computer and use it in GitHub Desktop.
Save maxlapshin/01773f0fca706acdcb4acb77d91d78bb to your computer and use it in GitHub Desktop.
Systemd support
-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.
@joaohf
Copy link

joaohf commented Jul 27, 2018

+1

@dinama
Copy link

dinama commented Oct 3, 2019

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.

@weiss
Copy link

weiss commented Jul 9, 2020

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 …

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment