Skip to content

Commit 231b94d

Browse files
authored
Merge pull request #5721 from last-genius/private/asultanov/standalone-systemd
2 parents e6af4ab + 7e74dad commit 231b94d

File tree

17 files changed

+246
-13
lines changed

17 files changed

+246
-13
lines changed

dune-project

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,6 @@
181181
mtime
182182
ppx_deriving_rpc
183183
rpclib
184-
systemd
185184
(ezxenstore (= :version))
186185
(uuid (= :version))
187186
xapi-backtrace
@@ -517,6 +516,7 @@ This package provides an Lwt compatible interface to the library.")
517516
(fd-send-recv (>= 2.0.0))
518517
(odoc :with-doc)
519518
xapi-backtrace
519+
unix-errno
520520
(xapi-stdext-pervasives (= :version))
521521
)
522522
)

ocaml/forkexecd/src/dune

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
astring
66
fd-send-recv
77
forkexec
8-
systemd
98
uuid
109
xapi-log
1110
xapi-stdext-unix

ocaml/forkexecd/src/fe_main.ml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@ let setup sock cmdargs id_to_fd_map syslog_stdout redirect_stderr_to_stdout env
4646
Some {Fe.fd_sock_path}
4747
)
4848

49-
let systemd_managed () = try Daemon.booted () with Unix.Unix_error _ -> false
50-
5149
let _ =
5250
Sys.set_signal Sys.sigpipe Sys.Signal_ignore ;
5351

@@ -63,8 +61,10 @@ let _ =
6361
in
6462
Xapi_stdext_unix.Unixext.mkdir_rec Forkhelpers.temp_dir_server 0o755 ;
6563

66-
if systemd_managed () then
67-
Daemon.notify Daemon.State.Ready |> ignore ;
64+
let module Daemon = Xapi_stdext_unix.Unixext.Daemon in
65+
if Daemon.systemd_booted () && not (Daemon.systemd_notify Daemon.State.Ready)
66+
then
67+
warn "Sending systemd notification failed at %s" __LOC__ ;
6868

6969
(* At this point the init.d script should return and we are listening on our socket. *)
7070
while true do

