diff --git a/command-line/albatross_cli.ml b/command-line/albatross_cli.ml index 489271b..4427ec6 100644 --- a/command-line/albatross_cli.ml +++ b/command-line/albatross_cli.ml @@ -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 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 | Ok () -> Ok Success | Error e -> Ok e diff --git a/daemon/albatross_console.ml b/daemon/albatross_console.ml index 7d3ccb2..74d7662 100644 --- a/daemon/albatross_console.ml +++ b/daemon/albatross_console.ml @@ -158,12 +158,12 @@ let handle s addr = let m = Vmm_core.conn_metrics "unix" -let jump _ influx tmpdir = +let jump _ systemd influx tmpdir = Sys.(set_signal sigpipe Signal_ignore) ; Albatross_cli.set_tmpdir tmpdir; Lwt_main.run (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 () = Lwt_unix.accept s >>= fun (cs, addr) -> m `Open; @@ -177,7 +177,7 @@ open Cmdliner open Albatross_cli 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 let () = match Term.eval cmd with `Ok () -> exit 0 | _ -> exit 1 diff --git a/daemon/albatross_log.ml b/daemon/albatross_log.ml index 98cfcc0..4695957 100644 --- a/daemon/albatross_log.ml +++ b/daemon/albatross_log.ml @@ -148,7 +148,7 @@ let handle mvar ring s addr = 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) ; Albatross_cli.set_tmpdir tmpdir; Lwt_main.run @@ -161,7 +161,7 @@ let jump _ file read_only influx tmpdir = Lwt.return_unit end else begin 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 List.iter (Vmm_ring.write ring) entries ; let mvar = Lwt_mvar.create_empty () in @@ -192,7 +192,7 @@ let read_only = Arg.(value & flag & info [ "read-only" ] ~doc) 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 let () = match Term.eval cmd with `Ok () -> exit 0 | _ -> exit 1 diff --git a/daemon/albatrossd.ml b/daemon/albatrossd.ml index 8a86ab8..d9953ed 100644 --- a/daemon/albatrossd.ml +++ b/daemon/albatrossd.ml @@ -135,7 +135,7 @@ let write_reply name fd txt (hdr, cmd) = 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); Albatross_cli.set_tmpdir tmpdir; Albatross_cli.set_dbdir dbdir; @@ -165,7 +165,7 @@ let jump _ influx tmpdir dbdir retries enable_stats = else Lwt.return_none) >>= fun s -> Lwt.catch - (fun () -> Vmm_lwt.server_socket `Vmmd) + (fun () -> Vmm_lwt.server_socket ~systemd `Vmmd) (fun e -> let str = Fmt.strf "unable to create server socket %a: %s" @@ -218,7 +218,7 @@ let jump _ influx tmpdir dbdir retries enable_stats = open Cmdliner 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 let () = match Term.eval cmd with `Ok () -> exit 0 | _ -> exit 1 diff --git a/packaging/Linux/README.md b/packaging/Linux/README.md index 8c84814..c6b1940 100644 --- a/packaging/Linux/README.md +++ b/packaging/Linux/README.md @@ -1,6 +1,8 @@ # 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. +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 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/. diff --git a/packaging/Linux/albatross_console.service b/packaging/Linux/albatross_console.service index 90c1118..6808312 100644 --- a/packaging/Linux/albatross_console.service +++ b/packaging/Linux/albatross_console.service @@ -3,12 +3,13 @@ # to create an override configuration: # systemctl edit albatross_console.service Description=Albatross console daemon (albatross_console) +Requires=albatross_console.socket After=syslog.target [Service] Type=simple 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 RuntimeDirectory=albatross ExecStartPre=/bin/mkdir -p %t/albatross/fifo @@ -18,4 +19,5 @@ PIDFile=%t/albatross/console.pid RestrictAddressFamilies=AF_UNIX [Install] +Also=albatross_console.socket WantedBy=multi-user.target diff --git a/packaging/Linux/albatross_console.socket b/packaging/Linux/albatross_console.socket new file mode 100644 index 0000000..e206bfe --- /dev/null +++ b/packaging/Linux/albatross_console.socket @@ -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 diff --git a/packaging/Linux/albatross_daemon.service b/packaging/Linux/albatross_daemon.service index 380f8ba..c5eb8d3 100644 --- a/packaging/Linux/albatross_daemon.service +++ b/packaging/Linux/albatross_daemon.service @@ -1,6 +1,6 @@ [Unit] 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 [Service] @@ -8,7 +8,7 @@ Type=simple # TODO not necessarily needs to be run as root, anything that can solo5-spt/hvt, # create tap interfaces should be fine! 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 #RuntimeDirectory=albatross PIDFile=%t/albatross/daemon.pid @@ -27,4 +27,5 @@ IgnoreSIGPIPE=true #RuntimeDirectoryMode=0700 [Install] +Also=albatross_daemon.socket WantedBy=multi-user.target diff --git a/packaging/Linux/albatross_daemon.socket b/packaging/Linux/albatross_daemon.socket new file mode 100644 index 0000000..fe2ec07 --- /dev/null +++ b/packaging/Linux/albatross_daemon.socket @@ -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 diff --git a/packaging/Linux/albatross_log.service b/packaging/Linux/albatross_log.service index 56a6408..a70bd87 100644 --- a/packaging/Linux/albatross_log.service +++ b/packaging/Linux/albatross_log.service @@ -4,17 +4,18 @@ # systemctl edit albatross_log.service Description=Albatross log daemon (albatross_log) After=syslog.target albatross_console.service -Requires=albatross_console.service +Requires=albatross_log.socket +AssertPathExists=/var/lib/albatross/albatross.log [Service] Type=simple User=albatross -AssertPathExists=/var/lib/albatross/albatross.log -ExecStart=/usr/local/sbin/albatross-log --logfile="/var/lib/albatross/albatross.log" --tmpdir="%t/albatross/" -vv +ExecStart=/usr/local/sbin/albatross-log --systemd-socket-activation --logfile="/var/lib/albatross/albatross.log" --tmpdir="%t/albatross/" -vv RuntimeDirectory=albatross albatross/util #RuntimeDirectoryPreserve=yes # avoid albatross.log being cleaned up PIDFile=%t/albatross/log.pid RestrictAddressFamilies=AF_UNIX [Install] +Also=albatross_log.socket WantedBy=multi-user.target diff --git a/packaging/Linux/albatross_log.socket b/packaging/Linux/albatross_log.socket new file mode 100644 index 0000000..25ba45c --- /dev/null +++ b/packaging/Linux/albatross_log.socket @@ -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 diff --git a/packaging/Linux/albatross_stat.service b/packaging/Linux/albatross_stats.service similarity index 73% rename from packaging/Linux/albatross_stat.service rename to packaging/Linux/albatross_stats.service index 247eb88..a212b9b 100644 --- a/packaging/Linux/albatross_stat.service +++ b/packaging/Linux/albatross_stats.service @@ -3,16 +3,18 @@ # to create an override configuration: # systemctl edit albatross_stat.service Description=Albatross stat daemon (albatross_stat) +Requires=albatross_stat.socket After=syslog.target [Service] Type=simple 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 RuntimeDirectory=albatross albatross/util PIDFile=%t/albatross/stat.pid RestrictAddressFamilies=AF_UNIX [Install] +Also=albatross_stat.socket WantedBy=multi-user.target diff --git a/packaging/Linux/albatross_stats.socket b/packaging/Linux/albatross_stats.socket new file mode 100644 index 0000000..eca6a5e --- /dev/null +++ b/packaging/Linux/albatross_stats.socket @@ -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 diff --git a/packaging/Linux/install.sh b/packaging/Linux/install.sh index 8e1f41a..fa7e6ef 100755 --- a/packaging/Linux/install.sh +++ b/packaging/Linux/install.sh @@ -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 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 stop albatross_console sudo systemctl start albatross_console diff --git a/src/vmm_lwt.ml b/src/vmm_lwt.ml index fd87cea..d370f05 100644 --- a/src/vmm_lwt.ml +++ b/src/vmm_lwt.ml @@ -12,20 +12,25 @@ let safe_close fd = (fun () -> Lwt_unix.close fd) (fun _ -> Lwt.return_unit) -let server_socket sock = - let name = Vmm_core.socket_path sock in - (Lwt_unix.file_exists name >>= function - | true -> Lwt_unix.unlink name - | false -> Lwt.return_unit) >>= fun () -> - let s = Lwt_unix.(socket PF_UNIX SOCK_STREAM 0) in - Lwt_unix.set_close_on_exec s ; - let old_umask = Unix.umask 0 in - let _ = Unix.umask (old_umask land 0o707) in - Lwt_unix.(bind s (ADDR_UNIX name)) >|= fun () -> - Logs.app (fun m -> m "listening on %s" name); - let _ = Unix.umask old_umask in - Lwt_unix.listen s 1 ; - s +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 + (Lwt_unix.file_exists name >>= function + | true -> Lwt_unix.unlink name + | false -> Lwt.return_unit) >>= fun () -> + let s = Lwt_unix.(socket PF_UNIX SOCK_STREAM 0) in + Lwt_unix.set_close_on_exec s ; + let old_umask = Unix.umask 0 in + let _ = Unix.umask (old_umask land 0o707) in + Lwt_unix.(bind s (ADDR_UNIX name)) >|= fun () -> + Logs.app (fun m -> m "listening on %s" name); + let _ = Unix.umask old_umask in + Lwt_unix.listen s 1 ; + s let connect addrtype sockaddr = let c = Lwt_unix.(socket addrtype SOCK_STREAM 0) in diff --git a/src/vmm_lwt.mli b/src/vmm_lwt.mli index 9cb67a7..fc8b26d 100644 --- a/src/vmm_lwt.mli +++ b/src/vmm_lwt.mli @@ -2,7 +2,7 @@ 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 diff --git a/src/vmm_unix.ml b/src/vmm_unix.ml index 4491c45..50cedb3 100644 --- a/src/vmm_unix.ml +++ b/src/vmm_unix.ml @@ -26,6 +26,26 @@ let check_solo5_cmd name = | Ok cmd, _ | _, Ok cmd -> Ok cmd | _ -> 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 *) let check_commands () = let uname_cmd = Bos.Cmd.v "uname" in diff --git a/src/vmm_unix.mli b/src/vmm_unix.mli index 837c8f5..42fe555 100644 --- a/src/vmm_unix.mli +++ b/src/vmm_unix.mli @@ -8,6 +8,8 @@ type supported = FreeBSD | Linux val uname : supported Lazy.t +val sd_listen_fds : unit -> Unix.file_descr list option + val set_dbdir : Fpath.t -> unit val check_commands : unit -> (unit, [> R.msg ]) result diff --git a/stats/albatross_stats.ml b/stats/albatross_stats.ml index af4deb3..7e6a904 100644 --- a/stats/albatross_stats.ml +++ b/stats/albatross_stats.ml @@ -66,13 +66,13 @@ let timer () = let m = Vmm_core.conn_metrics "unix" -let jump _ interval influx tmpdir = +let jump _ systemd interval influx tmpdir = Sys.(set_signal sigpipe Signal_ignore); Albatross_cli.set_tmpdir tmpdir; let interval = Duration.(to_f (of_sec interval)) in Lwt_main.run (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 rec loop () = Lwt_unix.accept s >>= fun (cs, addr) -> @@ -90,7 +90,7 @@ let interval = Arg.(value & opt int 10 & info [ "interval" ] ~doc) 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 let () = match Term.eval cmd with `Ok () -> exit 0 | _ -> exit 1