Systemd socket activation (#43)

* Use systemd socket activation
* Pass a new command line argument --systemd-socket-activation to the daemons if running on Linux
* Install .socket files
* Systemd services depend on their sockets
* Implement sd_listen_fds in OCaml
* Set FD_CLOEXEC in sd_listen_fds
* README: add comment about socket paths
* Linux systemd scripts: Rename albatross_stat -> albatross_stats
This commit is contained in:
Reynir Björnsson 2020-11-26 12:06:28 +01:00 committed by GitHub
parent 930775b256
commit 33f7b6bcee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 124 additions and 35 deletions

View file

@ -345,6 +345,13 @@ let retry_connections =
let doc = "Number of retries when connecting to other daemons (log, console, stats etc). 0 aborts after one failure, -1 is unlimited retries." in let doc = "Number of retries when connecting to other daemons (log, console, stats etc). 0 aborts after one failure, -1 is unlimited retries." in
Arg.(value & opt int 0 & info [ "retry-connections" ] ~doc) Arg.(value & opt int 0 & info [ "retry-connections" ] ~doc)
let systemd_socket_activation =
match Lazy.force Vmm_unix.uname with
| FreeBSD -> Term.const false
| Linux ->
let doc = "Pass this flag when systemd socket activation is being used" in
Arg.(value & flag & info [ "systemd-socket-activation" ] ~doc)
let exit_status = function let exit_status = function
| Ok () -> Ok Success | Ok () -> Ok Success
| Error e -> Ok e | Error e -> Ok e

View file

@ -158,12 +158,12 @@ let handle s addr =
let m = Vmm_core.conn_metrics "unix" let m = Vmm_core.conn_metrics "unix"
let jump _ influx tmpdir = let jump _ systemd influx tmpdir =
Sys.(set_signal sigpipe Signal_ignore) ; Sys.(set_signal sigpipe Signal_ignore) ;
Albatross_cli.set_tmpdir tmpdir; Albatross_cli.set_tmpdir tmpdir;
Lwt_main.run Lwt_main.run
(Albatross_cli.init_influx "albatross_console" influx; (Albatross_cli.init_influx "albatross_console" influx;
Vmm_lwt.server_socket `Console >>= fun s -> Vmm_lwt.server_socket ~systemd `Console >>= fun s ->
let rec loop () = let rec loop () =
Lwt_unix.accept s >>= fun (cs, addr) -> Lwt_unix.accept s >>= fun (cs, addr) ->
m `Open; m `Open;
@ -177,7 +177,7 @@ open Cmdliner
open Albatross_cli open Albatross_cli
let cmd = let cmd =
Term.(term_result (const jump $ setup_log $ influx $ tmpdir)), Term.(term_result (const jump $ setup_log $ systemd_socket_activation $ influx $ tmpdir)),
Term.info "albatross_console" ~version Term.info "albatross_console" ~version
let () = match Term.eval cmd with `Ok () -> exit 0 | _ -> exit 1 let () = match Term.eval cmd with `Ok () -> exit 0 | _ -> exit 1

View file

@ -148,7 +148,7 @@ let handle mvar ring s addr =
let m = Vmm_core.conn_metrics "unix" let m = Vmm_core.conn_metrics "unix"
let jump _ file read_only influx tmpdir = let jump _ systemd file read_only influx tmpdir =
Sys.(set_signal sigpipe Signal_ignore) ; Sys.(set_signal sigpipe Signal_ignore) ;
Albatross_cli.set_tmpdir tmpdir; Albatross_cli.set_tmpdir tmpdir;
Lwt_main.run Lwt_main.run
@ -161,7 +161,7 @@ let jump _ file read_only influx tmpdir =
Lwt.return_unit Lwt.return_unit
end else begin end else begin
Albatross_cli.init_influx "albatross_log" influx; Albatross_cli.init_influx "albatross_log" influx;
Vmm_lwt.server_socket `Log >>= fun s -> Vmm_lwt.server_socket ~systemd `Log >>= fun s ->
let ring = Vmm_ring.create `Startup () in let ring = Vmm_ring.create `Startup () in
List.iter (Vmm_ring.write ring) entries ; List.iter (Vmm_ring.write ring) entries ;
let mvar = Lwt_mvar.create_empty () in let mvar = Lwt_mvar.create_empty () in
@ -192,7 +192,7 @@ let read_only =
Arg.(value & flag & info [ "read-only" ] ~doc) Arg.(value & flag & info [ "read-only" ] ~doc)
let cmd = let cmd =
Term.(const jump $ setup_log $ file $ read_only $ influx $ tmpdir), Term.(const jump $ setup_log $ systemd_socket_activation $ file $ read_only $ influx $ tmpdir),
Term.info "albatross_log" ~version Term.info "albatross_log" ~version
let () = match Term.eval cmd with `Ok () -> exit 0 | _ -> exit 1 let () = match Term.eval cmd with `Ok () -> exit 0 | _ -> exit 1

View file

@ -135,7 +135,7 @@ let write_reply name fd txt (hdr, cmd) =
let m = conn_metrics "unix" let m = conn_metrics "unix"
let jump _ influx tmpdir dbdir retries enable_stats = let jump _ systemd influx tmpdir dbdir retries enable_stats =
Sys.(set_signal sigpipe Signal_ignore); Sys.(set_signal sigpipe Signal_ignore);
Albatross_cli.set_tmpdir tmpdir; Albatross_cli.set_tmpdir tmpdir;
Albatross_cli.set_dbdir dbdir; Albatross_cli.set_dbdir dbdir;
@ -165,7 +165,7 @@ let jump _ influx tmpdir dbdir retries enable_stats =
else else
Lwt.return_none) >>= fun s -> Lwt.return_none) >>= fun s ->
Lwt.catch Lwt.catch
(fun () -> Vmm_lwt.server_socket `Vmmd) (fun () -> Vmm_lwt.server_socket ~systemd `Vmmd)
(fun e -> (fun e ->
let str = let str =
Fmt.strf "unable to create server socket %a: %s" Fmt.strf "unable to create server socket %a: %s"
@ -218,7 +218,7 @@ let jump _ influx tmpdir dbdir retries enable_stats =
open Cmdliner open Cmdliner
let cmd = let cmd =
Term.(const jump $ setup_log $ influx $ tmpdir $ dbdir $ retry_connections $ enable_stats), Term.(const jump $ setup_log $ systemd_socket_activation $ influx $ tmpdir $ dbdir $ retry_connections $ enable_stats),
Term.info "albatrossd" ~version:Albatross_cli.version Term.info "albatrossd" ~version:Albatross_cli.version
let () = match Term.eval cmd with `Ok () -> exit 0 | _ -> exit 1 let () = match Term.eval cmd with `Ok () -> exit 0 | _ -> exit 1

View file

@ -1,6 +1,8 @@
# systemd service scripts # systemd service scripts
these are preliminary and just here to let people play with `solo5-spt`, the seccomp-enabled backend for [Solo5](https://github.com/Solo5/solo5) on Linux. these are preliminary and just here to let people play with `solo5-spt`, the seccomp-enabled backend for [Solo5](https://github.com/Solo5/solo5) on Linux.
Note: The socket paths are hardcoded relative to the RuntimeDirectory (tmpdir).
If you modify `Vmm_core.socket_path` you must modify the corresponding `.socket` file(s) in this directory.
1) You need to build the `albatross` tooling in this repository 1) You need to build the `albatross` tooling in this repository
2) To run unikernels, you need to build and install solo5-elftool and at least one of the tenders: solo5-hvt and solo5-spt. They can be installed somewhere in PATH or in /var/lib/albatross/. 2) To run unikernels, you need to build and install solo5-elftool and at least one of the tenders: solo5-hvt and solo5-spt. They can be installed somewhere in PATH or in /var/lib/albatross/.