ocaml/libs/xapi-stdext/lib/xapi-stdext-unix/dune

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
fd-send-recv
66
unix
77
xapi-backtrace
8+
unix-errno
9+
unix-errno.unix
10+
astring
811
xapi-stdext-pervasives)
912
(foreign_stubs
1013
(language c)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
(executable
2+
(modes exe)
3+
(name test_systemd)
4+
(libraries xapi-stdext-unix))
5+
6+
(cram
7+
(deps test_systemd.exe))
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
let _ =
2+
let module Daemon = Xapi_stdext_unix.Unixext.Daemon in
3+
let notify_test () =
4+
if Daemon.systemd_notify Daemon.State.Ready then
5+
exit 0
6+
else
7+
match Sys.getenv_opt "NOTIFY_SOCKET" with
8+
| Some _ ->
9+
exit 4
10+
| None ->
11+
print_endline "NOTIFY_SOCKET not set, notification couldn't be sent" ;
12+
exit 1
13+
in
14+
let server () =
15+
let temp_path =
16+
match Sys.getenv_opt "NOTIFY_SOCKET" with Some a -> a | None -> exit 4
17+
in
18+
let socket_path =
19+
if String.starts_with ~prefix:"@" temp_path then (
20+
print_endline temp_path ;
21+
"\x00" ^ String.sub temp_path 1 (String.length temp_path - 1)
22+
) else
23+
temp_path
24+
in
25+
Unix.(
26+
let sock = socket PF_UNIX SOCK_DGRAM 0 ~cloexec:true in
27+
bind sock (ADDR_UNIX socket_path) ;
28+
let b = Bytes.create 1024 in
29+
let i, _ = recvfrom sock b 0 1024 [] in
30+
print_endline (Bytes.sub_string b 0 i) ;
31+
close sock
32+
)
33+
in
34+
let booted_test () =
35+
if Daemon.systemd_booted () then (
36+
print_endline "Booted with systemd" ;
37+
exit 0
38+
) else (
39+
print_endline "Booted without systemd" ;
40+
exit 2
41+
)
42+
in
43+
Arg.parse
44+
[
45+
("--notify", Arg.Unit notify_test, "Test systemd_notify function")
46+
; ("--server", Arg.Unit server, "Listen for the notifications")
47+
; ("--booted", Arg.Unit booted_test, "Test systemd_booted function")
48+
]
49+
(fun _ -> raise (Arg.Bad "Specify a valid option"))
50+
"Unit test for the Daemon module in Xapi_stdext_unix\nUsage:\n"

ocaml/libs/xapi-stdext/lib/xapi-stdext-unix/test/test_systemd.mli

Whitespace-only changes.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
== Testing no-ops
2+
$ unset NOTIFY_SOCKET
3+
$ ./test_systemd.exe --notify
4+
NOTIFY_SOCKET not set, notification couldn't be sent
5+
[1]
6+
7+
== Use abstract sockets
8+
$ export NOTIFY_SOCKET="@systemd.socket"; echo "$NOTIFY_SOCKET"
9+
@systemd.socket
10+
$ ./test_systemd.exe --server &
11+
@systemd.socket
12+
READY=1
13+
$ sleep 1
14+
$ ./test_systemd.exe --notify
15+
16+
== Use socket files
17+
$ export TMPDIR=${TMPDIR:-/tmp}
18+
$ export XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-$TMPDIR}
19+
$ export NOTIFY_SOCKET="${XDG_RUNTIME_DIR}/systemd.socket"
20+
$ rm -f "$NOTIFY_SOCKET"
21+
$ ./test_systemd.exe --server &
22+
READY=1
23+
$ sleep 1
24+
$ test -S "$NOTIFY_SOCKET"
25+
$ ./test_systemd.exe --notify
26+
27+
== Currently not run tests because of insufficient permissions
28+
== in cram to be manipulating this file
29+
$ mv /run/systemd/system /run/systemd/system.old
30+
$ ./test_systemd.exe booted
31+
Booted without systemd
32+
$ mv /run/systemd/system.old /run/systemd/system
33+
$ ./test_systemd.exe booted
34+
Booted with systemd

