feature. See also
. The project being documented here (as the example) is the Zig library itself.
Threaded.processExecutablePath
fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) process.ExecutablePathError!usize
File
Code
fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) process.ExecutablePathError!usize {
const t: *Threaded = @ptrCast(@alignCast(userdata));
switch (native_os) {
.driverkit,
.ios,
.maccatalyst,
.macos,
.tvos,
.visionos,
.watchos,
=> {
// the executable.
var symlink_path_buf: [posix.PATH_MAX + 1]u8 = undefined;
var n: u32 = symlink_path_buf.len;
const rc = std.c._NSGetExecutablePath(&symlink_path_buf, &n);
if (rc != 0) return error.NameTooLong;
const symlink_path = std.mem.sliceTo(&symlink_path_buf, 0);
return Io.Dir.realPathFileAbsolute(io(t), symlink_path, out_buffer) catch |err| switch (err) {
error.NetworkNotFound => unreachable,
error.FileBusy => unreachable,
else => |e| return e,
};
},
.linux, .serenity => return Io.Dir.readLinkAbsolute(io(t), "/proc/self/exe", out_buffer) catch |err| switch (err) {
error.UnsupportedReparsePointType => unreachable,
error.NetworkNotFound => unreachable,
error.FileBusy => unreachable,
else => |e| return e,
},
.illumos => return Io.Dir.readLinkAbsolute(io(t), "/proc/self/path/a.out", out_buffer) catch |err| switch (err) {
error.UnsupportedReparsePointType => unreachable,
error.NetworkNotFound => unreachable,
error.FileBusy => unreachable,
else => |e| return e,
},
.freebsd, .dragonfly => {
var mib: [4]c_int = .{ posix.CTL.KERN, posix.KERN.PROC, posix.KERN.PROC_PATHNAME, -1 };
var out_len: usize = out_buffer.len;
const syscall: Syscall = try .start();
while (true) switch (posix.errno(posix.system.sysctl(&mib, mib.len, out_buffer.ptr, &out_len, null, 0))) {
.SUCCESS => {
syscall.finish();
return out_len - 1;
},
.INTR => {
try syscall.checkCancel();
continue;
},
.PERM => return syscall.fail(error.PermissionDenied),
.NOMEM => return syscall.fail(error.SystemResources),
.FAULT => |err| return syscall.errnoBug(err),
.NOENT => |err| return syscall.errnoBug(err),
else => |err| return syscall.unexpectedErrno(err),
};
},
.netbsd => {
var mib = [4]c_int{ posix.CTL.KERN, posix.KERN.PROC_ARGS, -1, posix.KERN.PROC_PATHNAME };
var out_len: usize = out_buffer.len;
const syscall: Syscall = try .start();
while (true) {
switch (posix.errno(posix.system.sysctl(&mib, mib.len, out_buffer.ptr, &out_len, null, 0))) {
.SUCCESS => {
syscall.finish();
return out_len - 1;
},
.INTR => {
try syscall.checkCancel();
continue;
},
.PERM => return syscall.fail(error.PermissionDenied),
.NOMEM => return syscall.fail(error.SystemResources),
.FAULT => |err| return syscall.errnoBug(err),
.NOENT => |err| return syscall.errnoBug(err),
else => |err| return syscall.unexpectedErrno(err),
}
}
},
.openbsd, .haiku => {
// the first process argument.
const argv0 = std.mem.span(t.argv0.value orelse return error.OperationUnsupported);
if (std.mem.findScalar(u8, argv0, '/') != null) {
var resolved_buf: [std.c.PATH_MAX]u8 = undefined;
const syscall: Syscall = try .start();
while (true) {
if (std.c.realpath(argv0, &resolved_buf)) |p| {
assert(p == &resolved_buf);
break syscall.finish();
} else switch (@as(std.c.E, @fromBackingInt(@intCast(std.c._errno().*)))) {
.INTR => {
try syscall.checkCancel();
continue;
},
else => |e| {
syscall.finish();
switch (e) {
.ACCES => return error.AccessDenied,
.INVAL => |err| return errnoBug(err),
.IO => return error.InputOutput,
.LOOP => return error.SymLinkLoop,
.NAMETOOLONG => return error.NameTooLong,
.NOENT => return error.FileNotFound,
.NOTDIR => return error.NotDir,
.NOMEM => |err| return errnoBug(err),
else => |err| return posix.unexpectedErrno(err),
}
},
}
}
const resolved = std.mem.sliceTo(&resolved_buf, 0);
if (resolved.len > out_buffer.len)
return error.NameTooLong;
@memcpy(out_buffer[0..resolved.len], resolved);
return resolved.len;
} else if (argv0.len != 0) {
t.scanEnviron();
const PATH = t.environ.string.PATH orelse return error.FileNotFound;
var it = std.mem.tokenizeScalar(u8, PATH, ':');
it: while (it.next()) |dir| {
var resolved_path_buf: [std.c.PATH_MAX]u8 = undefined;
const resolved_path = std.fmt.bufPrintSentinel(&resolved_path_buf, "{s}/{s}", .{
dir, argv0,
}, 0) catch continue;
var resolved_buf: [std.c.PATH_MAX]u8 = undefined;
const syscall: Syscall = try .start();
while (true) {
if (std.c.realpath(resolved_path, &resolved_buf)) |p| {
assert(p == &resolved_buf);
break syscall.finish();
} else switch (@as(std.c.E, @fromBackingInt(@intCast(std.c._errno().*)))) {
.INTR => {
try syscall.checkCancel();
continue;
},
.NAMETOOLONG => {
syscall.finish();
return error.NameTooLong;
},
.NOMEM => {
syscall.finish();
return error.SystemResources;
},
.IO => {
syscall.finish();
return error.InputOutput;
},
.ACCES, .LOOP, .NOENT, .NOTDIR => {
syscall.finish();
continue :it;
},
else => |err| {
syscall.finish();
return posix.unexpectedErrno(err);
},
}
}
const resolved = std.mem.sliceTo(&resolved_buf, 0);
if (resolved.len > out_buffer.len)
return error.NameTooLong;
@memcpy(out_buffer[0..resolved.len], resolved);
return resolved.len;
}
}
return error.FileNotFound;
},
.windows => {
// symlink, not the path that the symlink points to. We want the path
// that the symlink points to, though, so we need to get the realpath.
var path_name_w_buf = try wToPrefixedFileW(
null,
windows.peb().ProcessParameters.ImagePathName.sliceZ(),
.{},
);
const h_file = handle: {
if (OpenFile(path_name_w_buf.span(), .{
.dir = null,
.access_mask = .{
.GENERIC = .{ .READ = true },
.STANDARD = .{ .SYNCHRONIZE = true },
},
.creation = .OPEN,
.filter = .any,
})) |handle| {
break :handle handle;
} else |err| switch (err) {
error.WouldBlock => unreachable,
error.FileBusy => unreachable,
else => |e| return e,
}
};
defer windows.CloseHandle(h_file);
const wide_slice = try GetFinalPathNameByHandle(h_file, .{}, &path_name_w_buf.data);
const len = std.unicode.calcWtf8Len(wide_slice);
if (len > out_buffer.len)
return error.NameTooLong;
const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
return end_index;
},
else => return error.OperationUnsupported,
}
}