View file

@ -3,12 +3,13 @@
# to create an override configuration: # to create an override configuration:
# systemctl edit albatross_console.service # systemctl edit albatross_console.service
Description=Albatross console daemon (albatross_console) Description=Albatross console daemon (albatross_console)
Requires=albatross_console.socket
After=syslog.target After=syslog.target
[Service] [Service]
Type=simple Type=simple
User=albatross User=albatross
ExecStart=/usr/local/sbin/albatross-console --tmpdir="%t/albatross/" -vv ExecStart=/usr/local/sbin/albatross-console --systemd-socket-activation --tmpdir="%t/albatross/" -vv
RuntimeDirectoryPreserve=yes RuntimeDirectoryPreserve=yes
RuntimeDirectory=albatross RuntimeDirectory=albatross
ExecStartPre=/bin/mkdir -p %t/albatross/fifo ExecStartPre=/bin/mkdir -p %t/albatross/fifo
@ -18,4 +19,5 @@ PIDFile=%t/albatross/console.pid
RestrictAddressFamilies=AF_UNIX RestrictAddressFamilies=AF_UNIX
[Install] [Install]
Also=albatross_console.socket
WantedBy=multi-user.target WantedBy=multi-user.target

View file

@ -0,0 +1,12 @@
[Unit]
Description=Albatross console socket
PartOf=albatross_console.service
[Socket]
ListenStream=%t/albatross/util/console.sock
SocketUser=albatross
SocketMode=0600
Accept=no
[Install]
WantedBy=sockets.target

