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,31 @@ 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
+ Some ( Client :: Fifo {
103
+ file,
104
+ path : path. into ( ) ,
105
+ } )
106
+ }
107
+
108
+ /// `--jobserver-auth=R,W`
109
+ unsafe fn from_pipe ( s : & str ) -> Option < Client > {
80
110
let mut parts = s. splitn ( 2 , ',' ) ;
81
111
let read = parts. next ( ) . unwrap ( ) ;
82
112
let write = match parts. next ( ) {
@@ -110,12 +140,28 @@ impl Client {
110
140
}
111
141
112
142
unsafe fn from_fds ( read : c_int , write : c_int ) -> Client {
113
- Client {
143
+ Client :: Pipe {
114
144
read : File :: from_raw_fd ( read) ,
115
145
write : File :: from_raw_fd ( write) ,
116
146
}
117
147
}
118
148
149
+ /// Gets the read end of our jobserver client.
150
+ fn read ( & self ) -> & File {
151
+ match self {
152
+ Client :: Pipe { read, .. } => read,
153
+ Client :: Fifo { file, .. } => file,
154
+ }
155
+ }
156
+
157
+ /// Gets the write end of our jobserver client.
158
+ fn write ( & self ) -> & File {
159
+ match self {
160
+ Client :: Pipe { write, .. } => write,
161
+ Client :: Fifo { file, .. } => file,
162
+ }
163
+ }
164
+
119
165
pub fn acquire ( & self ) -> io:: Result < Acquired > {
120
166
// Ignore interrupts and keep trying if that happens
121
167
loop {
@@ -150,11 +196,12 @@ impl Client {
150
196
// to shut us down, so we otherwise punt all errors upwards.
151
197
unsafe {
152
198
let mut fd: libc:: pollfd = mem:: zeroed ( ) ;
153
- fd. fd = self . read . as_raw_fd ( ) ;
199
+ let mut read = self . read ( ) ;
200
+ fd. fd = read. as_raw_fd ( ) ;
154
201
fd. events = libc:: POLLIN ;
155
202
loop {
156
203
let mut buf = [ 0 ] ;
157
- match ( & self . read ) . read ( & mut buf) {
204
+ match read. read ( & mut buf) {
158
205
Ok ( 1 ) => return Ok ( Some ( Acquired { byte : buf[ 0 ] } ) ) ,
159
206
Ok ( _) => {
160
207
return Err ( io:: Error :: new (
@@ -192,7 +239,7 @@ impl Client {
192
239
// always quickly release a token). If that turns out to not be the
193
240
// case we'll get an error anyway!
194
241
let byte = data. map ( |d| d. byte ) . unwrap_or ( b'+' ) ;
195
- match ( & self . write ) . write ( & [ byte] ) ? {
242
+ match self . write ( ) . write ( & [ byte] ) ? {
196
243
1 => Ok ( ( ) ) ,
197
244
_ => Err ( io:: Error :: new (
198
245
io:: ErrorKind :: Other ,
@@ -202,22 +249,31 @@ impl Client {
202
249
}
203
250
204
251
pub fn string_arg ( & self ) -> String {
205
- format ! ( "{},{}" , self . read. as_raw_fd( ) , self . write. as_raw_fd( ) )
252
+ match self {
253
+ Client :: Pipe { read, write } => format ! ( "{},{}" , read. as_raw_fd( ) , write. as_raw_fd( ) ) ,
254
+ Client :: Fifo { path, .. } => format ! ( "fifo:{}" , path. to_str( ) . unwrap( ) ) ,
255
+ }
206
256
}
207
257
208
258
pub fn available ( & self ) -> io:: Result < usize > {
209
259
let mut len = MaybeUninit :: < c_int > :: uninit ( ) ;
210
- cvt ( unsafe { libc:: ioctl ( self . read . as_raw_fd ( ) , libc:: FIONREAD , len. as_mut_ptr ( ) ) } ) ?;
260
+ cvt ( unsafe { libc:: ioctl ( self . read ( ) . as_raw_fd ( ) , libc:: FIONREAD , len. as_mut_ptr ( ) ) } ) ?;
211
261
Ok ( unsafe { len. assume_init ( ) } as usize )
212
262
}
213
263
214
264
pub fn configure ( & self , cmd : & mut Command ) {
265
+ match self {
266
+ // We `File::open`ed it when inheriting from environment,
267
+ // so no need to set cloexec for fifo.
268
+ Client :: Fifo { .. } => return ,
269
+ Client :: Pipe { .. } => { }
270
+ } ;
215
271
// Here we basically just want to say that in the child process
216
272
// we'll configure the read/write file descriptors to *not* be
217
273
// cloexec, so they're inherited across the exec and specified as
218
274
// integers through `string_arg` above.
219
- let read = self . read . as_raw_fd ( ) ;
220
- let write = self . write . as_raw_fd ( ) ;
275
+ let read = self . read ( ) . as_raw_fd ( ) ;
276
+ let write = self . write ( ) . as_raw_fd ( ) ;
221
277
unsafe {
222
278
cmd. pre_exec ( move || {
223
279
set_cloexec ( read, false ) ?;
0 commit comments