• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

processone / ejabberd / 1258

12 Dec 2025 03:57PM UTC coverage: 33.638% (-0.006%) from 33.644%
1258

push

github

badlop
Container: Apply commit a22c88a

ejabberdctl.template: Show meaningful error when ERL_DIST_PORT is in use

15554 of 46240 relevant lines covered (33.64%)

1078.28 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

32.73
/src/ejabberd_systemd.erl
1
%%%----------------------------------------------------------------------
2
%%% File    : ejabberd_systemd.erl
3
%%% Author  : Holger Weiss <holger@zedat.fu-berlin.de>
4
%%% Purpose : Integrate with systemd
5
%%% Created :  5 Jan 2021 by Holger Weiss <holger@zedat.fu-berlin.de>
6
%%%
7
%%%
8
%%% ejabberd, Copyright (C) 2021   ProcessOne
9
%%%
10
%%% This program is free software; you can redistribute it and/or
11
%%% modify it under the terms of the GNU General Public License as
12
%%% published by the Free Software Foundation; either version 2 of the
13
%%% License, or (at your option) any later version.
14
%%%
15
%%% This program is distributed in the hope that it will be useful,
16
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
17
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18
%%% General Public License for more details.
19
%%%
20
%%% You should have received a copy of the GNU General Public License along
21
%%% with this program; if not, write to the Free Software Foundation, Inc.,
22
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23
%%%
24
%%%----------------------------------------------------------------------
25

26
-module(ejabberd_systemd).
27
-author('holger@zedat.fu-berlin.de').
28
-behaviour(gen_server).
29

30
-export([start_link/0,
31
         ready/0,
32
         reloading/0,
33
         stopping/0]).
34
-export([init/1,
35
         handle_call/3,
36
         handle_cast/2,
37
         handle_info/2,
38
         terminate/2,
39
         code_change/3]).
40

41
-include("logger.hrl").
42

43
-record(state,
44
        {socket :: gen_udp:socket() | undefined,
45
         destination :: inet:local_address() | undefined,
46
         interval :: pos_integer() | undefined,
47
         timer :: reference() | undefined}).
48

49
-type state() :: #state{}.
50

51
%%--------------------------------------------------------------------
52
%% API.
53
%%--------------------------------------------------------------------
54
-spec start_link() -> {ok, pid()} | ignore | {error, term()}.
55
start_link() ->
56
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
11✔
57

58
-spec ready() -> ok.
59
ready() ->
60
    cast_notification(<<"READY=1">>).
13✔
61

62
-spec reloading() -> ok.
63
reloading() ->
64
    cast_notification(<<"RELOADING=1">>).
2✔
65

66
-spec stopping() -> ok.
67
stopping() ->
68
    cast_notification(<<"STOPPING=1">>).
11✔
69

70
%%--------------------------------------------------------------------
71
%% gen_server callbacks.
72
%%--------------------------------------------------------------------
73
-spec init(any()) -> {ok, state()} | {stop, term()}.
74
init(_Opts) ->
75
    process_flag(trap_exit, true),
11✔
76
    case os:getenv("NOTIFY_SOCKET") of
11✔
77
        [$@ | _Abstract] ->
78
            ?CRITICAL_MSG("Abstract NOTIFY_SOCKET not supported", []),
×
79
            {stop, esocktnosupport};
×
80
        Path when is_list(Path), length(Path) > 0 ->
81
            ?DEBUG("Got NOTIFY_SOCKET: ~s", [Path]),
×
82
            Destination = {local, Path},
×
83
            case gen_udp:open(0, [local]) of
×
84
                {ok, Socket} ->
85
                    State = #state{socket = Socket,
×
86
                                   destination = Destination,
87
                                   interval = get_watchdog_interval()},
88
                    {ok, maybe_start_timer(State)};
×
89
                {error, Reason} ->
90
                    ?CRITICAL_MSG("Cannot open IPC socket: ~p", [Reason]),
×
91
                    {stop, Reason}
×
92
            end;
93
        _ ->
94
            ?INFO_MSG("Got no NOTIFY_SOCKET, notifications disabled", []),