View file

@ -1,6 +1,6 @@
[Unit] [Unit]
Description=Albatross VMM daemon (albatrossd) Description=Albatross VMM daemon (albatrossd)
Requires=albatross_console.service albatross_log.service Requires=albatross_console.socket albatross_log.socket albatross_daemon.socket
After=syslog.target albatross_console.service albatross_log.service After=syslog.target albatross_console.service albatross_log.service
[Service] [Service]
@ -8,7 +8,7 @@ Type=simple
# TODO not necessarily needs to be run as root, anything that can solo5-spt/hvt, # TODO not necessarily needs to be run as root, anything that can solo5-spt/hvt,
# create tap interfaces should be fine! # create tap interfaces should be fine!
User=root User=root
ExecStart=/usr/local/sbin/albatrossd --tmpdir="%t/albatross/" -vv ExecStart=/usr/local/sbin/albatrossd --systemd-socket-activation --tmpdir="%t/albatross/" -vv
#RuntimeDirectoryPreserve=yes #RuntimeDirectoryPreserve=yes
#RuntimeDirectory=albatross #RuntimeDirectory=albatross
PIDFile=%t/albatross/daemon.pid PIDFile=%t/albatross/daemon.pid
@ -27,4 +27,5 @@ IgnoreSIGPIPE=true
#RuntimeDirectoryMode=0700 #RuntimeDirectoryMode=0700
[Install] [Install]
Also=albatross_daemon.socket
WantedBy=multi-user.target WantedBy=multi-user.target

View file

@ -0,0 +1,11 @@
[Unit]
Description=Albatross daemon socket
PartOf=albatross_daemon.service
[Socket]
ListenStream=%t/albatross/util/vmmd.sock
SocketMode=0600
Accept=no
[Install]
WantedBy=sockets.target

View file

@ -4,17 +4,18 @@
# systemctl edit albatross_log.service # systemctl edit albatross_log.service
Description=Albatross log daemon (albatross_log) Description=Albatross log daemon (albatross_log)
After=syslog.target albatross_console.service After=syslog.target albatross_console.service
Requires=albatross_console.service Requires=albatross_log.socket
AssertPathExists=/var/lib/albatross/albatross.log
[Service] [Service]
Type=simple Type=simple
User=albatross User=albatross
AssertPathExists=/var/lib/albatross/albatross.log ExecStart=/usr/local/sbin/albatross-log --systemd-socket-activation --logfile="/var/lib/albatross/albatross.log" --tmpdir="%t/albatross/" -vv
ExecStart=/usr/local/sbin/albatross-log --logfile="/var/lib/albatross/albatross.log" --tmpdir="%t/albatross/" -vv
RuntimeDirectory=albatross albatross/util RuntimeDirectory=albatross albatross/util
#RuntimeDirectoryPreserve=yes # avoid albatross.log being cleaned up #RuntimeDirectoryPreserve=yes # avoid albatross.log being cleaned up
PIDFile=%t/albatross/log.pid PIDFile=%t/albatross/log.pid
RestrictAddressFamilies=AF_UNIX RestrictAddressFamilies=AF_UNIX
[Install] [Install]
Also=albatross_log.socket
WantedBy=multi-user.target WantedBy=multi-user.target