ocaml/libs/xapi-stdext/lib/xapi-stdext-unix/unixext.ml

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,3 +817,91 @@ module Direct = struct
817817
818818
let lseek fd x cmd = Unix.LargeFile.lseek fd x cmd
819819
end
820+
821+
(* --------------------------------------------------------------------------------------- *)
822+
823+
module Daemon = struct
824+
module State = struct
825+
type t =
826+
| Ready
827+
| Reloading
828+
| Stopping
829+
| Status of string
830+
| Error of Unix.error
831+
| Buserror of string
832+
| MainPID of int
833+
| Watchdog
834+
end
835+
836+
let systemd_notify state =
837+
let open State in
838+
let ( let* ) = Option.bind in
839+
let msg_status =
840+
let* msg =
841+
match state with
842+
| Ready ->
843+
Some "READY=1"
844+
| Reloading ->
845+
Some "RELOADING=1"
846+
| Stopping ->
847+
Some "STOPPING=1"
848+
| Status s ->
849+
Some ("STATUS=" ^ s)
850+
| Error e ->
851+
Option.map
852+
(fun x -> "ERRNO=" ^ x)
853+
( match Errno_unix.of_unix e with
854+
| h :: _ ->
855+
Option.map
856+
(fun x -> Signed.SInt.to_string x)
857+
(Errno.to_code ~host:Errno_unix.host h)
858+
| [] ->
859+
None
860+
(* If empty, then couldn't map the Unix.error to an
861+
integer - a requirement of systemd's protocol *)
862+
)
863+
| Buserror s ->
864+
Some ("BUSERROR=" ^ s)
865+
| MainPID i ->
866+
Some ("MAINPID=" ^ string_of_int i)
867+
| Watchdog ->
868+
Some "WATCHDOG=1"
869+
in
870+
let* env_socket = Sys.getenv_opt "NOTIFY_SOCKET" in
871+
(* If the variable is not set, the protocol is a noop *)
872+
let* socket_path =
873+
if String.starts_with ~prefix:"/" env_socket then
874+
Some env_socket
875+
else if String.starts_with ~prefix:"@" env_socket then
876+
Some ("\x00" ^ Astring.String.with_range ~first:1 env_socket)
877+
(* Handle abstract socket - replaces '@' with the null character *)
878+
else
879+
None
880+
(* Only AF_UNIX is supported, with path or abstract sockets *)
881+
in
882+
Unix.(
883+
let sock = socket PF_UNIX SOCK_DGRAM 0 ~cloexec:true in
884+
Xapi_stdext_pervasives.Pervasiveext.finally
885+
(fun _ ->
886+
let res =
887+
sendto_substring sock msg 0 (String.length msg) []
888+
(ADDR_UNIX socket_path)
889+
in
890+
if res >= 0 then Some () else None
891+
)
892+
(fun _ -> close sock)
893+
)
894+
in
895+
Option.is_some msg_status
896+
897+
(** We test whether the runtime unit file directory has been
898+
created. Systemd guarantees this to happen very early
899+
during boot.
900+
Note: libsystemd uses faccessat instead to avoid following
901+
symlinks. It is not, however, present in the OCaml Unix module. *)
902+
let systemd_booted () =
903+
try
904+
Unix.(access "/run/systemd/system" [F_OK]) ;
905+
true
906+
with Unix.Unix_error _ -> false
907+
end

ocaml/libs/xapi-stdext/lib/xapi-stdext-unix/unixext.mli

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,3 +274,50 @@ module Direct : sig
274274
val lseek : t -> int64 -> Unix.seek_command -> int64
275275
(** [lseek t offset command]: see Unix.LargeFile.lseek *)
276276
end
277+
278+
module Daemon : sig
279+
(** OCaml interface to libsystemd.
280+
281+
Standalone reimplementation of parts of `ocaml-systemd` and `libsystemd` to
282+
avoid linking libsystemd and its dependencies. Follows similar changes
283+
in the hypervisor in 78510f3a1522f2856330ffa429e0e35f8aab4277
284+
and caf864482689a5dd6a945759b6372bb260d49665 *)
285+
module State : sig
286+
type t =
287+
| Ready
288+
(** Tells the service manager that service startup is finished,
289+
or the service finished loading its configuration.
290+
Since there is little value in signaling non-readiness, the only value
291+
services should send is "READY=1" (i.e. "READY=0" is not defined). *)
292+
| Reloading
293+
(* Tells the service manager that the service is reloading its configuration. *)
294+
| Stopping
295+
(* Tells the service manager that the service is beginning its shutdown. *)
296+
| Status of string
297+
(* Passes a single-line UTF-8 status string back to the service
298+
manager that describes the service state. *)
299+
| Error of Unix.error
300+
(* If a service fails, the errno-style error code, formatted as string. *)
301+
| Buserror of string
302+
(* If a service fails, the D-Bus error-style error code. *)
303+
| MainPID of int
304+
(* The main process ID (PID) of the service, in case the service
305+
manager did not fork off the process itself. *)
306+
| Watchdog
307+
(* Tells the service manager to update the watchdog timestamp. *)
308+
end
309+
310+
val systemd_notify : State.t -> bool
311+
(** [systemd_notify state] informs systemd about changed
312+
daemon state.
313+
If the notification was sent successfully, returns true.
314+
Otherwise returns false.
315+
316+
See sd_notify(3) for more information *)
317+
318+
val systemd_booted : unit -> bool
319+
(** [systemd_booted] returns true if the system was booted with systemd,
320+
and false otherwise.
321+
322+
See sd_booted(3) for more information. *)
323+
end

0 commit comments

Comments
 (0)