Skip to content

Commit 831d915

Browse files
committed
WIP - embedding configuration
1 parent e8c590e commit 831d915

File tree

3 files changed

+98
-18
lines changed

3 files changed

+98
-18
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: 85 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//! dynamic linker. Since the goal of this project is to statically link pipewire, we need to stub
33
//! these out.
44
//!
5-
//! This file exports a number of `__wrap_*` functions, and the pipewire build uses the preprocessor
5+
//! This file pub exports a number of `__wrap_*` functions, and the pipewire build uses the preprocessor
66
//! to redirect those calls to here.
77

88
const std = @import("std");
@@ -18,27 +18,26 @@ const c = @cImport({
1818

1919
/// Since we're statically linked, we don't support `dlopen`. Instead, we look up "libraries" in the
2020
/// hard coded `libs` table.
21-
export fn __wrap_dlopen(path: ?[*:0]const u8, mode: std.c.RTLD) callconv(.c) ?*anyopaque {
21+
pub 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

3228
/// Since `dlopen` just returns handles from `libs`, `dlclose` is a noop.
33-
export fn __wrap_dlclose(handle: ?*anyopaque) callconv(.c) c_int {
29+
pub export fn __wrap_dlclose(handle: ?*anyopaque) callconv(.c) c_int {
3430
const lib: *const Lib = @ptrCast(@alignCast(handle.?));
3531
log.debug("dlclose({f})", .{lib});
3632
return 0;
3733
}
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+
pub 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,18 +64,18 @@ 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+
pub export fn __wrap_dlerror() callconv(.c) ?[*:0]const u8 {
6968
const result = err;
7069
err = null;
7170
return result;
7271
}
7372

7473
/// We don't support `dlinfo` as pipewire doesn't currently use it. If it's called, crash.
75-
export fn __wrap_dlinfo(
74+
pub 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+
pub 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,79 @@ 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+
pub 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,
106132
strategy,
107133
});
108134
return result;
109135
}
110136

137+
// XXX: wrong signature--look at the lib cinterface, not the linux one which mathces syscall types.
138+
// same issue in some other functions I think.
139+
/// Pipewire uses `open` to open the client config file. If it requests that path we fake the call,
140+
/// otherwise we pass it through as is. See `__wrap_access` for more information.
141+
pub export fn __wrap_open(
142+
path_c: [*:0]const u8,
143+
flags: std.os.linux.O,
144+
mode: std.os.linux.mode_t,
145+
) callconv(.c) usize {
146+
const path = std.mem.span(path_c);
147+
148+
const result, const strategy = b: {
149+
if (std.mem.eql(u8, path, client_config_path)) {
150+
if (client_config_fd != 0) @panic("client_config_path already open");
151+
client_config_fd = std.os.linux.open("/dev/null", flags, mode);
152+
break :b .{ client_config_fd, "faked" };
153+
} else {
154+
break :b .{ std.os.linux.open(path_c, flags, mode), "real" };
155+
}
156+
};
157+
log.debug("open(\"{f}\", {f}, {}) -> {} ({s})", .{
158+
std.zig.fmtString(path),
159+
fmtFlags(flags),
160+
mode,
161+
result,
162+
strategy,
163+
});
164+
return result;
165+
}
166+
167+
/// We wrap close just to null out `client_config_fd` when closed. See `__wrap_access` for more
168+
/// info.
169+
pub export fn __wrap_close(fd: i32) callconv(.c) usize {
170+
if (fd == client_config_fd) client_config_fd = 0;
171+
const result = std.os.linux.close(fd);
172+
log.debug("close({}) -> {}", .{ fd, result });
173+
return result;
174+
}
175+
111176
/// A dynamic library made static.
112177
const Lib = struct {
113178
/// You're allowed to pass null as the path to dlopen, in which case you're supposed to get a
@@ -354,6 +419,10 @@ const fops = struct {
354419
}
355420
};
356421

422+
pub fn fmtFlags(val: anytype) FmtFlags(@TypeOf(val)) {
423+
return .init(val);
424+
}
425+
357426
/// Formats flags, skipping any values that are 0 for brevity.
358427
fn FmtFlags(T: type) type {
359428
return struct {

0 commit comments

Comments
 (0)