View file

@ -0,0 +1,12 @@
[Unit]
Description=Albatross log socket
PartOf=albatross_log.service
[Socket]
ListenStream=%t/albatross/util/log.sock
SocketUser=albatross
SocketMode=0600
Accept=no
[Install]
WantedBy=sockets.target

View file

@ -3,16 +3,18 @@
# to create an override configuration: # to create an override configuration:
# systemctl edit albatross_stat.service # systemctl edit albatross_stat.service
Description=Albatross stat daemon (albatross_stat) Description=Albatross stat daemon (albatross_stat)
Requires=albatross_stat.socket
After=syslog.target After=syslog.target
[Service] [Service]
Type=simple Type=simple
User=albatross User=albatross
ExecStart=/usr/local/sbin/albatross-stats --tmpdir="%t/albatross/" -vv ExecStart=/usr/local/sbin/albatross-stats --systemd-socket-activation --tmpdir="%t/albatross/" -vv
RuntimeDirectoryPreserve=yes RuntimeDirectoryPreserve=yes
RuntimeDirectory=albatross albatross/util RuntimeDirectory=albatross albatross/util
PIDFile=%t/albatross/stat.pid PIDFile=%t/albatross/stat.pid
RestrictAddressFamilies=AF_UNIX RestrictAddressFamilies=AF_UNIX
[Install] [Install]
Also=albatross_stat.socket
WantedBy=multi-user.target WantedBy=multi-user.target

View file

@ -0,0 +1,12 @@
[Unit]
Description=Albatross stats socket
PartOf=albatross_stats.service
[Socket]
ListenStream=%t/albatross/util/stat.sock
SocketUser=albatross
SocketMode=0600
Accept=no
[Install]
WantedBy=sockets.target

View file

