mirror of
https://github.com/odin-lang/Odin.git
synced 2026-01-10 15:03:22 +00:00
os2: initial implementation for Darwin&BSDs, process API is only thing incomplete
This commit is contained in:
@@ -61,4 +61,3 @@ TEMP_ALLOCATOR_GUARD :: #force_inline proc(loc := #caller_location) -> (runtime.
|
||||
global_default_temp_allocator_index = (global_default_temp_allocator_index+1)%MAX_TEMP_ARENA_COUNT
|
||||
return tmp, loc
|
||||
}
|
||||
|
||||
|
||||
71
core/os/os2/dir_posix.odin
Normal file
71
core/os/os2/dir_posix.odin
Normal file
@@ -0,0 +1,71 @@
|
||||
//+private
|
||||
//+build darwin, netbsd, freebsd, openbsd
|
||||
package os2
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
import "core:sys/posix"
|
||||
|
||||
@(private)
|
||||
_read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files: []File_Info, err: Error) {
|
||||
if f == nil || f.impl == nil {
|
||||
err = .Invalid_File
|
||||
return
|
||||
}
|
||||
|
||||
n := n
|
||||
if n == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
impl := (^File_Impl)(f)
|
||||
|
||||
dir := posix.fdopendir(impl.fd)
|
||||
if dir == nil {
|
||||
err = _get_platform_error()
|
||||
return
|
||||
}
|
||||
defer posix.closedir(dir)
|
||||
|
||||
dfiles: [dynamic]File_Info
|
||||
dfiles.allocator = allocator
|
||||
defer if err != nil {
|
||||
file_info_slice_delete(dfiles[:], allocator)
|
||||
}
|
||||
|
||||
for {
|
||||
posix.set_errno(.NONE)
|
||||
entry := posix.readdir(dir)
|
||||
if entry == nil {
|
||||
if errno := posix.errno(); errno != .NONE {
|
||||
err = _get_platform_error()
|
||||
return
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
cname := cstring(raw_data(entry.d_name[:]))
|
||||
if cname == "." || cname == ".." {
|
||||
continue
|
||||
}
|
||||
|
||||
stat: posix.stat_t
|
||||
if posix.fstatat(posix.dirfd(dir), cname, &stat, { .SYMLINK_NOFOLLOW }) != .OK {
|
||||
err = _get_platform_error()
|
||||
return
|
||||
}
|
||||
|
||||
fullpath := concatenate({impl.name, "/", string(cname)}, allocator) or_return
|
||||
fi := internal_stat(stat, fullpath)
|
||||
append(&dfiles, fi) or_return
|
||||
|
||||
n -= 1
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
files = dfiles[:]
|
||||
return
|
||||
}
|
||||
78
core/os/os2/env_posix.odin
Normal file
78
core/os/os2/env_posix.odin
Normal file
@@ -0,0 +1,78 @@
|
||||
//+private
|
||||
//+build darwin, netbsd, freebsd, openbsd
|
||||
package os2
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
import "core:strings"
|
||||
import "core:sys/posix"
|
||||
|
||||
_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
|
||||
if key == "" {
|
||||
return
|
||||
}
|
||||
|
||||
assert(!is_temp(allocator))
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
|
||||
ckey := strings.clone_to_cstring(key, temp_allocator())
|
||||
cval := posix.getenv(ckey)
|
||||
if cval == nil {
|
||||
return
|
||||
}
|
||||
|
||||
found = true
|
||||
value = strings.clone(string(cval), allocator) // NOTE(laytan): what if allocation fails?
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
_set_env :: proc(key, value: string) -> (ok: bool) {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
|
||||
ckey := strings.clone_to_cstring(key, temp_allocator())
|
||||
cval := strings.clone_to_cstring(key, temp_allocator())
|
||||
|
||||
ok = posix.setenv(ckey, cval, true) == .OK
|
||||
return
|
||||
}
|
||||
|
||||
_unset_env :: proc(key: string) -> (ok: bool) {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
|
||||
ckey := strings.clone_to_cstring(key, temp_allocator())
|
||||
|
||||
ok = posix.unsetenv(ckey) == .OK
|
||||
return
|
||||
}
|
||||
|
||||
// NOTE(laytan): clearing the env is weird, why would you ever do that?
|
||||
|
||||
_clear_env :: proc() {
|
||||
for i, entry := 0, posix.environ[0]; entry != nil; i, entry = i+1, posix.environ[i] {
|
||||
key := strings.truncate_to_byte(string(entry), '=')
|
||||
_unset_env(key)
|
||||
}
|
||||
}
|
||||
|
||||
_environ :: proc(allocator: runtime.Allocator) -> (environ: []string) {
|
||||
n := 0
|
||||
for entry := posix.environ[0]; entry != nil; n, entry = n+1, posix.environ[n] {}
|
||||
|
||||
err: runtime.Allocator_Error
|
||||
if environ, err = make([]string, n, allocator); err != nil {
|
||||
// NOTE(laytan): is the environment empty or did allocation fail, how does the user know?
|
||||
return
|
||||
}
|
||||
|
||||
for i, entry := 0, posix.environ[0]; entry != nil; i, entry = i+1, posix.environ[i] {
|
||||
if environ[i], err = strings.clone(string(entry), allocator); err != nil {
|
||||
// NOTE(laytan): is the entire environment returned or did allocation fail, how does the user know?
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
30
core/os/os2/errors_posix.odin
Normal file
30
core/os/os2/errors_posix.odin
Normal file
@@ -0,0 +1,30 @@
|
||||
//+private
|
||||
//+build darwin, netbsd, freebsd, openbsd
|
||||
package os2
|
||||
|
||||
import "core:sys/posix"
|
||||
|
||||
_error_string :: proc(errno: i32) -> string {
|
||||
return string(posix.strerror(posix.Errno(errno)))
|
||||
}
|
||||
|
||||
_get_platform_error :: proc() -> Error {
|
||||
#partial switch errno := posix.errno(); errno {
|
||||
case .EPERM:
|
||||
return .Permission_Denied
|
||||
case .EEXIST:
|
||||
return .Exist
|
||||
case .ENOENT:
|
||||
return .Not_Exist
|
||||
case .ETIMEDOUT:
|
||||
return .Timeout
|
||||
case .EPIPE:
|
||||
return .Broken_Pipe
|
||||
case .EBADF:
|
||||
return .Invalid_File
|
||||
case .ENOMEM:
|
||||
return .Out_Of_Memory
|
||||
case:
|
||||
return Platform_Error(errno)
|
||||
}
|
||||
}
|
||||
446
core/os/os2/file_posix.odin
Normal file
446
core/os/os2/file_posix.odin
Normal file
@@ -0,0 +1,446 @@
|
||||
//+private
|
||||
//+build darwin, netbsd, freebsd, openbsd
|
||||
package os2
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
import "core:io"
|
||||
import "core:c"
|
||||
import "core:time"
|
||||
import "core:sys/posix"
|
||||
|
||||
// Most implementations will EINVAL at some point when doing big writes.
|
||||
// In practice a read/write call would probably never read/write these big buffers all at once,
|
||||
// which is why the number of bytes is returned and why there are procs that will call this in a
|
||||
// loop for you.
|
||||
// We set a max of 1GB to keep alignment and to be safe.
|
||||
MAX_RW :: 1 << 30
|
||||
|
||||
File_Impl :: struct {
|
||||
file: File,
|
||||
name: string,
|
||||
cname: cstring,
|
||||
fd: posix.FD,
|
||||
}
|
||||
|
||||
@(init)
|
||||
init_std_files :: proc() {
|
||||
// NOTE: is this (paths) also the case on non darwin?
|
||||
stdin = _new_file(posix.STDIN_FILENO, "/dev/stdin")
|
||||
stdout = _new_file(posix.STDOUT_FILENO, "/dev/stdout")
|
||||
stderr = _new_file(posix.STDERR_FILENO, "/dev/stdout")
|
||||
}
|
||||
|
||||
_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
|
||||
if name == "" {
|
||||
err = .Invalid_Path
|
||||
return
|
||||
}
|
||||
|
||||
sys_flags := posix.O_Flags{.NOCTTY, .CLOEXEC}
|
||||
|
||||
if .Write in flags {
|
||||
if .Read in flags {
|
||||
sys_flags += {.RDWR}
|
||||
} else {
|
||||
sys_flags += {.WRONLY}
|
||||
}
|
||||
}
|
||||
|
||||
if .Append in flags { sys_flags += {.APPEND} }
|
||||
if .Create in flags { sys_flags += {.CREAT} }
|
||||
if .Excl in flags { sys_flags += {.EXCL} }
|
||||
if .Sync in flags { sys_flags += {.DSYNC} }
|
||||
if .Trunc in flags { sys_flags += {.TRUNC} }
|
||||
if .Inheritable in flags { sys_flags -= {.CLOEXEC} }
|
||||
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
cname := temp_cstring(name)
|
||||
|
||||
fd := posix.open(cname, sys_flags, transmute(posix.mode_t)posix._mode_t(perm))
|
||||
if fd < 0 {
|
||||
err = _get_platform_error()
|
||||
return
|
||||
}
|
||||
|
||||
return _new_file(uintptr(fd), name), nil
|
||||
}
|
||||
|
||||
_new_file :: proc(handle: uintptr, name: string) -> ^File {
|
||||
if name == "" || handle == ~uintptr(0) {
|
||||
return nil
|
||||
}
|
||||
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
cname := temp_cstring(name)
|
||||
|
||||
crname := posix.realpath(cname, nil)
|
||||
assert(crname != nil)
|
||||
rname := string(crname)
|
||||
|
||||
f := __new_file(posix.FD(handle))
|
||||
impl := (^File_Impl)(f.impl)
|
||||
impl.name = rname
|
||||
impl.cname = crname
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
__new_file :: proc(handle: posix.FD) -> ^File {
|
||||
impl := new(File_Impl, file_allocator())
|
||||
impl.file.impl = impl
|
||||
impl.fd = posix.FD(handle)
|
||||
impl.file.stream = {
|
||||
data = impl,
|
||||
procedure = _file_stream_proc,
|
||||
}
|
||||
impl.file.fstat = _fstat
|
||||
return &impl.file
|
||||
}
|
||||
|
||||
_close :: proc(f: ^File_Impl) -> (err: Error) {
|
||||
if f == nil { return nil }
|
||||
|
||||
if posix.close(f.fd) != .OK {
|
||||
err = _get_platform_error()
|
||||
}
|
||||
|
||||
delete(f.cname, file_allocator())
|
||||
posix.free(f.cname)
|
||||
return
|
||||
}
|
||||
|
||||
_fd :: proc(f: ^File) -> uintptr {
|
||||
return uintptr(__fd(f))
|
||||
}
|
||||
|
||||
__fd :: proc(f: ^File) -> posix.FD {
|
||||
if f != nil && f.impl != nil {
|
||||
return (^File_Impl)(f.impl).fd
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
_name :: proc(f: ^File) -> string {
|
||||
if f != nil && f.impl != nil {
|
||||
return (^File_Impl)(f.impl).name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
_sync :: proc(f: ^File) -> Error {
|
||||
if posix.fsync(__fd(f)) != .OK {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_truncate :: proc(f: ^File, size: i64) -> Error {
|
||||
if posix.ftruncate(__fd(f), posix.off_t(size)) != .OK {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_remove :: proc(name: string) -> Error {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
cname := temp_cstring(name)
|
||||
if posix.remove(cname) != 0 {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_rename :: proc(old_path, new_path: string) -> Error {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
cold := temp_cstring(old_path)
|
||||
cnew := temp_cstring(new_path)
|
||||
if posix.rename(cold, cnew) != 0 {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_link :: proc(old_name, new_name: string) -> Error {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
cold := temp_cstring(old_name)
|
||||
cnew := temp_cstring(new_name)
|
||||
if posix.link(cold, cnew) != .OK {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_symlink :: proc(old_name, new_name: string) -> Error {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
cold := temp_cstring(old_name)
|
||||
cnew := temp_cstring(new_name)
|
||||
if posix.symlink(cold, cnew) != .OK {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, err: Error) {
|
||||
assert(!is_temp(allocator))
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
cname := temp_cstring(name)
|
||||
|
||||
buf: [dynamic]byte
|
||||
buf.allocator = allocator
|
||||
defer if err != nil { delete(buf) }
|
||||
|
||||
// Loop this because the file might've grown between lstat() and readlink().
|
||||
for {
|
||||
stat: posix.stat_t
|
||||
if posix.lstat(cname, &stat) != .OK {
|
||||
err = _get_platform_error()
|
||||
return
|
||||
}
|
||||
|
||||
bufsiz := int(stat.st_size + 1 if stat.st_size > 0 else posix.PATH_MAX)
|
||||
|
||||
if bufsiz == len(buf) {
|
||||
bufsiz *= 2
|
||||
}
|
||||
|
||||
// Overflow.
|
||||
if bufsiz <= 0 {
|
||||
err = Platform_Error(posix.Errno.E2BIG)
|
||||
return
|
||||
}
|
||||
|
||||
resize(&buf, bufsiz) or_return
|
||||
|
||||
size := posix.readlink(cname, raw_data(buf), uint(bufsiz))
|
||||
if size < 0 {
|
||||
err = _get_platform_error()
|
||||
return
|
||||
}
|
||||
|
||||
// File has probably grown between lstat() and readlink().
|
||||
if size == bufsiz {
|
||||
continue
|
||||
}
|
||||
|
||||
s = string(buf[:size])
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
_chdir :: proc(name: string) -> Error {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
cname := temp_cstring(name)
|
||||
if posix.chdir(cname) != .OK {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_fchdir :: proc(f: ^File) -> Error {
|
||||
if posix.fchdir(__fd(f)) != .OK {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_fchmod :: proc(f: ^File, mode: int) -> Error {
|
||||
if posix.fchmod(__fd(f), transmute(posix.mode_t)posix._mode_t(mode)) != .OK {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_chmod :: proc(name: string, mode: int) -> Error {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
cname := temp_cstring(name)
|
||||
if posix.chmod(cname, transmute(posix.mode_t)posix._mode_t(mode)) != .OK {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_fchown :: proc(f: ^File, uid, gid: int) -> Error {
|
||||
if posix.fchown(__fd(f), posix.uid_t(uid), posix.gid_t(gid)) != .OK {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_chown :: proc(name: string, uid, gid: int) -> Error {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
cname := temp_cstring(name)
|
||||
if posix.chown(cname, posix.uid_t(uid), posix.gid_t(gid)) != .OK {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_lchown :: proc(name: string, uid, gid: int) -> Error {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
cname := temp_cstring(name)
|
||||
if posix.lchown(cname, posix.uid_t(uid), posix.gid_t(gid)) != .OK {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
|
||||
times := [2]posix.timeval{
|
||||
{
|
||||
tv_sec = posix.time_t(atime._nsec/1e9), /* seconds */
|
||||
tv_usec = posix.suseconds_t(atime._nsec%1e9/1000), /* microseconds */
|
||||
},
|
||||
{
|
||||
tv_sec = posix.time_t(mtime._nsec/1e9), /* seconds */
|
||||
tv_usec = posix.suseconds_t(mtime._nsec%1e9/1000), /* microseconds */
|
||||
},
|
||||
}
|
||||
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
cname := temp_cstring(name)
|
||||
|
||||
if posix.utimes(cname, ×) != .OK {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
|
||||
times := [2]posix.timespec{
|
||||
{
|
||||
tv_sec = posix.time_t(atime._nsec/1e9), /* seconds */
|
||||
tv_nsec = c.long(atime._nsec%1e9), /* nanoseconds */
|
||||
},
|
||||
{
|
||||
tv_sec = posix.time_t(mtime._nsec/1e9), /* seconds */
|
||||
tv_nsec = c.long(mtime._nsec%1e9), /* nanoseconds */
|
||||
},
|
||||
}
|
||||
|
||||
if posix.futimens(__fd(f), ×) != .OK {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_exists :: proc(path: string) -> bool {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
cpath := temp_cstring(path)
|
||||
return posix.access(cpath) == .OK
|
||||
}
|
||||
|
||||
_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
|
||||
f := (^File_Impl)(stream_data)
|
||||
fd := f.fd
|
||||
|
||||
switch mode {
|
||||
case .Read:
|
||||
if len(p) <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
to_read := uint(min(len(p), MAX_RW))
|
||||
n = i64(posix.read(fd, raw_data(p), to_read))
|
||||
switch {
|
||||
case n == 0:
|
||||
err = .EOF
|
||||
case n < 0:
|
||||
err = .Unknown
|
||||
}
|
||||
return
|
||||
|
||||
case .Read_At:
|
||||
if len(p) <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if offset < 0 {
|
||||
err = .Invalid_Offset
|
||||
return
|
||||
}
|
||||
|
||||
to_read := uint(min(len(p), MAX_RW))
|
||||
n = i64(posix.pread(fd, raw_data(p), to_read, posix.off_t(offset)))
|
||||
switch {
|
||||
case n == 0:
|
||||
err = .EOF
|
||||
case n < 0:
|
||||
err = .Unknown
|
||||
}
|
||||
return
|
||||
|
||||
case .Write:
|
||||
p := p
|
||||
for len(p) > 0 {
|
||||
to_write := uint(min(len(p), MAX_RW))
|
||||
if _n := i64(posix.write(fd, raw_data(p), to_write)); n <= 0 {
|
||||
err = .Unknown
|
||||
return
|
||||
} else {
|
||||
p = p[_n:]
|
||||
n += _n
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
case .Write_At:
|
||||
p := p
|
||||
offset := offset
|
||||
|
||||
if offset < 0 {
|
||||
err = .Invalid_Offset
|
||||
return
|
||||
}
|
||||
|
||||
for len(p) > 0 {
|
||||
to_write := uint(min(len(p), MAX_RW))
|
||||
if _n := i64(posix.pwrite(fd, raw_data(p), to_write, posix.off_t(offset))); n <= 0 {
|
||||
err = .Unknown
|
||||
return
|
||||
} else {
|
||||
p = p[_n:]
|
||||
n += _n
|
||||
offset += _n
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
case .Seek:
|
||||
#assert(int(posix.Whence.SET) == int(io.Seek_From.Start))
|
||||
#assert(int(posix.Whence.CUR) == int(io.Seek_From.Current))
|
||||
#assert(int(posix.Whence.END) == int(io.Seek_From.End))
|
||||
|
||||
n = i64(posix.lseek(fd, posix.off_t(offset), posix.Whence(whence)))
|
||||
if n < 0 {
|
||||
err = .Unknown
|
||||
}
|
||||
return
|
||||
|
||||
case .Size:
|
||||
stat: posix.stat_t
|
||||
if posix.fstat(fd, &stat) != .OK {
|
||||
err = .Unknown
|
||||
return
|
||||
}
|
||||
|
||||
n = i64(stat.st_size)
|
||||
return
|
||||
|
||||
case .Flush:
|
||||
ferr := _sync(&f.file)
|
||||
err = error_to_io_error(ferr)
|
||||
return
|
||||
|
||||
case .Close, .Destroy:
|
||||
ferr := _close(f)
|
||||
err = error_to_io_error(ferr)
|
||||
return
|
||||
|
||||
case .Query:
|
||||
return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Query})
|
||||
|
||||
case:
|
||||
return 0, .Empty
|
||||
}
|
||||
}
|
||||
97
core/os/os2/heap_posix.odin
Normal file
97
core/os/os2/heap_posix.odin
Normal file
@@ -0,0 +1,97 @@
|
||||
//+private
|
||||
//+build darwin, netbsd, freebsd, openbsd
|
||||
package os2
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
import "core:mem"
|
||||
import "core:sys/posix"
|
||||
|
||||
|
||||
_heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, mem.Allocator_Error) {
|
||||
//
|
||||
// NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
|
||||
// Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert
|
||||
// padding. We also store the original pointer returned by heap_alloc right before
|
||||
// the pointer we return to the user.
|
||||
//
|
||||
|
||||
aligned_alloc :: proc(size, alignment: int, zero_memory: bool, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
|
||||
assert(size > 0)
|
||||
|
||||
a := max(alignment, align_of(rawptr))
|
||||
space := size + a - 1
|
||||
|
||||
allocated_mem: rawptr
|
||||
if old_ptr != nil {
|
||||
original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^
|
||||
allocated_mem = posix.realloc(original_old_ptr, uint(space)+size_of(rawptr))
|
||||
} else if zero_memory {
|
||||
allocated_mem = posix.calloc(1, uint(space)+size_of(rawptr))
|
||||
} else {
|
||||
allocated_mem = posix.malloc(uint(space)+size_of(rawptr))
|
||||
}
|
||||
aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr)))
|
||||
|
||||
ptr := uintptr(aligned_mem)
|
||||
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a)
|
||||
diff := int(aligned_ptr - ptr)
|
||||
if (size + diff) > space || allocated_mem == nil {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
|
||||
aligned_mem = rawptr(aligned_ptr)
|
||||
mem.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem
|
||||
|
||||
return mem.byte_slice(aligned_mem, size), nil
|
||||
}
|
||||
|
||||
aligned_free :: proc(p: rawptr) {
|
||||
if p != nil {
|
||||
posix.free(mem.ptr_offset((^rawptr)(p), -1)^)
|
||||
}
|
||||
}
|
||||
|
||||
aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int, zero_memory: bool) -> (new_memory: []byte, err: mem.Allocator_Error) {
|
||||
assert(p != nil)
|
||||
|
||||
new_memory = aligned_alloc(new_size, new_alignment, false, p) or_return
|
||||
|
||||
if zero_memory && new_size > old_size {
|
||||
new_region := raw_data(new_memory[old_size:])
|
||||
intrinsics.mem_zero(new_region, new_size - old_size)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return aligned_alloc(size, alignment, mode == .Alloc)
|
||||
|
||||
case .Free:
|
||||
aligned_free(old_memory)
|
||||
|
||||
case .Free_All:
|
||||
return nil, .Mode_Not_Implemented
|
||||
|
||||
case .Resize, .Resize_Non_Zeroed:
|
||||
if old_memory == nil {
|
||||
return aligned_alloc(size, alignment, mode == .Resize)
|
||||
}
|
||||
return aligned_resize(old_memory, old_size, size, alignment, mode == .Resize)
|
||||
|
||||
case .Query_Features:
|
||||
set := (^mem.Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Resize, .Query_Features}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
case .Query_Info:
|
||||
return nil, .Mode_Not_Implemented
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
@@ -43,7 +43,7 @@ clone_to_cstring :: proc(s: string, allocator: runtime.Allocator) -> (res: cstri
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
temp_cstring :: proc(s: string) -> (cstring, runtime.Allocator_Error) {
|
||||
temp_cstring :: proc(s: string) -> (cstring, runtime.Allocator_Error) #optional_allocator_error {
|
||||
return clone_to_cstring(s, temp_allocator())
|
||||
}
|
||||
|
||||
|
||||
127
core/os/os2/path_posix.odin
Normal file
127
core/os/os2/path_posix.odin
Normal file
@@ -0,0 +1,127 @@
|
||||
//+private
|
||||
//+build darwin, netbsd, freebsd, openbsd
|
||||
package os2
|
||||
|
||||
import "base:runtime"
|
||||
import "core:path/filepath"
|
||||
|
||||
import "core:sys/posix"
|
||||
|
||||
_Path_Separator :: '/'
|
||||
_Path_Separator_String :: "/"
|
||||
_Path_List_Separator :: ':'
|
||||
|
||||
_is_path_separator :: proc(c: byte) -> bool {
|
||||
return c == _Path_Separator
|
||||
}
|
||||
|
||||
_mkdir :: proc(name: string, perm: int) -> Error {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
cname := temp_cstring(name)
|
||||
if posix.mkdir(cname, transmute(posix.mode_t)posix._mode_t(perm)) != .OK {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_mkdir_all :: proc(path: string, perm: int) -> Error {
|
||||
if path == "" {
|
||||
return .Invalid_Path
|
||||
}
|
||||
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
|
||||
if exists(path) {
|
||||
return .Exist
|
||||
}
|
||||
|
||||
clean_path := filepath.clean(path, temp_allocator())
|
||||
return internal_mkdir_all(clean_path, perm)
|
||||
|
||||
internal_mkdir_all :: proc(path: string, perm: int) -> Error {
|
||||
a, _ := filepath.split(path)
|
||||
if a != path {
|
||||
if len(a) > 1 && a[len(a)-1] == '/' {
|
||||
a = a[:len(a)-1]
|
||||
}
|
||||
internal_mkdir_all(a, perm) or_return
|
||||
}
|
||||
|
||||
err := _mkdir(path, perm)
|
||||
if err == .Exist { err = nil }
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_remove_all :: proc(path: string) -> Error {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
cpath := temp_cstring(path)
|
||||
|
||||
dir := posix.opendir(cpath)
|
||||
if dir == nil {
|
||||
return _get_platform_error()
|
||||
}
|
||||
|
||||
for {
|
||||
posix.set_errno(.NONE)
|
||||
entry := posix.readdir(dir)
|
||||
if entry == nil {
|
||||
if errno := posix.errno(); errno != .NONE {
|
||||
return _get_platform_error()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
cname := cstring(raw_data(entry.d_name[:]))
|
||||
if cname == "." || cname == ".." {
|
||||
continue
|
||||
}
|
||||
|
||||
fullpath, _ := concatenate({path, "/", string(cname), "\x00"}, temp_allocator())
|
||||
if entry.d_type == .DIR {
|
||||
_remove_all(fullpath[:len(fullpath)-1])
|
||||
} else {
|
||||
if posix.unlink(cstring(raw_data(fullpath))) != .OK {
|
||||
return _get_platform_error()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if posix.rmdir(cpath) != .OK {
|
||||
return _get_platform_error()
|
||||
}
|
||||
posix.closedir(dir)
|
||||
return nil
|
||||
}
|
||||
|
||||
_get_working_directory :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
assert(!is_temp(allocator))
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
|
||||
buf: [dynamic]byte
|
||||
buf.allocator = temp_allocator()
|
||||
size := uint(posix.PATH_MAX)
|
||||
|
||||
cwd: cstring
|
||||
for ; cwd == nil; size *= 2 {
|
||||
resize(&buf, size)
|
||||
|
||||
cwd = posix.getcwd(raw_data(buf), len(buf))
|
||||
if cwd == nil && posix.errno() != .ERANGE {
|
||||
err = _get_platform_error()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return clone_string(string(cwd), allocator)
|
||||
}
|
||||
|
||||
_set_working_directory :: proc(dir: string) -> (err: Error) {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
cdir := temp_cstring(dir)
|
||||
if posix.chdir(cdir) != .OK {
|
||||
err = _get_platform_error()
|
||||
}
|
||||
return
|
||||
}
|
||||
37
core/os/os2/pipe_posix.odin
Normal file
37
core/os/os2/pipe_posix.odin
Normal file
@@ -0,0 +1,37 @@
|
||||
//+private
|
||||
//+build darwin, netbsd, freebsd, openbsd
|
||||
package os2
|
||||
|
||||
import "core:sys/posix"
|
||||
import "core:strings"
|
||||
|
||||
_pipe :: proc() -> (r, w: ^File, err: Error) {
|
||||
fds: [2]posix.FD
|
||||
if posix.pipe(&fds) != .OK {
|
||||
err = _get_platform_error()
|
||||
return
|
||||
}
|
||||
|
||||
r = __new_file(fds[0])
|
||||
ri := (^File_Impl)(r.impl)
|
||||
|
||||
rname := strings.builder_make(file_allocator())
|
||||
// TODO(laytan): is this on all the posix targets?
|
||||
strings.write_string(&rname, "/dev/fd/")
|
||||
strings.write_int(&rname, int(fds[0]))
|
||||
ri.name = strings.to_string(rname)
|
||||
ri.cname = strings.to_cstring(&rname)
|
||||
|
||||
w = __new_file(fds[1])
|
||||
wi := (^File_Impl)(w.impl)
|
||||
|
||||
wname := strings.builder_make(file_allocator())
|
||||
// TODO(laytan): is this on all the posix targets?
|
||||
strings.write_string(&wname, "/dev/fd/")
|
||||
strings.write_int(&wname, int(fds[1]))
|
||||
wi.name = strings.to_string(wname)
|
||||
wi.cname = strings.to_cstring(&wname)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ TIMEOUT_INFINITE :: time.MIN_DURATION // Note(flysand): Any negative duration wi
|
||||
*/
|
||||
args := get_args()
|
||||
|
||||
@(private="file", require_results)
|
||||
@(private="file")
|
||||
get_args :: proc() -> []string {
|
||||
result := make([]string, len(runtime.args__), heap_allocator())
|
||||
for rt_arg, i in runtime.args__ {
|
||||
@@ -131,6 +131,8 @@ Process_Info_Field :: enum {
|
||||
Working_Dir,
|
||||
}
|
||||
|
||||
ALL_INFO :: Process_Info_Fields{.Executable_Path, .PPid, .Priority, .Command_Line, .Command_Args, .Environment, .Username, .Working_Dir}
|
||||
|
||||
/*
|
||||
Contains information about the process as obtained by the `process_info()`
|
||||
procedure.
|
||||
@@ -166,8 +168,8 @@ Process_Info :: struct {
|
||||
a process given by `pid`.
|
||||
|
||||
Use `free_process_info` to free the memory allocated by this procedure. In
|
||||
case the function returns an error all temporary allocations would be freed
|
||||
and as such, calling `free_process_info()` is not needed.
|
||||
case the function returns an error it may only have been an error for one part
|
||||
of the information and you would still need to call it to free the other parts.
|
||||
|
||||
**Note**: The resulting information may or may contain the fields specified
|
||||
by the `selection` parameter. Always check whether the returned
|
||||
@@ -187,8 +189,8 @@ process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator:
|
||||
the `process` parameter.
|
||||
|
||||
Use `free_process_info` to free the memory allocated by this procedure. In
|
||||
case the function returns an error, all temporary allocations would be freed
|
||||
and as such, calling `free_process_info` is not needed.
|
||||
case the function returns an error it may only have been an error for one part
|
||||
of the information and you would still need to call it to free the other parts.
|
||||
|
||||
**Note**: The resulting information may or may contain the fields specified
|
||||
by the `selection` parameter. Always check whether the returned
|
||||
@@ -206,9 +208,9 @@ process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields,
|
||||
This procedure obtains the information, specified by `selection` parameter
|
||||
about the currently running process.
|
||||
|
||||
Use `free_process_info` to free the memory allocated by this function. In
|
||||
case this function returns an error, all temporary allocations would be
|
||||
freed and as such calling `free_process_info()` is not needed.
|
||||
Use `free_process_info` to free the memory allocated by this procedure. In
|
||||
case the function returns an error it may only have been an error for one part
|
||||
of the information and you would still need to call it to free the other parts.
|
||||
|
||||
**Note**: The resulting information may or may contain the fields specified
|
||||
by the `selection` parameter. Always check whether the returned
|
||||
@@ -239,12 +241,16 @@ process_info :: proc {
|
||||
free_process_info :: proc(pi: Process_Info, allocator: runtime.Allocator) {
|
||||
delete(pi.executable_path, allocator)
|
||||
delete(pi.command_line, allocator)
|
||||
for a in pi.command_args {
|
||||
delete(a, allocator)
|
||||
}
|
||||
delete(pi.command_args, allocator)
|
||||
for s in pi.environment {
|
||||
delete(s, allocator)
|
||||
}
|
||||
delete(pi.environment, allocator)
|
||||
delete(pi.working_dir, allocator)
|
||||
delete(pi.username, allocator)
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
69
core/os/os2/process_posix.odin
Normal file
69
core/os/os2/process_posix.odin
Normal file
@@ -0,0 +1,69 @@
|
||||
//+private
|
||||
//+build darwin, netbsd, freebsd, openbsd
|
||||
package os2
|
||||
|
||||
import "base:runtime"
|
||||
import "core:time"
|
||||
|
||||
import "core:sys/posix"
|
||||
|
||||
_exit :: proc "contextless" (code: int) -> ! {
|
||||
posix.exit(i32(code))
|
||||
}
|
||||
|
||||
_get_uid :: proc() -> int {
|
||||
return int(posix.getuid())
|
||||
}
|
||||
|
||||
_get_euid :: proc() -> int {
|
||||
return int(posix.geteuid())
|
||||
}
|
||||
|
||||
_get_gid :: proc() -> int {
|
||||
return int(posix.getgid())
|
||||
}
|
||||
|
||||
_get_egid :: proc() -> int {
|
||||
return int(posix.getegid())
|
||||
}
|
||||
|
||||
_get_pid :: proc() -> int {
|
||||
return int(posix.getpid())
|
||||
}
|
||||
|
||||
_get_ppid :: proc() -> int {
|
||||
return int(posix.getppid())
|
||||
}
|
||||
|
||||
_process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
|
||||
return _process_info_by_pid(process.pid, selection, allocator)
|
||||
}
|
||||
|
||||
_current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
|
||||
return _process_info_by_pid(_get_pid(), selection, allocator)
|
||||
}
|
||||
|
||||
_process_open :: proc(pid: int, flags: Process_Open_Flags) -> (process: Process, err: Error) {
|
||||
err = .Unsupported
|
||||
return
|
||||
}
|
||||
|
||||
_Sys_Process_Attributes :: struct {}
|
||||
|
||||
_process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
err = .Unsupported
|
||||
return
|
||||
}
|
||||
|
||||
_process_wait :: proc(process: Process, timeout: time.Duration) -> (process_state: Process_State, err: Error) {
|
||||
err = .Unsupported
|
||||
return
|
||||
}
|
||||
|
||||
_process_close :: proc(process: Process) -> Error {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_process_kill :: proc(process: Process) -> Error {
|
||||
return .Unsupported
|
||||
}
|
||||
251
core/os/os2/process_posix_darwin.odin
Normal file
251
core/os/os2/process_posix_darwin.odin
Normal file
@@ -0,0 +1,251 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import "base:runtime"
|
||||
import "base:intrinsics"
|
||||
|
||||
import "core:bytes"
|
||||
import "core:sys/darwin"
|
||||
import "core:sys/posix"
|
||||
import "core:sys/unix"
|
||||
|
||||
foreign import lib "system:System.framework"
|
||||
|
||||
foreign lib {
|
||||
sysctl :: proc(
|
||||
name: [^]i32, namelen: u32,
|
||||
oldp: rawptr, oldlenp: ^uint,
|
||||
newp: rawptr, newlen: uint,
|
||||
) -> posix.result ---
|
||||
}
|
||||
|
||||
_process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
|
||||
info.pid = pid
|
||||
|
||||
get_pidinfo :: proc(pid: int, selection: Process_Info_Fields) -> (ppid: u32, nice: Maybe(i32), uid: posix.uid_t, ok: bool) {
|
||||
// Short info is enough and requires less permissions if the priority isn't requested.
|
||||
if .Priority in selection {
|
||||
pinfo: darwin.proc_bsdinfo
|
||||
ret := darwin.proc_pidinfo(posix.pid_t(pid), .BSDINFO, 0, &pinfo, size_of(pinfo))
|
||||
if ret > 0 {
|
||||
assert(ret == size_of(pinfo))
|
||||
ppid = pinfo.pbi_ppid
|
||||
nice = pinfo.pbi_nice
|
||||
uid = pinfo.pbi_uid
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Try short info, requires less permissions, but doesn't give a `nice`.
|
||||
psinfo: darwin.proc_bsdshortinfo
|
||||
ret := darwin.proc_pidinfo(posix.pid_t(pid), .SHORTBSDINFO, 0, &psinfo, size_of(psinfo))
|
||||
if ret > 0 {
|
||||
assert(ret == size_of(psinfo))
|
||||
ppid = psinfo.pbsi_ppid
|
||||
uid = psinfo.pbsi_uid
|
||||
ok = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Thought on errors is: allocation failures return immediately (also why the non-allocation stuff is done first),
|
||||
// other errors usually mean other parts of the info could be retrieved though, so in those cases we keep trying to get the other information.
|
||||
|
||||
pidinfo: {
|
||||
if selection >= {.PPid, .Priority, .Username } {
|
||||
ppid, mnice, uid, ok := get_pidinfo(pid, selection)
|
||||
if !ok {
|
||||
if err == nil {
|
||||
err = _get_platform_error()
|
||||
}
|
||||
break pidinfo
|
||||
}
|
||||
|
||||
if .PPid in selection {
|
||||
info.ppid = int(ppid)
|
||||
info.fields += {.PPid}
|
||||
}
|
||||
|
||||
if nice, has_nice := mnice.?; has_nice && .Priority in selection {
|
||||
info.priority = int(nice)
|
||||
info.fields += {.Priority}
|
||||
}
|
||||
|
||||
if .Username in selection {
|
||||
pw := posix.getpwuid(uid)
|
||||
if pw == nil {
|
||||
if err == nil {
|
||||
err = _get_platform_error()
|
||||
}
|
||||
break pidinfo
|
||||
}
|
||||
|
||||
info.username = clone_string(string(pw.pw_name), allocator) or_return
|
||||
info.fields += {.Username}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if .Working_Dir in selection {
|
||||
pinfo: darwin.proc_vnodepathinfo
|
||||
ret := darwin.proc_pidinfo(posix.pid_t(pid), .VNODEPATHINFO, 0, &pinfo, size_of(pinfo))
|
||||
if ret > 0 {
|
||||
assert(ret == size_of(pinfo))
|
||||
info.working_dir = clone_string(string(cstring(raw_data(pinfo.pvi_cdir.vip_path[:]))), allocator) or_return
|
||||
info.fields += {.Working_Dir}
|
||||
} else if err == nil {
|
||||
err = _get_platform_error()
|
||||
}
|
||||
}
|
||||
|
||||
if .Executable_Path in selection {
|
||||
buffer: [darwin.PIDPATHINFO_MAXSIZE]byte = ---
|
||||
ret := darwin.proc_pidpath(posix.pid_t(pid), raw_data(buffer[:]), len(buffer))
|
||||
if ret > 0 {
|
||||
info.executable_path = clone_string(string(buffer[:ret]), allocator) or_return
|
||||
info.fields += {.Executable_Path}
|
||||
} else if err == nil {
|
||||
err = _get_platform_error()
|
||||
}
|
||||
}
|
||||
|
||||
args: if selection >= { .Command_Line, .Command_Args, .Environment } {
|
||||
mib := []i32{
|
||||
unix.CTL_KERN,
|
||||
unix.KERN_PROCARGS2,
|
||||
i32(pid),
|
||||
}
|
||||
length: uint
|
||||
if sysctl(raw_data(mib), 3, nil, &length, nil, 0) != .OK {
|
||||
if err == nil {
|
||||
err = _get_platform_error()
|
||||
}
|
||||
break args
|
||||
}
|
||||
|
||||
buf := make([]byte, length, temp_allocator())
|
||||
if sysctl(raw_data(mib), 3, raw_data(buf), &length, nil, 0) != .OK {
|
||||
if err == nil {
|
||||
err = _get_platform_error()
|
||||
}
|
||||
break args
|
||||
}
|
||||
|
||||
buf = buf[:length]
|
||||
|
||||
if len(buf) < 4 {
|
||||
break args
|
||||
}
|
||||
|
||||
// Layout isn't really documented anywhere, I deduced it to be:
|
||||
// i32 - argc
|
||||
// cstring - command name (skipped)
|
||||
// [^]byte - couple of 0 bytes (skipped)
|
||||
// [^]cstring - argv (up to argc entries)
|
||||
// [^]cstring - key=value env entries until the end (many intermittent 0 bytes and entries without `=` we skip here too)
|
||||
|
||||
argc := (^i32)(raw_data(buf))^
|
||||
buf = buf[size_of(i32):]
|
||||
|
||||
{
|
||||
command_line: [dynamic]byte
|
||||
command_line.allocator = allocator
|
||||
|
||||
argv: [dynamic]string
|
||||
argv.allocator = allocator
|
||||
|
||||
defer if err != nil {
|
||||
for arg in argv { delete(arg, allocator) }
|
||||
delete(argv)
|
||||
delete(command_line)
|
||||
}
|
||||
|
||||
_, _ = bytes.split_iterator(&buf, {0})
|
||||
buf = bytes.trim_left(buf, {0})
|
||||
|
||||
first_arg := true
|
||||
for arg in bytes.split_iterator(&buf, {0}) {
|
||||
if .Command_Line in selection {
|
||||
if !first_arg {
|
||||
append(&command_line, ' ') or_return
|
||||
}
|
||||
append(&command_line, ..arg) or_return
|
||||
}
|
||||
|
||||
if .Command_Args in selection {
|
||||
sarg := clone_string(string(arg), allocator) or_return
|
||||
append(&argv, sarg) or_return
|
||||
}
|
||||
|
||||
first_arg = false
|
||||
argc -= 1
|
||||
if argc == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if .Command_Line in selection {
|
||||
info.command_line = string(command_line[:])
|
||||
info.fields += {.Command_Line}
|
||||
}
|
||||
if .Command_Args in selection {
|
||||
info.command_args = argv[:]
|
||||
info.fields += {.Command_Args}
|
||||
}
|
||||
}
|
||||
|
||||
if .Environment in selection {
|
||||
environment: [dynamic]string
|
||||
environment.allocator = allocator
|
||||
|
||||
defer if err != nil {
|
||||
for entry in environment { delete(entry, allocator) }
|
||||
delete(environment)
|
||||
}
|
||||
|
||||
for entry in bytes.split_iterator(&buf, {0}) {
|
||||
if bytes.index_byte(entry, '=') > -1 {
|
||||
sentry := clone_string(string(entry), allocator) or_return
|
||||
append(&environment, sentry) or_return
|
||||
}
|
||||
}
|
||||
|
||||
info.environment = environment[:]
|
||||
info.fields += {.Environment}
|
||||
}
|
||||
}
|
||||
|
||||
// Fields were requested that we didn't add.
|
||||
if err == nil && selection - info.fields != {} {
|
||||
err = .Unsupported
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
_process_list :: proc(allocator: runtime.Allocator) -> (list: []int, err: Error) {
|
||||
ret := darwin.proc_listallpids(nil, 0)
|
||||
if ret < 0 {
|
||||
err = _get_platform_error()
|
||||
return
|
||||
}
|
||||
|
||||
assert(!is_temp(allocator))
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
|
||||
buffer := make([]i32, ret, temp_allocator())
|
||||
ret = darwin.proc_listallpids(raw_data(buffer), ret*size_of(i32))
|
||||
if ret < 0 {
|
||||
err = _get_platform_error()
|
||||
return
|
||||
}
|
||||
|
||||
list = make([]int, ret, allocator) or_return
|
||||
#no_bounds_check for &entry, i in list {
|
||||
entry = int(buffer[i])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
15
core/os/os2/process_posix_other.odin
Normal file
15
core/os/os2/process_posix_other.odin
Normal file
@@ -0,0 +1,15 @@
|
||||
//+private
|
||||
//+build netbsd, openbsd, freebsd
|
||||
package os2
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
_process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
|
||||
err = .Unsupported
|
||||
return
|
||||
}
|
||||
|
||||
_process_list :: proc(allocator: runtime.Allocator) -> (list: []int, err: Error) {
|
||||
err = .Unsupported
|
||||
return
|
||||
}
|
||||
@@ -30,8 +30,8 @@ file_info_clone :: proc(fi: File_Info, allocator: runtime.Allocator) -> (cloned:
|
||||
}
|
||||
|
||||
file_info_slice_delete :: proc(infos: []File_Info, allocator: runtime.Allocator) {
|
||||
for i := len(infos)-1; i >= 0; i -= 1 {
|
||||
file_info_delete(infos[i], allocator)
|
||||
#reverse for info in infos {
|
||||
file_info_delete(info, allocator)
|
||||
}
|
||||
delete(infos, allocator)
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ _fstat_internal :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (fi: File
|
||||
|
||||
// NOTE: _stat and _lstat are using _fstat to avoid a race condition when populating fullpath
|
||||
_stat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
|
||||
assert(!is_temp(allocator))
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
name_cstr := temp_cstring(name) or_return
|
||||
|
||||
@@ -60,6 +61,7 @@ _stat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err
|
||||
}
|
||||
|
||||
_lstat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
|
||||
assert(!is_temp(allocator))
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
name_cstr := temp_cstring(name) or_return
|
||||
|
||||
|
||||
113
core/os/os2/stat_posix.odin
Normal file
113
core/os/os2/stat_posix.odin
Normal file
@@ -0,0 +1,113 @@
|
||||
//+private
|
||||
//+build darwin, netbsd, freebsd, openbsd
|
||||
package os2
|
||||
|
||||
import "base:runtime"
|
||||
import "core:path/filepath"
|
||||
import "core:sys/posix"
|
||||
import "core:time"
|
||||
|
||||
internal_stat :: proc(stat: posix.stat_t, fullpath: string) -> (fi: File_Info) {
|
||||
fi.fullpath = fullpath
|
||||
fi.name = filepath.base(fi.fullpath)
|
||||
|
||||
fi.inode = u64(stat.st_ino)
|
||||
fi.size = i64(stat.st_size)
|
||||
|
||||
fi.mode = int(transmute(posix._mode_t)(stat.st_mode - posix._S_IFMT))
|
||||
|
||||
fi.type = .Undetermined
|
||||
switch {
|
||||
case posix.S_ISBLK(stat.st_mode):
|
||||
fi.type = .Block_Device
|
||||
case posix.S_ISCHR(stat.st_mode):
|
||||
fi.type = .Character_Device
|
||||
case posix.S_ISDIR(stat.st_mode):
|
||||
fi.type = .Directory
|
||||
case posix.S_ISFIFO(stat.st_mode):
|
||||
fi.type = .Named_Pipe
|
||||
case posix.S_ISLNK(stat.st_mode):
|
||||
fi.type = .Symlink
|
||||
case posix.S_ISREG(stat.st_mode):
|
||||
fi.type = .Regular
|
||||
case posix.S_ISSOCK(stat.st_mode):
|
||||
fi.type = .Socket
|
||||
}
|
||||
|
||||
fi.creation_time = timespec_time(stat.st_birthtimespec)
|
||||
fi.modification_time = timespec_time(stat.st_mtim)
|
||||
fi.access_time = timespec_time(stat.st_atim)
|
||||
|
||||
timespec_time :: proc(t: posix.timespec) -> time.Time {
|
||||
return time.Time{_nsec = i64(t.tv_sec) * 1e9 + i64(t.tv_nsec)}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
|
||||
if f == nil || f.impl == nil {
|
||||
err = .Invalid_File
|
||||
return
|
||||
}
|
||||
|
||||
impl := (^File_Impl)(f.impl)
|
||||
|
||||
stat: posix.stat_t
|
||||
if posix.fstat(impl.fd, &stat) != .OK {
|
||||
err = _get_platform_error()
|
||||
return
|
||||
}
|
||||
|
||||
fullpath := clone_string(impl.name, allocator) or_return
|
||||
return internal_stat(stat, fullpath), nil
|
||||
}
|
||||
|
||||
// NOTE: _stat and _lstat are using _fstat to avoid a race condition when populating fullpath
|
||||
_stat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
|
||||
if name == "" {
|
||||
err = .Invalid_Path
|
||||
return
|
||||
}
|
||||
|
||||
assert(!is_temp(allocator))
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
cname := temp_cstring(name) or_return
|
||||
|
||||
rcname := posix.realpath(cname)
|
||||
|
||||
stat: posix.stat_t
|
||||
if posix.stat(rcname, &stat) != .OK {
|
||||
err = _get_platform_error()
|
||||
return
|
||||
}
|
||||
|
||||
fullpath := clone_string(string(rcname), allocator) or_return
|
||||
return internal_stat(stat, fullpath), nil
|
||||
}
|
||||
|
||||
_lstat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
|
||||
if name == "" {
|
||||
err = .Invalid_Path
|
||||
return
|
||||
}
|
||||
|
||||
assert(!is_temp(allocator))
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
cname := temp_cstring(name) or_return
|
||||
|
||||
rcname := posix.realpath(cname)
|
||||
|
||||
stat: posix.stat_t
|
||||
if posix.lstat(rcname, &stat) != .OK {
|
||||
err = _get_platform_error()
|
||||
return
|
||||
}
|
||||
|
||||
fullpath := clone_string(string(rcname), allocator) or_return
|
||||
return internal_stat(stat, fullpath), nil
|
||||
}
|
||||
|
||||
_same_file :: proc(fi1, fi2: File_Info) -> bool {
|
||||
return fi1.fullpath == fi2.fullpath
|
||||
}
|
||||
@@ -44,6 +44,8 @@ full_path_from_name :: proc(name: string, allocator: runtime.Allocator) -> (path
|
||||
if name == "" {
|
||||
name = "."
|
||||
}
|
||||
|
||||
assert(!is_temp(allocator))
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
|
||||
p := win32_utf8_to_utf16(name, temp_allocator()) or_return
|
||||
@@ -127,7 +129,10 @@ _cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (strin
|
||||
if n == 0 {
|
||||
return "", _get_platform_error()
|
||||
}
|
||||
|
||||
assert(!is_temp(allocator))
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
|
||||
buf := make([]u16, max(n, 260)+1, temp_allocator())
|
||||
n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0)
|
||||
return _cleanpath_from_buf(buf[:n], allocator)
|
||||
@@ -143,7 +148,10 @@ _cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) {
|
||||
if n == 0 {
|
||||
return nil, _get_platform_error()
|
||||
}
|
||||
|
||||
assert(!is_temp(allocator))
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
|
||||
buf := make([]u16, max(n, 260)+1, temp_allocator())
|
||||
n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0)
|
||||
return _cleanpath_strip_prefix(buf[:n]), nil
|
||||
|
||||
@@ -10,7 +10,7 @@ MAX_ATTEMPTS :: 1<<13 // Should be enough for everyone, right?
|
||||
// Opens the file for reading and writing, with 0o666 permissions, and returns the new `^File`.
|
||||
// The filename is generated by taking a pattern, and adding a randomized string to the end.
|
||||
// If the pattern includes an "*", the random string replaces the last "*".
|
||||
// If `dir` is an empty tring, `temp_directory()` will be used.
|
||||
// If `dir` is an empty string, `temp_directory()` will be used.
|
||||
//
|
||||
// The caller must `close` the file once finished with.
|
||||
@(require_results)
|
||||
|
||||
20
core/os/os2/temp_file_posix.odin
Normal file
20
core/os/os2/temp_file_posix.odin
Normal file
@@ -0,0 +1,20 @@
|
||||
//+private
|
||||
//+build darwin, netbsd, freebsd, openbsd
|
||||
package os2
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
@(require)
|
||||
import "core:sys/posix"
|
||||
|
||||
_temp_dir :: proc(allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) {
|
||||
if tmp, ok := _lookup_env("TMPDIR", allocator); ok {
|
||||
return tmp, nil
|
||||
}
|
||||
|
||||
when #defined(posix.P_tmpdir) {
|
||||
return clone_string(posix.P_tmpdir, allocator)
|
||||
}
|
||||
|
||||
return clone_string("/tmp/", allocator)
|
||||
}
|
||||
142
core/sys/darwin/proc.odin
Normal file
142
core/sys/darwin/proc.odin
Normal file
@@ -0,0 +1,142 @@
|
||||
package darwin
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
import "core:sys/posix"
|
||||
|
||||
foreign import lib "system:System.framework"
|
||||
|
||||
// Incomplete bindings to the proc API on MacOS, add to when needed.
|
||||
|
||||
foreign lib {
|
||||
proc_pidinfo :: proc(pid: posix.pid_t, flavor: PID_Info_Flavor, arg: i64, buffer: rawptr, buffersize: i32) -> i32 ---
|
||||
proc_pidpath :: proc(pid: posix.pid_t, buffer: [^]byte, buffersize: u32) -> i32 ---
|
||||
proc_listallpids :: proc(buffer: [^]i32, buffersize: i32) -> i32 ---
|
||||
}
|
||||
|
||||
MAXCOMLEN :: 16
|
||||
|
||||
proc_bsdinfo :: struct {
|
||||
pbi_flags: PBI_Flags,
|
||||
pbi_status: u32,
|
||||
pbi_xstatus: u32,
|
||||
pbi_pid: u32,
|
||||
pbi_ppid: u32,
|
||||
pbi_uid: posix.uid_t,
|
||||
pbi_gid: posix.gid_t,
|
||||
pbi_ruid: posix.uid_t,
|
||||
pbi_rgid: posix.gid_t,
|
||||
pbi_svuid: posix.uid_t,
|
||||
pbi_svgid: posix.gid_t,
|
||||
rfu_1: u32,
|
||||
pbi_comm: [MAXCOMLEN]byte `fmt:"s,0"`,
|
||||
pbi_name: [2 * MAXCOMLEN]byte `fmt:"s,0"`,
|
||||
pbi_nfiles: u32,
|
||||
pbi_pgid: u32,
|
||||
pbi_pjobc: u32,
|
||||
e_tdev: u32,
|
||||
e_tpgid: u32,
|
||||
pbi_nice: i32,
|
||||
pbi_start_tvsec: u64,
|
||||
pbi_start_tvusec: u64,
|
||||
}
|
||||
|
||||
proc_bsdshortinfo :: struct {
|
||||
pbsi_pid: u32,
|
||||
pbsi_ppid: u32,
|
||||
pbsi_pgid: u32,
|
||||
pbsi_status: u32,
|
||||
pbsi_comm: [MAXCOMLEN]byte `fmt:"s,0"`,
|
||||
pbsi_flags: PBI_Flags,
|
||||
pbsi_uid: posix.uid_t,
|
||||
pbsi_gid: posix.gid_t,
|
||||
pbsi_ruid: posix.uid_t,
|
||||
pbsi_rgid: posix.gid_t,
|
||||
pbsi_svuid: posix.uid_t,
|
||||
pbsi_svgid: posix.gid_t,
|
||||
pbsi_rfu: u32,
|
||||
}
|
||||
|
||||
proc_vnodepathinfo :: struct {
|
||||
pvi_cdir: vnode_info_path,
|
||||
pvi_rdir: vnode_info_path,
|
||||
}
|
||||
|
||||
vnode_info_path :: struct {
|
||||
vip_vi: vnode_info,
|
||||
vip_path: [posix.PATH_MAX]byte,
|
||||
}
|
||||
|
||||
vnode_info :: struct {
|
||||
vi_stat: vinfo_stat,
|
||||
vi_type: i32,
|
||||
vi_pad: i32,
|
||||
vi_fsid: fsid_t,
|
||||
}
|
||||
|
||||
vinfo_stat :: struct {
|
||||
vst_dev: u32,
|
||||
vst_mode: u16,
|
||||
vst_nlink: u16,
|
||||
vst_ino: u64,
|
||||
vst_uid: posix.uid_t,
|
||||
vst_gid: posix.gid_t,
|
||||
vst_atime: i64,
|
||||
vst_atimensec: i64,
|
||||
vst_mtime: i64,
|
||||
vst_mtimensec: i64,
|
||||
vst_ctime: i64,
|
||||
vst_ctimensec: i64,
|
||||
vst_birthtime: i64,
|
||||
vst_birthtimensec: i64,
|
||||
vst_size: posix.off_t,
|
||||
vst_blocks: i64,
|
||||
vst_blksize: i32,
|
||||
vst_flags: u32,
|
||||
vst_gen: u32,
|
||||
vst_rdev: u32,
|
||||
vst_qspare: [2]i64,
|
||||
}
|
||||
|
||||
fsid_t :: distinct [2]i32
|
||||
|
||||
PBI_Flag_Bits :: enum u32 {
|
||||
SYSTEM = intrinsics.constant_log2(0x0001),
|
||||
TRACED = intrinsics.constant_log2(0x0002),
|
||||
INEXIT = intrinsics.constant_log2(0x0004),
|
||||
PWAIT = intrinsics.constant_log2(0x0008),
|
||||
LP64 = intrinsics.constant_log2(0x0010),
|
||||
SLEADER = intrinsics.constant_log2(0x0020),
|
||||
CTTY = intrinsics.constant_log2(0x0040),
|
||||
CONTROLT = intrinsics.constant_log2(0x0080),
|
||||
THCWD = intrinsics.constant_log2(0x0100),
|
||||
PC_THROTTLE = intrinsics.constant_log2(0x0200),
|
||||
PC_SUSP = intrinsics.constant_log2(0x0400),
|
||||
PC_KILL = intrinsics.constant_log2(0x0600),
|
||||
PA_THROTTLE = intrinsics.constant_log2(0x0800),
|
||||
PA_SUSP = intrinsics.constant_log2(0x1000),
|
||||
PA_PSUGID = intrinsics.constant_log2(0x2000),
|
||||
EXEC = intrinsics.constant_log2(0x4000),
|
||||
}
|
||||
PBI_Flags :: bit_set[PBI_Flag_Bits; u32]
|
||||
|
||||
PID_Info_Flavor :: enum i32 {
|
||||
LISTFDS = 1,
|
||||
TASKALLINFO,
|
||||
BSDINFO,
|
||||
TASKINFO,
|
||||
THREADINFO,
|
||||
LISTTHREADS,
|
||||
REGIONINFO,
|
||||
REGIONPATHINFO,
|
||||
VNODEPATHINFO,
|
||||
THREADPATHINFO,
|
||||
PATHINFO,
|
||||
WORKQUEUEINFO,
|
||||
SHORTBSDINFO,
|
||||
LISTFILEPORTS,
|
||||
THREADID64INFO,
|
||||
RUSAGE,
|
||||
}
|
||||
|
||||
PIDPATHINFO_MAXSIZE :: 4*posix.PATH_MAX
|
||||
@@ -211,7 +211,7 @@ foreign lib {
|
||||
|
||||
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/_exit.html ]]
|
||||
*/
|
||||
_exit :: proc(status: c.int) ---
|
||||
_exit :: proc(status: c.int) -> ! ---
|
||||
|
||||
/*
|
||||
The exec family of functions shall replace the current process image with a new process image.
|
||||
@@ -416,8 +416,11 @@ foreign lib {
|
||||
}
|
||||
|
||||
cwd = posix.getcwd(raw_data(buf), len(buf))
|
||||
if errno := posix.errno(); cwd == nil && errno != .ERANGE {
|
||||
fmt.panicf("getcwd failure: %v", posix.strerror(errno))
|
||||
if cwd == nil {
|
||||
errno := posix.errno()
|
||||
if errno != .ERANGE {
|
||||
fmt.panicf("getcwd failure: %v", posix.strerror(errno))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,8 @@ CTL_KERN :: 1
|
||||
KERN_VERSION :: 4 // Darwin Kernel Version 21.5.0: Tue Apr 26 21:08:22 PDT 2022; root:darwin-8020.121.3~4/RELEASE_X86_64
|
||||
KERN_OSRELDATE :: 26 // i32: OS release date
|
||||
KERN_OSVERSION :: 65 // Build number, e.g. 21F79
|
||||
KERN_PROCARGS :: 38
|
||||
KERN_PROCARGS2 :: 49
|
||||
CTL_VM :: 2
|
||||
CTL_VFS :: 3
|
||||
CTL_NET :: 4
|
||||
@@ -82,4 +84,4 @@ CTL_HW :: 6
|
||||
HW_AVAILCPU :: 25 /* int: number of available CPUs */
|
||||
|
||||
CTL_MACHDEP :: 7
|
||||
CTL_USER :: 8
|
||||
CTL_USER :: 8
|
||||
|
||||
@@ -23,6 +23,8 @@ CTL_KERN :: 1
|
||||
KERN_OSRELEASE :: 2
|
||||
KERN_OSREV :: 3
|
||||
KERN_VERSION :: 4
|
||||
KERN_PROC :: 14
|
||||
KERN_PROC_PATHNAME :: 12
|
||||
CTL_VM :: 2
|
||||
CTL_VFS :: 3
|
||||
CTL_NET :: 4
|
||||
|
||||
Reference in New Issue
Block a user