22//
33// SPDX-License-Identifier: Apache-2.0
44
5- use std:: { fs :: Permissions , future:: pending, os:: unix:: fs :: PermissionsExt } ;
5+ use std:: { future:: pending, io , os:: unix:: net :: UnixListener as StdUnixListener } ;
66
77use anyhow:: { anyhow, Context , Result } ;
88use clap:: Parser ;
99use config:: BindAddr ;
1010use guest_api_service:: GuestApiHandler ;
11+ use listenfd:: ListenFd ;
1112use rocket:: {
1213 fairing:: AdHoc ,
1314 figment:: Figment ,
14- listener:: { Bind , DefaultListener } ,
15+ listener:: { unix :: UnixStream , Bind , DefaultListener , Endpoint , Listener } ,
1516} ;
1617use rocket_vsock_listener:: VsockListener ;
1718use rpc_service:: { AppState , ExternalRpcHandler , InternalRpcHandler , InternalRpcHandlerV0 } ;
@@ -26,6 +27,59 @@ mod http_routes;
2627mod models;
2728mod rpc_service;
2829
30+ /// Socket indices for systemd socket activation.
31+ /// Order matches ListenStream declarations in dstack-guest-agent.socket.
32+ mod socket_index {
33+ pub const DSTACK : usize = 0 ;
34+ pub const TAPPD : usize = 1 ;
35+ }
36+
37+ /// Systemd-activated sockets passed via LISTEN_FDS.
38+ struct ActivatedSockets {
39+ dstack : Option < StdUnixListener > ,
40+ tappd : Option < StdUnixListener > ,
41+ }
42+
43+ impl ActivatedSockets {
44+ fn from_env ( ) -> Self {
45+ let mut listenfd = ListenFd :: from_env ( ) ;
46+ Self {
47+ dstack : listenfd. take_unix_listener ( socket_index:: DSTACK ) . ok ( ) . flatten ( ) ,
48+ tappd : listenfd. take_unix_listener ( socket_index:: TAPPD ) . ok ( ) . flatten ( ) ,
49+ }
50+ }
51+ }
52+
53+ /// Wrapper for systemd-activated Unix socket that implements rocket's Listener trait.
54+ struct ActivatedUnixListener {
55+ listener : tokio:: net:: UnixListener ,
56+ }
57+
58+ impl ActivatedUnixListener {
59+ fn new ( std_listener : StdUnixListener ) -> io:: Result < Self > {
60+ std_listener. set_nonblocking ( true ) ?;
61+ let listener = tokio:: net:: UnixListener :: from_std ( std_listener) ?;
62+ Ok ( Self { listener } )
63+ }
64+ }
65+
66+ impl Listener for ActivatedUnixListener {
67+ type Accept = UnixStream ;
68+ type Connection = UnixStream ;
69+
70+ async fn accept ( & self ) -> io:: Result < Self :: Accept > {
71+ Ok ( self . listener . accept ( ) . await ?. 0 )
72+ }
73+
74+ async fn connect ( & self , accept : Self :: Accept ) -> io:: Result < Self :: Connection > {
75+ Ok ( accept)
76+ }
77+
78+ fn endpoint ( & self ) -> io:: Result < Endpoint > {
79+ self . listener . local_addr ( ) ?. try_into ( )
80+ }
81+ }
82+
2983const CARGO_PKG_VERSION : & str = env ! ( "CARGO_PKG_VERSION" ) ;
3084const GIT_REV : & str = git_version:: git_version!(
3185 args = [ "--abbrev=20" , "--always" , "--dirty=-modified" ] ,
@@ -52,6 +106,7 @@ struct Args {
52106async fn run_internal_v0 (
53107 state : AppState ,
54108 figment : Figment ,
109+ activated_socket : Option < StdUnixListener > ,
55110 sock_ready_tx : oneshot:: Sender < ( ) > ,
56111) -> Result < ( ) > {
57112 let rocket = rocket:: custom ( figment)
@@ -64,26 +119,36 @@ async fn run_internal_v0(
64119 . ignite ( )
65120 . await
66121 . map_err ( |err| anyhow ! ( "Failed to ignite rocket: {err}" ) ) ?;
67- let endpoint = DefaultListener :: bind_endpoint ( & ignite)
68- . map_err ( |err| anyhow ! ( "Failed to get endpoint: {err}" ) ) ?;
69- let listener = DefaultListener :: bind ( & ignite)
70- . await
71- . map_err ( |err| anyhow ! ( "Failed to bind on {endpoint}: {err}" ) ) ?;
72- if let Some ( path) = endpoint. unix ( ) {
73- // Allow any user to connect to the socket
74- fs_err:: set_permissions ( path, Permissions :: from_mode ( 0o777 ) ) ?;
122+
123+ if let Some ( std_listener) = activated_socket {
124+ // Use systemd-activated socket
125+ info ! ( "Using systemd-activated socket for tappd.sock" ) ;
126+ let listener = ActivatedUnixListener :: new ( std_listener) ?;
127+ sock_ready_tx. send ( ( ) ) . ok ( ) ;
128+ ignite
129+ . launch_on ( listener)
130+ . await
131+ . map_err ( |err : rocket:: Error | anyhow ! ( err. to_string( ) ) ) ?;
132+ } else {
133+ // Fall back to binding our own socket
134+ let endpoint = DefaultListener :: bind_endpoint ( & ignite)
135+ . map_err ( |err| anyhow ! ( "Failed to get endpoint: {err}" ) ) ?;
136+ let listener = DefaultListener :: bind ( & ignite)
137+ . await
138+ . map_err ( |err| anyhow ! ( "Failed to bind on {endpoint}: {err}" ) ) ?;
139+ sock_ready_tx. send ( ( ) ) . ok ( ) ;
140+ ignite
141+ . launch_on ( listener)
142+ . await
143+ . map_err ( |err| anyhow ! ( err. to_string( ) ) ) ?;
75144 }
76- sock_ready_tx. send ( ( ) ) . ok ( ) ;
77- ignite
78- . launch_on ( listener)
79- . await
80- . map_err ( |err| anyhow ! ( err. to_string( ) ) ) ?;
81145 Ok ( ( ) )
82146}
83147
84148async fn run_internal (
85149 state : AppState ,
86150 figment : Figment ,
151+ activated_socket : Option < StdUnixListener > ,
87152 sock_ready_tx : oneshot:: Sender < ( ) > ,
88153) -> Result < ( ) > {
89154 let rocket = rocket:: custom ( figment)
@@ -93,20 +158,29 @@ async fn run_internal(
93158 . ignite ( )
94159 . await
95160 . map_err ( |err| anyhow ! ( "Failed to ignite rocket: {err}" ) ) ?;
96- let endpoint = DefaultListener :: bind_endpoint ( & ignite)
97- . map_err ( |err| anyhow ! ( "Failed to get endpoint: {err}" ) ) ?;
98- let listener = DefaultListener :: bind ( & ignite)
99- . await
100- . map_err ( |err| anyhow ! ( "Failed to bind on {endpoint}: {err}" ) ) ?;
101- if let Some ( path) = endpoint. unix ( ) {
102- // Allow any user to connect to the socket
103- fs_err:: set_permissions ( path, Permissions :: from_mode ( 0o777 ) ) ?;
161+
162+ if let Some ( std_listener) = activated_socket {
163+ // Use systemd-activated socket
164+ info ! ( "Using systemd-activated socket for dstack.sock" ) ;
165+ let listener = ActivatedUnixListener :: new ( std_listener) ?;
166+ sock_ready_tx. send ( ( ) ) . ok ( ) ;
167+ ignite
168+ . launch_on ( listener)
169+ . await
170+ . map_err ( |err : rocket:: Error | anyhow ! ( err. to_string( ) ) ) ?;
171+ } else {
172+ // Fall back to binding our own socket
173+ let endpoint = DefaultListener :: bind_endpoint ( & ignite)
174+ . map_err ( |err| anyhow ! ( "Failed to get endpoint: {err}" ) ) ?;
175+ let listener = DefaultListener :: bind ( & ignite)
176+ . await
177+ . map_err ( |err| anyhow ! ( "Failed to bind on {endpoint}: {err}" ) ) ?;
178+ sock_ready_tx. send ( ( ) ) . ok ( ) ;
179+ ignite
180+ . launch_on ( listener)
181+ . await
182+ . map_err ( |err| anyhow ! ( err. to_string( ) ) ) ?;
104183 }
105- sock_ready_tx. send ( ( ) ) . ok ( ) ;
106- ignite
107- . launch_on ( listener)
108- . await
109- . map_err ( |err| anyhow ! ( err. to_string( ) ) ) ?;
110184 Ok ( ( ) )
111185}
112186
@@ -219,11 +293,18 @@ async fn main() -> Result<()> {
219293 . extract ( )
220294 . context ( "Failed to extract bind address" ) ?;
221295 let guest_api_figment = figment. select ( "guest-api" ) ;
296+
297+ // Get systemd-activated sockets if available
298+ let activated = ActivatedSockets :: from_env ( ) ;
299+ if activated. dstack . is_some ( ) || activated. tappd . is_some ( ) {
300+ info ! ( "Systemd socket activation detected" ) ;
301+ }
302+
222303 let ( tappd_ready_tx, tappd_ready_rx) = oneshot:: channel ( ) ;
223304 let ( sock_ready_tx, sock_ready_rx) = oneshot:: channel ( ) ;
224305 tokio:: select!(
225- res = run_internal_v0( state. clone( ) , internal_v0_figment, tappd_ready_tx) => res?,
226- res = run_internal( state. clone( ) , internal_figment, sock_ready_tx) => res?,
306+ res = run_internal_v0( state. clone( ) , internal_v0_figment, activated . tappd , tappd_ready_tx) => res?,
307+ res = run_internal( state. clone( ) , internal_figment, activated . dstack , sock_ready_tx) => res?,
227308 res = run_external( state. clone( ) , external_figment) => res?,
228309 res = run_guest_api( state. clone( ) , guest_api_figment) => res?,
229310 _ = async {
0 commit comments