1
1
use libc:: c_int;
2
2
3
- use std:: fs:: File ;
3
+ use std:: fs:: { File , OpenOptions } ;
4
4
use std:: io:: { self , Read , Write } ;
5
5
use std:: mem;
6
6
use std:: mem:: MaybeUninit ;
7
7
use std:: os:: unix:: prelude:: * ;
8
+ use std:: path:: { Path , PathBuf } ;
8
9
use std:: process:: Command ;
9
10
use std:: ptr;
10
11
use std:: sync:: { Arc , Once } ;
11
12
use std:: thread:: { self , Builder , JoinHandle } ;
12
13
use std:: time:: Duration ;
13
14
14
15
#[ derive( Debug ) ]
15
- pub struct Client {
16
- read : File ,
17
- write : File ,
16
+ pub enum Client {
17
+ /// `--jobserver-auth=R,W`
18
+ Pipe { read : File , write : File } ,
19
+ /// `--jobserver-auth=fifo:PATH`
20
+ Fifo { file : File , path : PathBuf } ,
18
21
}
19
22
20
23
#[ derive( Debug ) ]
@@ -30,16 +33,18 @@ impl Client {
30
33
// wrong!
31
34
const BUFFER : [ u8 ; 128 ] = [ b'|' ; 128 ] ;
32
35
33
- set_nonblocking ( client. write . as_raw_fd ( ) , true ) ?;
36
+ let mut write = client. write ( ) ;
37
+
38
+ set_nonblocking ( write. as_raw_fd ( ) , true ) ?;
34
39
35
40
while limit > 0 {
36
41
let n = limit. min ( BUFFER . len ( ) ) ;
37
42
38
- ( & client . write ) . write_all ( & BUFFER [ ..n] ) ?;
43
+ write. write_all ( & BUFFER [ ..n] ) ?;
39
44
limit -= n;
40
45
}
41
46
42
- set_nonblocking ( client . write . as_raw_fd ( ) , false ) ?;
47
+ set_nonblocking ( write. as_raw_fd ( ) , false ) ?;
43
48
44
49
Ok ( client)
45
50
}
@@ -77,6 +82,32 @@ impl Client {
77
82
}
78
83
79
84
pub unsafe fn open ( s : & str ) -> Option < Client > {
85
+ Client :: from_fifo ( s) . or_else ( || Client :: from_pipe ( s) )
86
+ }
87
+
88
+ /// `--jobserver-auth=fifo:PATH`
89
+ fn from_fifo ( s : & str ) -> Option < Client > {
90
+ let mut parts = s. splitn ( 2 , ':' ) ;
91
+ if parts. next ( ) . unwrap ( ) != "fifo" {
92
+ return None ;
93
+ }
94
+ let path = match parts. next ( ) {
95
+ Some ( p) => Path :: new ( p) ,
96
+ None => return None ,
97
+ } ;
98
+ let file = match OpenOptions :: new ( ) . read ( true ) . write ( true ) . open ( path) {
99
+ Ok ( f) => f,
100
+ Err ( _) => return None ,
101
+ } ;
102
+ drop ( set_cloexec ( file. as_raw_fd ( ) , true ) ) ;
103
+ Some ( Client :: Fifo {
104
+ file,
105
+ path : path. into ( ) ,
106
+ } )
107
+ }
108
+
109
+ /// `--jobserver-auth=R,W`
110
+ unsafe fn from_pipe ( s : & str ) -> Option < Client > {
80
111
let mut parts = s. splitn ( 2 , ',' ) ;
81
112
let read = parts. next ( ) . unwrap ( ) ;
82
113
let write = match parts. next ( ) {
@@ -110,12 +141,28 @@ impl Client {
110
141
}
111
142
112
143
unsafe fn from_fds ( read : c_int , write : c_int ) -> Client {
113
- Client {
144
+ Client :: Pipe {
114
145
read : File :: from_raw_fd ( read) ,
115
146
write : File :: from_raw_fd ( write) ,
116
147
}
117
148
}
118
149
150
+ /// Gets the read end of our jobserver client.
151
+ fn read ( & self ) -> & File {
152
+ match self {
153
+ Client :: Pipe { read, .. } => read,
154
+ Client :: Fifo { file, .. } => file,
155
+ }
156
+ }
157
+
158
+ /// Gets the write end of our jobserver client.
159
+ fn write ( & self ) -> & File {
160
+ match self {
161
+ Client :: Pipe { write, .. } => write,
162
+ Client :: Fifo { file, .. } => file,
163
+ }
164
+ }
165
+
119
166
pub fn acquire ( & self ) -> io:: Result < Acquired > {
120
167
// Ignore interrupts and keep trying if that happens
121
168
loop {
@@ -150,11 +197,12 @@ impl Client {
150
197
// to shut us down, so we otherwise punt all errors upwards.
151
198
unsafe {
152
199
let mut fd: libc:: pollfd = mem:: zeroed ( ) ;
153
- fd. fd = self . read . as_raw_fd ( ) ;
200
+ let mut read = self . read ( ) ;
201
+ fd. fd = read. as_raw_fd ( ) ;
154
202
fd. events = libc:: POLLIN ;
155
203
loop {
156
204
let mut buf = [ 0 ] ;
157
- match ( & self . read ) . read ( & mut buf) {
205
+ match read. read ( & mut buf) {
158
206
Ok ( 1 ) => return Ok ( Some ( Acquired { byte : buf[ 0 ] } ) ) ,
159
207
Ok ( _) => {
160
208
return Err ( io:: Error :: new (
@@ -192,7 +240,7 @@ impl Client {
192
240
// always quickly release a token). If that turns out to not be the
193
241
// case we'll get an error anyway!
194
242
let byte = data. map ( |d| d. byte ) . unwrap_or ( b'+' ) ;
195
- match ( & self . write ) . write ( & [ byte] ) ? {
243
+ match self . write ( ) . write ( & [ byte] ) ? {
196
244
1 => Ok ( ( ) ) ,
197
245
_ => Err ( io:: Error :: new (
198
246
io:: ErrorKind :: Other ,
@@ -202,12 +250,15 @@ impl Client {
202
250
}
203
251
204
252
pub fn string_arg ( & self ) -> String {
205
- format ! ( "{},{}" , self . read. as_raw_fd( ) , self . write. as_raw_fd( ) )
253
+ match self {
254
+ Client :: Pipe { read, write } => format ! ( "{},{}" , read. as_raw_fd( ) , write. as_raw_fd( ) ) ,
255
+ Client :: Fifo { path, .. } => format ! ( "fifo:{}" , path. to_str( ) . unwrap( ) ) ,
256
+ }
206
257
}
207
258
208
259
pub fn available ( & self ) -> io:: Result < usize > {
209
260
let mut len = MaybeUninit :: < c_int > :: uninit ( ) ;
210
- cvt ( unsafe { libc:: ioctl ( self . read . as_raw_fd ( ) , libc:: FIONREAD , len. as_mut_ptr ( ) ) } ) ?;
261
+ cvt ( unsafe { libc:: ioctl ( self . read ( ) . as_raw_fd ( ) , libc:: FIONREAD , len. as_mut_ptr ( ) ) } ) ?;
211
262
Ok ( unsafe { len. assume_init ( ) } as usize )
212
263
}
213
264
@@ -216,12 +267,19 @@ impl Client {
216
267
// we'll configure the read/write file descriptors to *not* be
217
268
// cloexec, so they're inherited across the exec and specified as
218
269
// integers through `string_arg` above.
219
- let read = self . read . as_raw_fd ( ) ;
220
- let write = self . write . as_raw_fd ( ) ;
270
+ let read = self . read ( ) . as_raw_fd ( ) ;
271
+ let write = self . write ( ) . as_raw_fd ( ) ;
272
+ let is_pipe = match self {
273
+ Client :: Pipe { .. } => true ,
274
+ _ => false ,
275
+ } ;
221
276
unsafe {
222
277
cmd. pre_exec ( move || {
223
278
set_cloexec ( read, false ) ?;
224
- set_cloexec ( write, false ) ?;
279
+ // Only simple pipe style needs to configure both ends.
280
+ if is_pipe {
281
+ set_cloexec ( write, false ) ?;
282
+ }
225
283
Ok ( ( ) )
226
284
} ) ;
227
285
}
0 commit comments