Skip to content

Commit 7a19c82

Browse files
committed
WIP - embedding configuration
1 parent e8c590e commit 7a19c82

File tree

3 files changed

+126
-14
lines changed

3 files changed

+126
-14
lines changed

build.zig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ pub fn build(b: *std.Build) void {
9090
.flags = flags,
9191
});
9292

93+
// XXX: don't install this, embed it
9394
// Build and install the library configuration
9495
{
9596
const generate_conf = b.addExecutable(.{
@@ -342,6 +343,7 @@ pub fn build(b: *std.Build) void {
342343
const run_step = b.step("video-play", "Run the video-play example");
343344

344345
const run_cmd = b.addRunArtifact(video_play);
346+
// XXX: cwd...
345347
run_cmd.setCwd(.{ .cwd_relative = b.getInstallPath(.bin, "") });
346348
run_step.dependOn(&run_cmd.step);
347349

@@ -397,6 +399,13 @@ const flags: []const []const u8 = &.{
397399
"-Ddlerror=__wrap_dlerror",
398400
"-Ddlinfo=__wrap_dlinfo",
399401
"-Dstat=__wrap_stat",
402+
"-Daccess=__wrap_access",
403+
"-Dopen=__wrap_open",
404+
"-Dclose=__wrap_close",
405+
406+
// Since `spa_autoclose` points to a function defined in a header, its close doesn't get
407+
// wrapped. Wrap it manually.
408+
"-Dspa_autoclose=__attribute__((__cleanup__(__wrap_close)))",
400409
};
401410

402411
pub const PluginAndModuleCtx = struct {

build.zig.zon

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
.hash = "N-V-__8AAMV2BgB9aKHnchADe5onASuvEqAeRRO7Ai1oTuzW",
1313
},
1414
.upstream = .{
15-
.url = "git+https://gitlab.freedesktop.org/pipewire/pipewire.git#1.5.81",
16-
.hash = "N-V-__8AAKYw2AD301ZQsWszbYSWZQF5y-q4WXJif0UGRvFh",
15+
// XXX: ...
16+
// .url = "git+https://gitlab.freedesktop.org/pipewire/pipewire.git#1.5.81",
17+
// .hash = "N-V-__8AAKYw2AD301ZQsWszbYSWZQF5y-q4WXJif0UGRvFh",
18+
.path = "../pipewire",
1719
},
1820
// Used by the examples
1921
.sdl = .{

src/wrap.zig

Lines changed: 113 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,7 @@ const c = @cImport({
2121
export fn __wrap_dlopen(path: ?[*:0]const u8, mode: std.c.RTLD) callconv(.c) ?*anyopaque {
2222
const span = if (path) |p| std.mem.span(p) else Lib.main_program_name;
2323
const lib = if (libs.getIndex(span)) |index| &libs.kvs.values[index] else null;
24-
log.debug("dlopen(\"{f}\", {f}) -> {?f}", .{
25-
std.zig.fmtString(span),
26-
FmtFlags(std.c.RTLD).init(mode),
27-
lib,
28-
});
24+
log.debug("dlopen(\"{f}\", {f}) -> {?f}", .{ std.zig.fmtString(span), fmtFlags(mode), lib });
2925
return @ptrCast(@constCast(lib));
3026
}
3127

@@ -38,7 +34,10 @@ export fn __wrap_dlclose(handle: ?*anyopaque) callconv(.c) c_int {
3834

3935
/// Since `dlopen` just returns handles from `libs`, `dlsym` retrieves symbols from the correct part
4036
/// of that table.
41-
export fn __wrap_dlsym(noalias handle: ?*anyopaque, noalias name: [*:0]u8) ?*anyopaque {
37+
export fn __wrap_dlsym(
38+
noalias handle: ?*anyopaque,
39+
noalias name: [*:0]u8,
40+
) callconv(.c) ?*anyopaque {
4241
const lib: *const Lib = @ptrCast(@alignCast(handle.?));
4342
const span = std.mem.span(name);
4443
var msg: ?[:0]const u8 = null;
@@ -65,7 +64,7 @@ export fn __wrap_dlsym(noalias handle: ?*anyopaque, noalias name: [*:0]u8) ?*any
6564
/// Since `dlopen` is allowed to return null on success if the symbol is zero, `dlerror` is a
6665
/// necessary part of the `dlopen` interface.
6766
var err: ?[*:0]const u8 = null;
68-
export fn __wrap_dlerror() ?[*:0]const u8 {
67+
export fn __wrap_dlerror() callconv(.c) ?[*:0]const u8 {
6968
const result = err;
7069
err = null;
7170
return result;
@@ -76,7 +75,7 @@ export fn __wrap_dlinfo(
7675
noalias handle: ?*anyopaque,
7776
request: c_int,
7877
noalias info: ?*anyopaque,
79-
) c_int {
78+
) callconv(.c) c_int {
8079
const lib: *const Lib = @ptrCast(@alignCast(handle.?));
8180
log.debug("dlinfo({f}, {}, {x})", .{ lib, request, @intFromPtr(info) });
8281
@panic("unimplemented");
@@ -86,9 +85,8 @@ export fn __wrap_dlinfo(
8685
/// exist, we wrap `stat` to pretend that they do so it doesn't fail prematurely.
8786
///
8887
/// We do this by faking any stat calls whose paths end with `.so`. All other stat calls from
89-
/// pipewire are forwarded to the standard implementation as is, though in practice, there probably
90-
/// shouldn't be any others.
91-
export fn __wrap_stat(pathname_c: [*:0]const u8, statbuf: *std.os.linux.Stat) usize {
88+
/// pipewire are forwarded to the standard implementation as is.
89+
export fn __wrap_stat(pathname_c: [*:0]const u8, statbuf: *std.os.linux.Stat) callconv(.c) usize {
9290
const pathname = std.mem.span(pathname_c);
9391
const result, const strategy = b: {
9492
if (std.mem.endsWith(u8, pathname, ".so")) {
@@ -102,12 +100,111 @@ export fn __wrap_stat(pathname_c: [*:0]const u8, statbuf: *std.os.linux.Stat) us
102100
std.zig.fmtString(pathname),
103101
statbuf,
104102
result,
105-
FmtFlags(std.os.linux.Stat).init(statbuf.*),
103+
fmtFlags(statbuf.*),
104+
strategy,
105+
});
106+
return result;
107+
}
108+
109+
/// The path pipewire looks for client config at.
110+
const client_config_path = "pipewire-0.3/confdata/client.conf";
111+
var client_config_fd: usize = 0;
112+
113+
/// Pipewire uses `access` to check for the presence of `client.conf`. We stub this out so that
114+
/// pipewire believes it exists expected path, allowing us to later provide it directly instead of
115+
/// requiring it be available on the user's computer or at a path relative to the working directory.
116+
export fn __wrap_access(path_c: [*:0]const u8, mode: u32) callconv(.c) usize {
117+
const path = std.mem.span(path_c);
118+
119+
const result, const strategy = b: {
120+
if (mode == std.os.linux.R_OK and
121+
std.mem.eql(u8, path, client_config_path))
122+
{
123+
break :b .{ 0, "faked" };
124+
} else {
125+
break :b .{ std.os.linux.access(path, mode), "real" };
126+
}
127+
};
128+
log.debug("access(\"{f}\", {}) -> {} ({s})", .{
129+
std.zig.fmtString(path),
130+
mode,
131+
result,
132+
strategy,
133+
});
134+
return result;
135+
}
136+
137+
// XXX: wait is this usize or c_int? i think it's supposed to be cint, i think in general the linux
138+
// namespace functions may ahve different types due to just being the syscalls...
139+
// XXX: not getting called...
140+
/// Pipewire uses `open` to open the client config file. If it requests that path we fake the call,
141+
/// otherwise we pass it through as is. See `__wrap_access` for more information.
142+
export fn __wrap_open(
143+
path_c: [*:0]const u8,
144+
flags: std.os.linux.O,
145+
mode: std.os.linux.mode_t,
146+
) callconv(.c) usize {
147+
const path = std.mem.span(path_c);
148+
149+
const result, const strategy = b: {
150+
if (std.mem.eql(u8, path, client_config_path)) {
151+
if (client_config_fd != 0) @panic("client_config_path already open");
152+
client_config_fd = std.os.linux.open("/dev/null", flags, mode);
153+
break :b .{ client_config_fd, "faked" };
154+
} else {
155+
break :b .{ std.os.linux.open(path_c, flags, mode), "real" };
156+
}
157+
};
158+
// XXX: change to debug
159+
log.info("open(\"{f}\", {f}, {}) -> {} ({s})", .{
160+
std.zig.fmtString(path),
161+
fmtFlags(flags),
162+
mode,
163+
result,
106164
strategy,
107165
});
108166
return result;
109167
}
110168

169+
// XXX: get list of wrapped functions or defines from this file in the build script autoamtically? could
170+
// even just find stuff prefixed iwth __wrap if we wanted
171+
// XXX: make exports public too cause why not?
172+
/// We wrap close just to null out `client_config_fd` when closed. See `__wrap_access` for more
173+
/// info.
174+
export fn __wrap_close(fd: i32) callconv(.c) usize {
175+
if (fd == client_config_fd) client_config_fd = 0;
176+
const result = std.os.linux.close(fd);
177+
// XXX: change to debug
178+
log.err("close({}) -> {}", .{ fd, result });
179+
return result;
180+
}
181+
182+
// XXX: wrap close to close client config fd
183+
184+
// XXX: ...
185+
// /// Pipewire uses `fstat` to check a second time if a config file exists, after already having
186+
// /// checked it with `access`. We stub this out as well.
187+
// export fn fstat(fd: i32, stat_buf: *std.os.linux.Stat) usize
188+
// const path = std.mem.span(path_c);
189+
190+
// const result, const strategy = b: {
191+
// if (mode == std.os.linux.R_OK and
192+
// std.mem.eql(u8, path, "pipewire-0.3/confdata/client.conf"))
193+
// {
194+
// break :b .{ 0, "faked" };
195+
// } else {
196+
// break :b .{ std.os.linux.access(path, mode), "real" };
197+
// }
198+
// };
199+
// log.debug("access(\"{f}\", {}) -> {} ({s})", .{
200+
// std.zig.fmtString(path),
201+
// mode,
202+
// result,
203+
// strategy,
204+
// });
205+
// return result;
206+
// }
207+
111208
/// A dynamic library made static.
112209
const Lib = struct {
113210
/// You're allowed to pass null as the path to dlopen, in which case you're supposed to get a
@@ -354,6 +451,10 @@ const fops = struct {
354451
}
355452
};
356453

454+
pub fn fmtFlags(val: anytype) FmtFlags(@TypeOf(val)) {
455+
return .init(val);
456+
}
457+
357458
/// Formats flags, skipping any values that are 0 for brevity.
358459
fn FmtFlags(T: type) type {
359460
return struct {

0 commit comments

Comments
 (0)