7
7
use super :: listener;
8
8
use listener:: Listen ;
9
9
use listener:: { Connection , ConnectionMetadata } ;
10
- use log:: error;
11
- #[ cfg( not( feature = "no-parsec-user-and-clients-group" ) ) ]
12
- use std:: ffi:: CString ;
10
+ use log:: { error, warn} ;
13
11
use std:: fs;
14
12
use std:: fs:: Permissions ;
15
13
use std:: io:: { Error , ErrorKind , Result } ;
14
+ use std:: os:: unix:: fs:: FileTypeExt ;
16
15
use std:: os:: unix:: fs:: PermissionsExt ;
17
16
use std:: os:: unix:: io:: FromRawFd ;
18
17
use std:: os:: unix:: net:: UnixListener ;
19
- use std:: path:: Path ;
18
+ use std:: path:: PathBuf ;
20
19
use std:: time:: Duration ;
21
20
22
- static SOCKET_PATH : & str = "/run/parsec/parsec.sock" ;
23
- #[ cfg( not( feature = "no-parsec-user-and-clients-group" ) ) ]
21
+ static DEFAULT_SOCKET_PATH : & str = "/run/parsec/parsec.sock" ;
24
22
const PARSEC_USERNAME : & str = "parsec" ;
25
- #[ cfg( not( feature = "no-parsec-user-and-clients-group" ) ) ]
26
23
const PARSEC_GROUPNAME : & str = "parsec-clients" ;
27
24
28
25
/// Unix Domain Socket IPC manager
@@ -38,29 +35,37 @@ pub struct DomainSocketListener {
38
35
39
36
impl DomainSocketListener {
40
37
/// Initialise the connection to the Unix socket.
41
- pub fn new ( timeout : Duration ) -> Result < Self > {
42
- #[ cfg( not( feature = "no-parsec-user-and-clients-group" ) ) ]
43
- DomainSocketListener :: check_user_details ( ) ?;
38
+ pub fn new ( timeout : Duration , socket_path : PathBuf ) -> Result < Self > {
39
+ DomainSocketListener :: check_user_details ( ) ;
44
40
45
41
// If Parsec was service activated or not started under systemd, this
46
42
// will return `0`. `1` will be returned in case Parsec is socket activated.
47
43
let listener = match sd_notify:: listen_fds ( ) ? {
48
44
0 => {
49
- let socket = Path :: new ( SOCKET_PATH ) ;
50
- let parent_dir = socket. parent ( ) . unwrap ( ) ;
51
- if !parent_dir. exists ( ) {
52
- fs:: create_dir_all ( parent_dir) ?;
53
- } else if socket. exists ( ) {
54
- fs:: remove_file ( & socket) ?;
45
+ if socket_path. exists ( ) {
46
+ let meta = fs:: metadata ( & socket_path) ?;
47
+ if meta. file_type ( ) . is_socket ( ) {
48
+ warn ! (
49
+ "Removing the existing socket file at {}." ,
50
+ socket_path. display( )
51
+ ) ;
52
+ fs:: remove_file ( & socket_path) ?;
53
+ } else {
54
+ error ! (
55
+ "A file exists at {} but is not a Unix Domain Socket." ,
56
+ socket_path. display( )
57
+ ) ;
58
+ }
55
59
}
56
60
57
- let listener = UnixListener :: bind ( SOCKET_PATH ) ?;
61
+ // Will fail if a file already exists at the path.
62
+ let listener = UnixListener :: bind ( & socket_path) ?;
58
63
listener. set_nonblocking ( true ) ?;
59
64
60
65
// Set the socket's permission to 666 to allow clients of different user to
61
66
// connect.
62
67
let permissions = Permissions :: from_mode ( 0o666 ) ;
63
- fs:: set_permissions ( SOCKET_PATH , permissions) ?;
68
+ fs:: set_permissions ( socket_path , permissions) ?;
64
69
65
70
listener
66
71
}
@@ -88,53 +93,17 @@ impl DomainSocketListener {
88
93
Ok ( Self { listener, timeout } )
89
94
}
90
95
91
- #[ cfg( not( feature = "no-parsec-user-and-clients-group" ) ) ]
92
- fn check_user_details ( ) -> Result < ( ) > {
96
+ fn check_user_details ( ) {
93
97
// Check Parsec is running as parsec user
94
98
if users:: get_current_username ( ) != Some ( PARSEC_USERNAME . into ( ) ) {
95
- error ! (
96
- "Incorrect user. Parsec should be run as user {}." ,
99
+ warn ! (
100
+ "Incorrect user. Parsec should be run as user {}. Follow recommendations to install Parsec securely or clients might not be able to connect. " ,
97
101
PARSEC_USERNAME
98
102
) ;
99
- return Err ( Error :: new (
100
- ErrorKind :: PermissionDenied ,
101
- "Parsec run as incorrect user" ,
102
- ) ) ;
103
103
}
104
- // Check Parsec client group exists and parsec user is a member of it
105
- if let Some ( parsec_clients_group) = users:: get_group_by_name ( PARSEC_GROUPNAME ) {
106
- if let Some ( groups) = users:: get_user_groups ( PARSEC_USERNAME , users:: get_current_gid ( ) )
107
- {
108
- // Split to make `clippy` happy
109
- let parsec_user_in_parsec_clients_group = groups. into_iter ( ) . any ( |group| {
110
- group. gid ( ) == parsec_clients_group. gid ( )
111
- && group. name ( ) == parsec_clients_group. name ( )
112
- } ) ;
113
- // Check the parsec user is a member of the parsec clients group
114
- if parsec_user_in_parsec_clients_group {
115
- return Ok ( ( ) ) ;
116
- }
117
- error ! (
118
- "{} user not a member of {}." ,
119
- PARSEC_USERNAME , PARSEC_GROUPNAME
120
- ) ;
121
- Err ( Error :: new (
122
- ErrorKind :: PermissionDenied ,
123
- "User permissions incorrect" ,
124
- ) )
125
- } else {
126
- error ! ( "Retrieval of groups for user {} failed." , PARSEC_USERNAME ) ;
127
- Err ( Error :: new (
128
- ErrorKind :: InvalidInput ,
129
- "Failed to retrieve user groups" ,
130
- ) )
131
- }
132
- } else {
133
- error ! ( "{} group does not exist." , PARSEC_GROUPNAME ) ;
134
- Err ( Error :: new (
135
- ErrorKind :: PermissionDenied ,
136
- "Group permissions incorrect" ,
137
- ) )
104
+ // Check Parsec client group exists
105
+ if users:: get_group_by_name ( PARSEC_GROUPNAME ) . is_none ( ) {
106
+ warn ! ( "{} group does not exist. Follow recommendations to install Parsec securely or clients might not be able to connect." , PARSEC_GROUPNAME ) ;
138
107
}
139
108
}
140
109
}
@@ -190,15 +159,19 @@ impl Listen for DomainSocketListener {
190
159
}
191
160
192
161
/// Builder for `DomainSocketListener`
193
- #[ derive( Copy , Clone , Debug , Default ) ]
162
+ #[ derive( Clone , Debug , Default ) ]
194
163
pub struct DomainSocketListenerBuilder {
195
164
timeout : Option < Duration > ,
165
+ socket_path : Option < PathBuf > ,
196
166
}
197
167
198
168
impl DomainSocketListenerBuilder {
199
169
/// Create a new DomainSocketListener builder
200
170
pub fn new ( ) -> Self {
201
- DomainSocketListenerBuilder { timeout : None }
171
+ DomainSocketListenerBuilder {
172
+ timeout : None ,
173
+ socket_path : None ,
174
+ }
202
175
}
203
176
204
177
/// Add a timeout on the Unix Domain Socket used
@@ -207,12 +180,22 @@ impl DomainSocketListenerBuilder {
207
180
self
208
181
}
209
182
183
+ /// Specify the Unix Domain Socket path
184
+ pub fn with_socket_path ( mut self , socket_path : PathBuf ) -> Self {
185
+ self . socket_path = Some ( socket_path) ;
186
+ self
187
+ }
188
+
210
189
/// Build the builder into the listener
211
190
pub fn build ( self ) -> Result < DomainSocketListener > {
212
- DomainSocketListener :: new ( self . timeout . ok_or_else ( || {
213
- error ! ( "The listener timeout was not set." ) ;
214
- Error :: new ( ErrorKind :: InvalidInput , "listener timeout missing" )
215
- } ) ?)
191
+ DomainSocketListener :: new (
192
+ self . timeout . ok_or_else ( || {
193
+ error ! ( "The listener timeout was not set." ) ;
194
+ Error :: new ( ErrorKind :: InvalidInput , "listener timeout missing" )
195
+ } ) ?,
196
+ self . socket_path
197
+ . unwrap_or_else ( || DEFAULT_SOCKET_PATH . into ( ) ) ,
198
+ )
216
199
}
217
200
}
218
201
0 commit comments