@ -5,7 +5,7 @@ sudo mkdir -m 0700 -p /var/lib/albatross/block
sudo install -o "$ALBATROSS_USER" -- /dev/null /var/lib/albatross/albatross.log sudo install -o "$ALBATROSS_USER" -- /dev/null /var/lib/albatross/albatross.log
sudo cp ../../_build/install/default/bin/* /usr/local/sbin/ sudo cp ../../_build/install/default/bin/* /usr/local/sbin/
sudo cp ./albatross_*.service /etc/systemd/system/ sudo cp ./albatross_*.service ./albatross_*.socket /etc/systemd/system/
sudo systemctl daemon-reload sudo systemctl daemon-reload
sudo systemctl stop albatross_console sudo systemctl stop albatross_console
sudo systemctl start albatross_console sudo systemctl start albatross_console

View file

@ -12,7 +12,12 @@ let safe_close fd =
(fun () -> Lwt_unix.close fd) (fun () -> Lwt_unix.close fd)
(fun _ -> Lwt.return_unit) (fun _ -> Lwt.return_unit)
let server_socket sock = let server_socket ~systemd sock =
if systemd
then match Vmm_unix.sd_listen_fds () with
| Some [fd] -> Lwt.return (Lwt_unix.of_unix_file_descr fd)
| _ -> failwith "Systemd socket activation error" (* FIXME *)
else
let name = Vmm_core.socket_path sock in let name = Vmm_core.socket_path sock in
(Lwt_unix.file_exists name >>= function (Lwt_unix.file_exists name >>= function
| true -> Lwt_unix.unlink name | true -> Lwt_unix.unlink name

View file

@ -2,7 +2,7 @@
val pp_sockaddr : Format.formatter -> Lwt_unix.sockaddr -> unit val pp_sockaddr : Format.formatter -> Lwt_unix.sockaddr -> unit
val server_socket : Vmm_core.service -> Lwt_unix.file_descr Lwt.t val server_socket : systemd:bool -> Vmm_core.service -> Lwt_unix.file_descr Lwt.t
val connect : Lwt_unix.socket_domain -> Lwt_unix.sockaddr -> Lwt_unix.file_descr option Lwt.t val connect : Lwt_unix.socket_domain -> Lwt_unix.sockaddr -> Lwt_unix.file_descr option Lwt.t

View file

@ -26,6 +26,26 @@ let check_solo5_cmd name =
| Ok cmd, _ | _, Ok cmd -> Ok cmd | Ok cmd, _ | _, Ok cmd -> Ok cmd
| _ -> R.error_msgf "%s does not exist" name | _ -> R.error_msgf "%s does not exist" name
(* Pure OCaml implementation of SystemD's sd_listen_fds.
* Note: this implementation does not unset environment variables. *)
let sd_listen_fds () =
let fd_of_int (fd : int) : Unix.file_descr = Obj.magic fd in
let sd_listen_fds_start = 3 in
match Sys.getenv_opt "LISTEN_PID", Sys.getenv_opt "LISTEN_FDS" with
| None, _ | _, None -> None
| Some listen_pid, Some listen_fds ->
match int_of_string_opt listen_pid, int_of_string_opt listen_fds with
| None, _ | _, None -> None
| Some listen_pid, Some listen_fds ->
if listen_pid = Unix.getpid ()
then Some (List.init listen_fds
(fun i ->
let fd = fd_of_int (sd_listen_fds_start + i) in
let () = Unix.set_close_on_exec fd in
fd))
else None
(* here we check that the binaries we use in this file are actually present *) (* here we check that the binaries we use in this file are actually present *)
let check_commands () = let check_commands () =
let uname_cmd = Bos.Cmd.v "uname" in let uname_cmd = Bos.Cmd.v "uname" in

View file

@ -8,6 +8,8 @@ type supported = FreeBSD | Linux
val uname : supported Lazy.t val uname : supported Lazy.t
val sd_listen_fds : unit -> Unix.file_descr list option
val set_dbdir : Fpath.t -> unit val set_dbdir : Fpath.t -> unit
val check_commands : unit -> (unit, [> R.msg ]) result val check_commands : unit -> (unit, [> R.msg ]) result

View file

@ -66,13 +66,13 @@ let timer () =
let m = Vmm_core.conn_metrics "unix" let m = Vmm_core.conn_metrics "unix"
let jump _ interval influx tmpdir = let jump _ systemd interval influx tmpdir =
Sys.(set_signal sigpipe Signal_ignore); Sys.(set_signal sigpipe Signal_ignore);
Albatross_cli.set_tmpdir tmpdir; Albatross_cli.set_tmpdir tmpdir;
let interval = Duration.(to_f (of_sec interval)) in let interval = Duration.(to_f (of_sec interval)) in
Lwt_main.run Lwt_main.run
(Albatross_cli.init_influx "albatross_stats" influx; (Albatross_cli.init_influx "albatross_stats" influx;
Vmm_lwt.server_socket `Stats >>= fun s -> Vmm_lwt.server_socket ~systemd `Stats >>= fun s ->
let _ev = Lwt_engine.on_timer interval true (fun _e -> Lwt.async timer) in let _ev = Lwt_engine.on_timer interval true (fun _e -> Lwt.async timer) in
let rec loop () = let rec loop () =
Lwt_unix.accept s >>= fun (cs, addr) -> Lwt_unix.accept s >>= fun (cs, addr) ->
@ -90,7 +90,7 @@ let interval =
Arg.(value & opt int 10 & info [ "interval" ] ~doc) Arg.(value & opt int 10 & info [ "interval" ] ~doc)
let cmd = let cmd =
Term.(term_result (const jump $ setup_log $ interval $ influx $ tmpdir)), Term.(term_result (const jump $ setup_log $ systemd_socket_activation $ interval $ influx $ tmpdir)),
Term.info "albatross_stats" ~version Term.info "albatross_stats" ~version
let () = match Term.eval cmd with `Ok () -> exit 0 | _ -> exit 1 let () = match Term.eval cmd with `Ok () -> exit 0 | _ -> exit 1