11✔
95
            {ok, #state{}}
11✔
96
    end.
97

98
-spec handle_call(term(), {pid(), term()}, state())
99
      -> {reply, {error, badarg}, state()}.
100
handle_call(Request, From, State) ->
101
    ?ERROR_MSG("Got unexpected request from ~p: ~p", [From, Request]),
×
102
    {reply, {error, badarg}, State}.
×
103

104
-spec handle_cast({notify, binary()} | term(), state()) -> {noreply, state()}.
105
handle_cast({notify, Notification},
106
            #state{destination = undefined} = State) ->
107
    ?DEBUG("No NOTIFY_SOCKET, dropping ~s notification", [Notification]),
26✔
108
    {noreply, State};
26✔
109
handle_cast({notify, Notification}, State) ->
110
    try notify(State, Notification)
×
111
    catch _:Err ->
112
            ?ERROR_MSG("Cannot send ~s notification: ~p", [Notification, Err])
×
113
    end,
114
    {noreply, State};
×
115
handle_cast(Msg, State) ->
116
    ?ERROR_MSG("Got unexpected message: ~p", [Msg]),
×
117
    {noreply, State}.
×
118

119
-spec handle_info(ping_watchdog | term(), state()) -> {noreply, state()}.
120
handle_info(ping_watchdog, #state{interval = Interval} = State)
121
  when is_integer(Interval), Interval > 0 ->
122
    try notify(State, <<"WATCHDOG=1">>)
×
123
    catch _:Err ->
124
            ?ERROR_MSG("Cannot ping watchdog: ~p", [Err])
×
125
    end,
126
    {noreply, start_timer(State)};
×
127
handle_info(Info, State) ->
128
    ?ERROR_MSG("Got unexpected info: ~p", [Info]),
×
129
    {noreply, State}.
×
130

131
-spec terminate(normal | shutdown | {shutdown, term()} | term(), state()) -> ok.
132
terminate(Reason, #state{socket = Socket} = State) ->
133
    ?DEBUG("Terminating ~s (~p)", [?MODULE, Reason]),
11✔
134
    cancel_timer(State),
11✔
135
    case Socket of
11✔
136
        undefined ->
137
            ok;
11✔
138
        _Socket ->
139
            gen_udp:close(Socket)
×
140
    end.
141

142
-spec code_change({down, term()} | term(), state(), term()) -> {ok, state()}.
143
code_change(_OldVsn, State, _Extra) ->
144
    ?INFO_MSG("Got code change request", []),
×
145
    {ok, State}.
×
146

147
%%--------------------------------------------------------------------
148
%% Internal functions.
149
%%--------------------------------------------------------------------
150
-spec get_watchdog_interval() -> integer() | undefined.
151
get_watchdog_interval() ->
152
    case os:getenv("WATCHDOG_USEC") of
×
153
        WatchdogUSec when is_list(WatchdogUSec), length(WatchdogUSec) > 0 ->
154
            Interval = round(0.5 * list_to_integer(WatchdogUSec)),
×
155
            ?DEBUG("Watchdog interval: ~B microseconds", [Interval]),
×
156
            erlang:convert_time_unit(Interval, microsecond, millisecond);
×
157
        _ ->
158
            undefined
×
159
    end.
160

161
-spec maybe_start_timer(state()) -> state().
162
maybe_start_timer(#state{interval = Interval} = State)
163
  when is_integer(Interval), Interval > 0 ->
164
    ?INFO_MSG("Watchdog notifications enabled", []),
×
165
    start_timer(State);
×
166
maybe_start_timer(State) ->
167
    ?INFO_MSG("Watchdog notifications disabled", []),
×
168
    State.
×
169

170
-spec start_timer(state()) -> state().
171
start_timer(#state{interval = Interval} = State) ->
172
    ?DEBUG("Pinging watchdog in ~B milliseconds", [Interval]),
×
173
    State#state{timer = erlang:send_after(Interval, self(), ping_watchdog)}.
×
174

175
-spec cancel_timer(state()) -> ok.
176
cancel_timer(#state{timer = Timer}) ->
177
    ?DEBUG("Cancelling watchdog timer", []),
11✔
178
    misc:cancel_timer(Timer).
11✔
179

180
-spec notify(state(), binary()) -> ok.
181
notify(#state{socket = Socket, destination = Destination},
182
       Notification) ->
183
    ?DEBUG("Notifying systemd: ~s", [Notification]),
×
184
    ok = gen_udp:send(Socket, Destination, 0, Notification).
×
185

186
-spec cast_notification(binary()) -> ok.
187
cast_notification(Notification) ->
188
    ?DEBUG("Closing NOTIFY_SOCKET", []),
26✔
189
    gen_server:cast(?MODULE, {notify, Notification}).
26✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc