@@ -3,6 +3,7 @@ const assert = std.debug.assert;
3
3
const Allocator = std .mem .Allocator ;
4
4
const builtin = @import ("builtin" );
5
5
const posix = std .posix ;
6
+ const xev = @import ("../global.zig" ).xev ;
6
7
7
8
const log = std .log .scoped (.flatpak );
8
9
@@ -71,18 +72,28 @@ pub const FlatpakHostCommand = struct {
71
72
72
73
/// Process started with the given pid on the host.
73
74
started : struct {
74
- pid : c_int ,
75
+ pid : u32 ,
76
+ loop_xev : ? * xev.Loop ,
77
+ completion : ? * Completion ,
75
78
subscription : c.guint ,
76
79
loop : * c.GMainLoop ,
77
80
},
78
81
79
82
/// Process exited
80
83
exited : struct {
81
- pid : c_int ,
84
+ pid : u32 ,
82
85
status : u8 ,
83
86
},
84
87
};
85
88
89
+ pub const Completion = struct {
90
+ callback : * const fn (ud : ? * anyopaque , l : * xev.Loop , c : * Completion , r : WaitError ! u8 ) void = noopCallback ,
91
+ c_xev : xev.Completion = .{},
92
+ userdata : ? * anyopaque = null ,
93
+ timer : ? xev.Timer = null ,
94
+ result : ? WaitError ! u8 = null ,
95
+ };
96
+
86
97
/// Errors that are possible from us.
87
98
pub const Error = error {
88
99
FlatpakMustBeStarted ,
@@ -91,12 +102,14 @@ pub const FlatpakHostCommand = struct {
91
102
FlatpakRPCFail ,
92
103
};
93
104
105
+ pub const WaitError = xev .Timer .RunError || Error ;
106
+
94
107
/// Spawn the command. This will start the host command. On return,
95
108
/// the pid will be available. This must only be called with the
96
109
/// state in "init".
97
110
///
98
111
/// Precondition: The self pointer MUST be stable.
99
- pub fn spawn (self : * FlatpakHostCommand , alloc : Allocator ) ! c_int {
112
+ pub fn spawn (self : * FlatpakHostCommand , alloc : Allocator ) ! u32 {
100
113
const thread = try std .Thread .spawn (.{}, threadMain , .{ self , alloc });
101
114
thread .setName ("flatpak-host-command" ) catch {};
102
115
@@ -135,6 +148,77 @@ pub const FlatpakHostCommand = struct {
135
148
}
136
149
}
137
150
151
+ /// Wait for the process to end asynchronously via libxev. This
152
+ /// can only be called ONCE.
153
+ pub fn waitXev (
154
+ self : * FlatpakHostCommand ,
155
+ loop : * xev.Loop ,
156
+ completion : * Completion ,
157
+ comptime Userdata : type ,
158
+ userdata : ? * Userdata ,
159
+ comptime cb : * const fn (
160
+ ud : ? * Userdata ,
161
+ l : * xev.Loop ,
162
+ c : * Completion ,
163
+ r : WaitError ! u8 ,
164
+ ) void ,
165
+ ) void {
166
+ self .state_mutex .lock ();
167
+ defer self .state_mutex .unlock ();
168
+
169
+ completion .* = .{
170
+ .callback = (struct {
171
+ fn callback (
172
+ ud_ : ? * anyopaque ,
173
+ l_inner : * xev.Loop ,
174
+ c_inner : * Completion ,
175
+ r : WaitError ! u8 ,
176
+ ) void {
177
+ const ud = @as (? * Userdata , if (Userdata == void ) null else @ptrCast (@alignCast (ud_ )));
178
+ @call (.always_inline , cb , .{ ud , l_inner , c_inner , r });
179
+ }
180
+ }).callback ,
181
+ .userdata = userdata ,
182
+ .timer = xev .Timer .init () catch unreachable , // not great, but xev timer can't fail atm
183
+ };
184
+
185
+ switch (self .state ) {
186
+ .init = > completion .result = Error .FlatpakMustBeStarted ,
187
+ .err = > completion .result = Error .FlatpakSpawnFail ,
188
+ .started = > | * v | {
189
+ v .loop_xev = loop ;
190
+ v .completion = completion ;
191
+ return ;
192
+ },
193
+ .exited = > | v | {
194
+ completion .result = v .status ;
195
+ },
196
+ }
197
+
198
+ completion .timer .? .run (
199
+ loop ,
200
+ & completion .c_xev ,
201
+ 0 ,
202
+ anyopaque ,
203
+ completion .userdata ,
204
+ (struct {
205
+ fn callback (
206
+ ud : ? * anyopaque ,
207
+ l_inner : * xev.Loop ,
208
+ c_inner : * xev.Completion ,
209
+ r : xev .Timer .RunError ! void ,
210
+ ) xev.CallbackAction {
211
+ const c_outer : * Completion = @fieldParentPtr ("c_xev" , c_inner );
212
+ defer if (c_outer .timer ) | * t | t .deinit ();
213
+
214
+ const result = if (r ) | _ | c_outer .result .? else | err | err ;
215
+ c_outer .callback (ud , l_inner , c_outer , result );
216
+ return .disarm ;
217
+ }
218
+ }).callback ,
219
+ );
220
+ }
221
+
138
222
/// Send a signal to the started command. This does nothing if the
139
223
/// command is not in the started state.
140
224
pub fn signal (self : * FlatpakHostCommand , sig : u8 , pg : bool ) ! void {
@@ -326,7 +410,7 @@ pub const FlatpakHostCommand = struct {
326
410
};
327
411
defer c .g_variant_unref (reply );
328
412
329
- var pid : c_int = 0 ;
413
+ var pid : u32 = 0 ;
330
414
c .g_variant_get (reply , "(u)" , & pid );
331
415
log .debug ("HostCommand started pid={} subscription={}" , .{
332
416
pid ,
@@ -338,6 +422,8 @@ pub const FlatpakHostCommand = struct {
338
422
.pid = pid ,
339
423
.subscription = subscription_id ,
340
424
.loop = loop ,
425
+ .completion = null ,
426
+ .loop_xev = null ,
341
427
},
342
428
});
343
429
}
@@ -366,18 +452,44 @@ pub const FlatpakHostCommand = struct {
366
452
break :state self .state .started ;
367
453
};
368
454
369
- var pid : c_int = 0 ;
370
- var exit_status : c_int = 0 ;
371
- c .g_variant_get (params .? , "(uu)" , & pid , & exit_status );
455
+ var pid : u32 = 0 ;
456
+ var exit_status_raw : u32 = 0 ;
457
+ c .g_variant_get (params .? , "(uu)" , & pid , & exit_status_raw );
372
458
if (state .pid != pid ) return ;
373
459
460
+ const exit_status = posix .W .EXITSTATUS (exit_status_raw );
374
461
// Update our state
375
462
self .updateState (.{
376
463
.exited = .{
377
464
.pid = pid ,
378
- .status = std . math . cast ( u8 , exit_status ) orelse 255 ,
465
+ .status = exit_status ,
379
466
},
380
467
});
468
+ if (state .completion ) | completion | {
469
+ completion .result = exit_status ;
470
+ completion .timer .? .run (
471
+ state .loop_xev .? ,
472
+ & completion .c_xev ,
473
+ 0 ,
474
+ anyopaque ,
475
+ completion .userdata ,
476
+ (struct {
477
+ fn callback (
478
+ ud_inner : ? * anyopaque ,
479
+ l_inner : * xev.Loop ,
480
+ c_inner : * xev.Completion ,
481
+ r : xev .Timer .RunError ! void ,
482
+ ) xev.CallbackAction {
483
+ const c_outer : * Completion = @fieldParentPtr ("c_xev" , c_inner );
484
+ defer if (c_outer .timer ) | * t | t .deinit ();
485
+
486
+ const result = if (r ) | _ | c_outer .result .? else | err | err ;
487
+ c_outer .callback (ud_inner , l_inner , c_outer , result );
488
+ return .disarm ;
489
+ }
490
+ }).callback ,
491
+ );
492
+ }
381
493
log .debug ("HostCommand exited pid={} status={}" , .{ pid , exit_status });
382
494
383
495
// We're done now, so we can unsubscribe
@@ -386,4 +498,6 @@ pub const FlatpakHostCommand = struct {
386
498
// We are also done with our loop so we can exit.
387
499
c .g_main_loop_quit (state .loop );
388
500
}
501
+
502
+ fn noopCallback (_ : ? * anyopaque , _ : * xev.Loop , _ : * Completion , _ : WaitError ! u8 ) void {}
389
503
};
0 commit comments