mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-31 20:00:09 +00:00
update to master
This commit is contained in:
@@ -62,8 +62,8 @@ TEMP_ALLOCATOR_GUARD_END :: proc(temp: runtime.Arena_Temp, loc := #caller_locati
|
||||
|
||||
@(deferred_out=TEMP_ALLOCATOR_GUARD_END)
|
||||
TEMP_ALLOCATOR_GUARD :: #force_inline proc(loc := #caller_location) -> (runtime.Arena_Temp, runtime.Source_Code_Location) {
|
||||
tmp := temp_allocator_temp_begin(loc)
|
||||
global_default_temp_allocator_index = (global_default_temp_allocator_index+1)%MAX_TEMP_ARENA_COUNT
|
||||
tmp := temp_allocator_temp_begin(loc)
|
||||
return tmp, loc
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
|
||||
|
||||
@(require_results)
|
||||
_read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) {
|
||||
return {}, nil
|
||||
return {}, .Unsupported
|
||||
}
|
||||
|
||||
_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
|
||||
|
||||
@@ -16,28 +16,25 @@ find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW, al
|
||||
}
|
||||
path := concatenate({base_path, `\`, win32_utf16_to_utf8(d.cFileName[:], temp_allocator()) or_else ""}, allocator) or_return
|
||||
|
||||
handle := win32.HANDLE(_open_internal(path, {.Read}, 0o666) or_else 0)
|
||||
defer win32.CloseHandle(handle)
|
||||
|
||||
fi.fullpath = path
|
||||
fi.name = basename(path)
|
||||
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
|
||||
|
||||
fi.type, fi.mode = _file_type_mode_from_file_attributes(d.dwFileAttributes, nil, d.dwReserved0)
|
||||
fi.type, fi.mode = _file_type_mode_from_file_attributes(d.dwFileAttributes, handle, d.dwReserved0)
|
||||
|
||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
||||
|
||||
|
||||
handle := win32.HANDLE(_open_internal(path, {.Read}, 0o666) or_else 0)
|
||||
defer win32.CloseHandle(handle)
|
||||
|
||||
if file_id_info: win32.FILE_ID_INFO; handle != nil && win32.GetFileInformationByHandleEx(handle, .FileIdInfo, &file_id_info, size_of(file_id_info)) {
|
||||
#assert(size_of(fi.inode) == size_of(file_id_info.FileId))
|
||||
#assert(size_of(fi.inode) == 16)
|
||||
runtime.mem_copy_non_overlapping(&fi.inode, &file_id_info.FileId, 16)
|
||||
}
|
||||
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -137,5 +134,6 @@ _read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
|
||||
return
|
||||
}
|
||||
file_info_delete(it.impl.prev_fi, file_allocator())
|
||||
delete(it.impl.path, file_allocator())
|
||||
win32.FindClose(it.impl.find_handle)
|
||||
}
|
||||
|
||||
@@ -162,6 +162,8 @@ _get_platform_error :: proc(errno: linux.Errno) -> Error {
|
||||
return .Invalid_File
|
||||
case .ENOMEM:
|
||||
return .Out_Of_Memory
|
||||
case .ENOSYS:
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
return Platform_Error(i32(errno))
|
||||
|
||||
@@ -26,6 +26,8 @@ _get_platform_error :: proc() -> Error {
|
||||
return .Invalid_File
|
||||
case .ENOMEM:
|
||||
return .Out_Of_Memory
|
||||
case .ENOSYS:
|
||||
return .Unsupported
|
||||
case:
|
||||
return Platform_Error(errno)
|
||||
}
|
||||
|
||||
@@ -55,13 +55,15 @@ _get_platform_error :: proc() -> Error {
|
||||
case win32.ERROR_NEGATIVE_SEEK:
|
||||
return .Invalid_Offset
|
||||
|
||||
case win32.ERROR_BROKEN_PIPE:
|
||||
return .Broken_Pipe
|
||||
|
||||
case
|
||||
win32.ERROR_BAD_ARGUMENTS,
|
||||
win32.ERROR_INVALID_PARAMETER,
|
||||
win32.ERROR_NOT_ENOUGH_MEMORY,
|
||||
win32.ERROR_NO_MORE_FILES,
|
||||
win32.ERROR_LOCK_VIOLATION,
|
||||
win32.ERROR_BROKEN_PIPE,
|
||||
win32.ERROR_CALL_NOT_IMPLEMENTED,
|
||||
win32.ERROR_INSUFFICIENT_BUFFER,
|
||||
win32.ERROR_INVALID_NAME,
|
||||
|
||||
@@ -164,7 +164,7 @@ read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator) -> (d
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
write_entire_file :: proc(name: string, data: []byte, perm: int, truncate := true) -> Error {
|
||||
write_entire_file :: proc(name: string, data: []byte, perm: int = 0o644, truncate := true) -> Error {
|
||||
flags := O_WRONLY|O_CREATE
|
||||
if truncate {
|
||||
flags |= O_TRUNC
|
||||
|
||||
@@ -50,9 +50,9 @@ init_std_files :: proc() {
|
||||
}
|
||||
@(fini)
|
||||
fini_std_files :: proc() {
|
||||
close(stdin)
|
||||
close(stdout)
|
||||
close(stderr)
|
||||
_destroy((^File_Impl)(stdin.impl))
|
||||
_destroy((^File_Impl)(stdout.impl))
|
||||
_destroy((^File_Impl)(stderr.impl))
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ _mkdir_all :: proc(path: string, perm: int) -> Error {
|
||||
|
||||
internal_mkdir_all :: proc(path: string, perm: int) -> Error {
|
||||
dir, file := filepath.split(path)
|
||||
if file != path {
|
||||
if file != path && dir != "/" {
|
||||
if len(dir) > 1 && dir[len(dir) - 1] == '/' {
|
||||
dir = dir[:len(dir) - 1]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,43 @@
|
||||
package os2
|
||||
|
||||
/*
|
||||
Create an anonymous pipe.
|
||||
|
||||
This procedure creates an anonymous pipe, returning two ends of the pipe, `r`
|
||||
and `w`. The file `r` is the readable end of the pipe. The file `w` is a
|
||||
writeable end of the pipe.
|
||||
|
||||
Pipes are used as an inter-process communication mechanism, to communicate
|
||||
between a parent and a child process. The child uses one end of the pipe to
|
||||
write data, and the parent uses the other end to read from the pipe
|
||||
(or vice-versa). When a parent passes one of the ends of the pipe to the child
|
||||
process, that end of the pipe needs to be closed by the parent, before any data
|
||||
is attempted to be read.
|
||||
|
||||
Although pipes look like files and is compatible with most file APIs in package
|
||||
os2, the way it's meant to be read is different. Due to asynchronous nature of
|
||||
the communication channel, the data may not be present at the time of a read
|
||||
request. The other scenario is when a pipe has no data because the other end
|
||||
of the pipe was closed by the child process.
|
||||
*/
|
||||
@(require_results)
|
||||
pipe :: proc() -> (r, w: ^File, err: Error) {
|
||||
return _pipe()
|
||||
}
|
||||
|
||||
/*
|
||||
Check if the pipe has any data.
|
||||
|
||||
This procedure checks whether a read-end of the pipe has data that can be
|
||||
read, and returns `true`, if the pipe has readable data, and `false` if the
|
||||
pipe is empty. This procedure does not block the execution of the current
|
||||
thread.
|
||||
|
||||
**Note**: If the other end of the pipe was closed by the child process, the
|
||||
`.Broken_Pipe`
|
||||
can be returned by this procedure. Handle these errors accordingly.
|
||||
*/
|
||||
@(require_results)
|
||||
pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) {
|
||||
return _pipe_has_data(r)
|
||||
}
|
||||
|
||||
@@ -15,3 +15,29 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
_pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) {
|
||||
if r == nil || r.impl == nil {
|
||||
return false, nil
|
||||
}
|
||||
fd := linux.Fd((^File_Impl)(r.impl).fd)
|
||||
poll_fds := []linux.Poll_Fd {
|
||||
linux.Poll_Fd {
|
||||
fd = fd,
|
||||
events = {.IN, .HUP},
|
||||
},
|
||||
}
|
||||
n, errno := linux.poll(poll_fds, 0)
|
||||
if n != 1 || errno != nil {
|
||||
return false, _get_platform_error(errno)
|
||||
}
|
||||
pipe_events := poll_fds[0].revents
|
||||
if pipe_events >= {.IN} {
|
||||
return true, nil
|
||||
}
|
||||
if pipe_events >= {.HUP} {
|
||||
return false, .Broken_Pipe
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
@@ -44,3 +44,30 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
_pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) {
|
||||
if r == nil || r.impl == nil {
|
||||
return false, nil
|
||||
}
|
||||
fd := __fd(r)
|
||||
poll_fds := []posix.pollfd {
|
||||
posix.pollfd {
|
||||
fd = fd,
|
||||
events = {.IN, .HUP},
|
||||
},
|
||||
}
|
||||
n := posix.poll(raw_data(poll_fds), u32(len(poll_fds)), 0)
|
||||
if n < 0 {
|
||||
return false, _get_platform_error()
|
||||
} else if n != 1 {
|
||||
return false, nil
|
||||
}
|
||||
pipe_events := poll_fds[0].revents
|
||||
if pipe_events >= {.IN} {
|
||||
return true, nil
|
||||
}
|
||||
if pipe_events >= {.HUP} {
|
||||
return false, .Broken_Pipe
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -15,3 +15,15 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
|
||||
return new_file(uintptr(p[0]), ""), new_file(uintptr(p[1]), ""), nil
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
_pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) {
|
||||
if r == nil || r.impl == nil {
|
||||
return false, nil
|
||||
}
|
||||
handle := win32.HANDLE((^File_Impl)(r.impl).fd)
|
||||
bytes_available: u32
|
||||
if !win32.PeekNamedPipe(handle, nil, 0, nil, &bytes_available, nil) {
|
||||
return false, _get_platform_error()
|
||||
}
|
||||
return bytes_available > 0, nil
|
||||
}
|
||||
+227
-119
@@ -1,16 +1,17 @@
|
||||
package os2
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
import "core:time"
|
||||
|
||||
/*
|
||||
In procedures that explicitly state this as one of the allowed values,
|
||||
specifies an infinite timeout.
|
||||
In procedures that explicitly state this as one of the allowed values,
|
||||
specifies an infinite timeout.
|
||||
*/
|
||||
TIMEOUT_INFINITE :: time.MIN_DURATION // Note(flysand): Any negative duration will be treated as infinity
|
||||
|
||||
/*
|
||||
Arguments to the current process.
|
||||
Arguments to the current process.
|
||||
*/
|
||||
args := get_args()
|
||||
|
||||
@@ -24,17 +25,17 @@ get_args :: proc() -> []string {
|
||||
}
|
||||
|
||||
/*
|
||||
Exit the current process.
|
||||
Exit the current process.
|
||||
*/
|
||||
exit :: proc "contextless" (code: int) -> ! {
|
||||
_exit(code)
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the UID of the current process.
|
||||
Obtain the UID of the current process.
|
||||
|
||||
**Note(windows)**: Windows doesn't follow the posix permissions model, so
|
||||
the function simply returns -1.
|
||||
**Note(windows)**: Windows doesn't follow the posix permissions model, so
|
||||
the function simply returns -1.
|
||||
*/
|
||||
@(require_results)
|
||||
get_uid :: proc() -> int {
|
||||
@@ -42,15 +43,15 @@ get_uid :: proc() -> int {
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the effective UID of the current process.
|
||||
Obtain the effective UID of the current process.
|
||||
|
||||
The effective UID is typically the same as the UID of the process. In case
|
||||
the process was run by a user with elevated permissions, the process may
|
||||
lower the privilege to perform some tasks without privilege. In these cases
|
||||
the real UID of the process and the effective UID are different.
|
||||
|
||||
**Note(windows)**: Windows doesn't follow the posix permissions model, so
|
||||
the function simply returns -1.
|
||||
The effective UID is typically the same as the UID of the process. In case
|
||||
the process was run by a user with elevated permissions, the process may
|
||||
lower the privilege to perform some tasks without privilege. In these cases
|
||||
the real UID of the process and the effective UID are different.
|
||||
|
||||
**Note(windows)**: Windows doesn't follow the posix permissions model, so
|
||||
the function simply returns -1.
|
||||
*/
|
||||
@(require_results)
|
||||
get_euid :: proc() -> int {
|
||||
@@ -58,10 +59,10 @@ get_euid :: proc() -> int {
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the GID of the current process.
|
||||
|
||||
**Note(windows)**: Windows doesn't follow the posix permissions model, so
|
||||
the function simply returns -1.
|
||||
Obtain the GID of the current process.
|
||||
|
||||
**Note(windows)**: Windows doesn't follow the posix permissions model, so
|
||||
the function simply returns -1.
|
||||
*/
|
||||
@(require_results)
|
||||
get_gid :: proc() -> int {
|
||||
@@ -69,15 +70,15 @@ get_gid :: proc() -> int {
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the effective GID of the current process.
|
||||
|
||||
The effective GID is typically the same as the GID of the process. In case
|
||||
the process was run by a user with elevated permissions, the process may
|
||||
lower the privilege to perform some tasks without privilege. In these cases
|
||||
the real GID of the process and the effective GID are different.
|
||||
Obtain the effective GID of the current process.
|
||||
|
||||
**Note(windows)**: Windows doesn't follow the posix permissions model, so
|
||||
the function simply returns -1.
|
||||
The effective GID is typically the same as the GID of the process. In case
|
||||
the process was run by a user with elevated permissions, the process may
|
||||
lower the privilege to perform some tasks without privilege. In these cases
|
||||
the real GID of the process and the effective GID are different.
|
||||
|
||||
**Note(windows)**: Windows doesn't follow the posix permissions model, so
|
||||
the function simply returns -1.
|
||||
*/
|
||||
@(require_results)
|
||||
get_egid :: proc() -> int {
|
||||
@@ -85,7 +86,7 @@ get_egid :: proc() -> int {
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the ID of the current process.
|
||||
Obtain the ID of the current process.
|
||||
*/
|
||||
@(require_results)
|
||||
get_pid :: proc() -> int {
|
||||
@@ -93,13 +94,13 @@ get_pid :: proc() -> int {
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain the ID of the parent process.
|
||||
Obtain the ID of the parent process.
|
||||
|
||||
**Note(windows)**: Windows does not mantain strong relationships between
|
||||
parent and child processes. This function returns the ID of the process
|
||||
that has created the current process. In case the parent has died, the ID
|
||||
returned by this function can identify a non-existent or a different
|
||||
process.
|
||||
**Note(windows)**: Windows does not mantain strong relationships between
|
||||
parent and child processes. This function returns the ID of the process
|
||||
that has created the current process. In case the parent has died, the ID
|
||||
returned by this function can identify a non-existent or a different
|
||||
process.
|
||||
*/
|
||||
@(require_results)
|
||||
get_ppid :: proc() -> int {
|
||||
@@ -107,7 +108,7 @@ get_ppid :: proc() -> int {
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain ID's of all processes running in the system.
|
||||
Obtain ID's of all processes running in the system.
|
||||
*/
|
||||
@(require_results)
|
||||
process_list :: proc(allocator: runtime.Allocator) -> ([]int, Error) {
|
||||
@@ -115,9 +116,9 @@ process_list :: proc(allocator: runtime.Allocator) -> ([]int, Error) {
|
||||
}
|
||||
|
||||
/*
|
||||
Bit set specifying which fields of the `Process_Info` struct need to be
|
||||
obtained by the `process_info()` procedure. Each bit corresponds to a
|
||||
field in the `Process_Info` struct.
|
||||
Bit set specifying which fields of the `Process_Info` struct need to be
|
||||
obtained by the `process_info()` procedure. Each bit corresponds to a
|
||||
field in the `Process_Info` struct.
|
||||
*/
|
||||
Process_Info_Fields :: bit_set[Process_Info_Field]
|
||||
Process_Info_Field :: enum {
|
||||
@@ -134,8 +135,8 @@ Process_Info_Field :: enum {
|
||||
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.
|
||||
Contains information about the process as obtained by the `process_info()`
|
||||
procedure.
|
||||
*/
|
||||
Process_Info :: struct {
|
||||
// The information about a process the struct contains. `pid` is always
|
||||
@@ -162,19 +163,19 @@ Process_Info :: struct {
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain information about a process.
|
||||
Obtain information about a process.
|
||||
|
||||
This procedure obtains an information, specified by `selection` parameter of
|
||||
a process given by `pid`.
|
||||
This procedure obtains an information, specified by `selection` parameter of
|
||||
a process given by `pid`.
|
||||
|
||||
Use `free_process_info` to free the memory allocated by this procedure. The
|
||||
`free_process_info` procedure needs to be called, even if this procedure
|
||||
returned an error, as some of the fields may have been allocated.
|
||||
Use `free_process_info` to free the memory allocated by this procedure. The
|
||||
`free_process_info` procedure needs to be called, even if this procedure
|
||||
returned an error, as some of the fields may have been allocated.
|
||||
|
||||
**Note**: The resulting information may or may contain the fields specified
|
||||
by the `selection` parameter. Always check whether the returned
|
||||
`Process_Info` struct has the required fields before checking the error code
|
||||
returned by this procedure.
|
||||
**Note**: The resulting information may or may contain the fields specified
|
||||
by the `selection` parameter. Always check whether the returned
|
||||
`Process_Info` struct has the required fields before checking the error code
|
||||
returned by this procedure.
|
||||
*/
|
||||
@(require_results)
|
||||
process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
|
||||
@@ -182,20 +183,20 @@ process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator:
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain information about a process.
|
||||
Obtain information about a process.
|
||||
|
||||
This procedure obtains information, specified by `selection` parameter
|
||||
about a process that has been opened by the application, specified in
|
||||
the `process` parameter.
|
||||
This procedure obtains information, specified by `selection` parameter
|
||||
about a process that has been opened by the application, specified in
|
||||
the `process` parameter.
|
||||
|
||||
Use `free_process_info` to free the memory allocated by this procedure. The
|
||||
`free_process_info` procedure needs to be called, even if this procedure
|
||||
returned an error, as some of the fields may have been allocated.
|
||||
Use `free_process_info` to free the memory allocated by this procedure. The
|
||||
`free_process_info` procedure needs to be called, even if this procedure
|
||||
returned an error, as some of the fields may have been allocated.
|
||||
|
||||
**Note**: The resulting information may or may contain the fields specified
|
||||
by the `selection` parameter. Always check whether the returned
|
||||
`Process_Info` struct has the required fields before checking the error code
|
||||
returned by this procedure.
|
||||
**Note**: The resulting information may or may contain the fields specified
|
||||
by the `selection` parameter. Always check whether the returned
|
||||
`Process_Info` struct has the required fields before checking the error code
|
||||
returned by this procedure.
|
||||
*/
|
||||
@(require_results)
|
||||
process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
|
||||
@@ -203,19 +204,19 @@ process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields,
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain information about the current process.
|
||||
Obtain information about the current process.
|
||||
|
||||
This procedure obtains the information, specified by `selection` parameter
|
||||
about the currently running process.
|
||||
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 procedure. The
|
||||
`free_process_info` procedure needs to be called, even if this procedure
|
||||
returned an error, as some of the fields may have been allocated.
|
||||
Use `free_process_info` to free the memory allocated by this procedure. The
|
||||
`free_process_info` procedure needs to be called, even if this procedure
|
||||
returned an error, as some of the fields may have been allocated.
|
||||
|
||||
**Note**: The resulting information may or may contain the fields specified
|
||||
by the `selection` parameter. Always check whether the returned
|
||||
`Process_Info` struct has the required fields before checking the error code
|
||||
returned by this procedure.
|
||||
**Note**: The resulting information may or may contain the fields specified
|
||||
by the `selection` parameter. Always check whether the returned
|
||||
`Process_Info` struct has the required fields before checking the error code
|
||||
returned by this procedure.
|
||||
*/
|
||||
@(require_results)
|
||||
current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
|
||||
@@ -223,7 +224,7 @@ current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.
|
||||
}
|
||||
|
||||
/*
|
||||
Obtain information about the specified process.
|
||||
Obtain information about the specified process.
|
||||
*/
|
||||
process_info :: proc {
|
||||
process_info_by_pid,
|
||||
@@ -232,11 +233,11 @@ process_info :: proc {
|
||||
}
|
||||
|
||||
/*
|
||||
Free the information about the process.
|
||||
Free the information about the process.
|
||||
|
||||
This procedure frees the memory occupied by process info using the provided
|
||||
allocator. The allocator needs to be the same allocator that was supplied
|
||||
to the `process_info` function.
|
||||
This procedure frees the memory occupied by process info using the provided
|
||||
allocator. The allocator needs to be the same allocator that was supplied
|
||||
to the `process_info` function.
|
||||
*/
|
||||
free_process_info :: proc(pi: Process_Info, allocator: runtime.Allocator) {
|
||||
delete(pi.executable_path, allocator)
|
||||
@@ -254,13 +255,13 @@ free_process_info :: proc(pi: Process_Info, allocator: runtime.Allocator) {
|
||||
}
|
||||
|
||||
/*
|
||||
Represents a process handle.
|
||||
Represents a process handle.
|
||||
|
||||
When a process dies, the OS is free to re-use the pid of that process. The
|
||||
`Process` struct represents a handle to the process that will refer to a
|
||||
specific process, even after it has died.
|
||||
When a process dies, the OS is free to re-use the pid of that process. The
|
||||
`Process` struct represents a handle to the process that will refer to a
|
||||
specific process, even after it has died.
|
||||
|
||||
**Note(linux)**: The `handle` will be referring to pidfd.
|
||||
**Note(linux)**: The `handle` will be referring to pidfd.
|
||||
*/
|
||||
Process :: struct {
|
||||
pid: int,
|
||||
@@ -276,13 +277,13 @@ Process_Open_Flag :: enum {
|
||||
}
|
||||
|
||||
/*
|
||||
Open a process handle using it's pid.
|
||||
Open a process handle using it's pid.
|
||||
|
||||
This procedure obtains a process handle of a process specified by `pid`.
|
||||
This procedure can be subject to race conditions. See the description of
|
||||
`Process`.
|
||||
This procedure obtains a process handle of a process specified by `pid`.
|
||||
This procedure can be subject to race conditions. See the description of
|
||||
`Process`.
|
||||
|
||||
Use `process_close()` function to close the process handle.
|
||||
Use `process_close()` function to close the process handle.
|
||||
*/
|
||||
@(require_results)
|
||||
process_open :: proc(pid: int, flags := Process_Open_Flags {}) -> (Process, Error) {
|
||||
@@ -322,31 +323,139 @@ Process_Desc :: struct {
|
||||
}
|
||||
|
||||
/*
|
||||
Create a new process and obtain its handle.
|
||||
Create a new process and obtain its handle.
|
||||
|
||||
This procedure creates a new process, with a given command and environment
|
||||
strings as parameters. Use `environ()` to inherit the environment of the
|
||||
current process.
|
||||
This procedure creates a new process, with a given command and environment
|
||||
strings as parameters. Use `environ()` to inherit the environment of the
|
||||
current process.
|
||||
|
||||
The `desc` parameter specifies the description of how the process should
|
||||
be created. It contains information such as the command line, the
|
||||
environment of the process, the starting directory and many other options.
|
||||
Most of the fields in the struct can be set to `nil` or an empty value.
|
||||
|
||||
Use `process_close` to close the handle to the process. Note, that this
|
||||
is not the same as terminating the process. One can terminate the process
|
||||
and not close the handle, in which case the handle would be leaked. In case
|
||||
the function returns an error, an invalid handle is returned.
|
||||
The `desc` parameter specifies the description of how the process should
|
||||
be created. It contains information such as the command line, the
|
||||
environment of the process, the starting directory and many other options.
|
||||
Most of the fields in the struct can be set to `nil` or an empty value.
|
||||
|
||||
This procedure is not thread-safe. It may alter the inheritance properties
|
||||
of file handles in an unpredictable manner. In case multiple threads change
|
||||
handle inheritance properties, make sure to serialize all those calls.
|
||||
Use `process_close` to close the handle to the process. Note, that this
|
||||
is not the same as terminating the process. One can terminate the process
|
||||
and not close the handle, in which case the handle would be leaked. In case
|
||||
the function returns an error, an invalid handle is returned.
|
||||
|
||||
This procedure is not thread-safe. It may alter the inheritance properties
|
||||
of file handles in an unpredictable manner. In case multiple threads change
|
||||
handle inheritance properties, make sure to serialize all those calls.
|
||||
*/
|
||||
@(require_results)
|
||||
process_start :: proc(desc := Process_Desc {}) -> (Process, Error) {
|
||||
process_start :: proc(desc: Process_Desc) -> (Process, Error) {
|
||||
return _process_start(desc)
|
||||
}
|
||||
|
||||
/*
|
||||
Execute the process and capture stdout and stderr streams.
|
||||
|
||||
This procedure creates a new process, with a given command and environment
|
||||
strings as parameters, and waits until the process finishes execution. While
|
||||
the process is running, this procedure accumulates the output of its stdout
|
||||
and stderr streams and returns byte slices containing the captured data from
|
||||
the streams.
|
||||
|
||||
This procedure expects that `stdout` and `stderr` fields of the `desc` parameter
|
||||
are left at default, i.e. a `nil` value. You can not capture stdout/stderr and
|
||||
redirect it to a file at the same time.
|
||||
|
||||
This procedure does not free `stdout` and `stderr` slices before an error is
|
||||
returned. Make sure to call `delete` on these slices.
|
||||
*/
|
||||
@(require_results)
|
||||
process_exec :: proc(
|
||||
desc: Process_Desc,
|
||||
allocator: runtime.Allocator,
|
||||
loc := #caller_location,
|
||||
) -> (
|
||||
state: Process_State,
|
||||
stdout: []byte,
|
||||
stderr: []byte,
|
||||
err: Error,
|
||||
) {
|
||||
assert(desc.stdout == nil, "Cannot redirect stdout when it's being captured", loc)
|
||||
assert(desc.stderr == nil, "Cannot redirect stderr when it's being captured", loc)
|
||||
|
||||
stdout_r, stdout_w := pipe() or_return
|
||||
defer close(stdout_r)
|
||||
stderr_r, stderr_w := pipe() or_return
|
||||
defer close(stderr_r)
|
||||
|
||||
process: Process
|
||||
{
|
||||
// NOTE(flysand): Make sure the write-ends are closed, regardless
|
||||
// of the outcome. This makes read-ends readable on our side.
|
||||
defer close(stdout_w)
|
||||
defer close(stderr_w)
|
||||
desc := desc
|
||||
desc.stdout = stdout_w
|
||||
desc.stderr = stderr_w
|
||||
process = process_start(desc) or_return
|
||||
}
|
||||
|
||||
{
|
||||
stdout_b: [dynamic]byte
|
||||
stdout_b.allocator = allocator
|
||||
defer stdout = stdout_b[:]
|
||||
|
||||
stderr_b: [dynamic]byte
|
||||
stderr_b.allocator = allocator
|
||||
defer stderr = stderr_b[:]
|
||||
|
||||
buf: [1024]u8 = ---
|
||||
|
||||
stdout_done, stderr_done, has_data: bool
|
||||
for err == nil && (!stdout_done || !stderr_done) {
|
||||
n := 0
|
||||
|
||||
if !stdout_done {
|
||||
has_data, err = pipe_has_data(stdout_r)
|
||||
if has_data {
|
||||
n, err = read(stdout_r, buf[:])
|
||||
}
|
||||
|
||||
switch err {
|
||||
case nil:
|
||||
_, err = append(&stdout_b, ..buf[:n])
|
||||
case .EOF, .Broken_Pipe:
|
||||
stdout_done = true
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil && !stderr_done {
|
||||
n = 0
|
||||
has_data, err = pipe_has_data(stderr_r)
|
||||
if has_data {
|
||||
n, err = read(stderr_r, buf[:])
|
||||
}
|
||||
|
||||
switch err {
|
||||
case nil:
|
||||
_, err = append(&stderr_b, ..buf[:n])
|
||||
case .EOF, .Broken_Pipe:
|
||||
stderr_done = true
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
state, _ = process_wait(process, timeout=0)
|
||||
if !state.exited {
|
||||
_ = process_kill(process)
|
||||
state, _ = process_wait(process)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
state, err = process_wait(process)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
The state of the process after it has finished execution.
|
||||
*/
|
||||
@@ -371,17 +480,17 @@ Process_State :: struct {
|
||||
}
|
||||
|
||||
/*
|
||||
Wait for a process event.
|
||||
Wait for a process event.
|
||||
|
||||
This procedure blocks the execution until the process has exited or the
|
||||
timeout (if specified) has reached zero. If the timeout is `TIMEOUT_INFINITE`,
|
||||
no timeout restriction is imposed and the procedure can block indefinately.
|
||||
This procedure blocks the execution until the process has exited or the
|
||||
timeout (if specified) has reached zero. If the timeout is `TIMEOUT_INFINITE`,
|
||||
no timeout restriction is imposed and the procedure can block indefinately.
|
||||
|
||||
If the timeout has expired, the `General_Error.Timeout` is returned as
|
||||
the error.
|
||||
If the timeout has expired, the `General_Error.Timeout` is returned as
|
||||
the error.
|
||||
|
||||
If an error is returned for any other reason, other than timeout, the
|
||||
process state is considered undetermined.
|
||||
If an error is returned for any other reason, other than timeout, the
|
||||
process state is considered undetermined.
|
||||
*/
|
||||
@(require_results)
|
||||
process_wait :: proc(process: Process, timeout := TIMEOUT_INFINITE) -> (Process_State, Error) {
|
||||
@@ -389,12 +498,12 @@ process_wait :: proc(process: Process, timeout := TIMEOUT_INFINITE) -> (Process_
|
||||
}
|
||||
|
||||
/*
|
||||
Close the handle to a process.
|
||||
Close the handle to a process.
|
||||
|
||||
This procedure closes the handle associated with a process. It **does not**
|
||||
terminate a process, in case it was running. In case a termination is
|
||||
desired, kill the process first, wait for the process to finish,
|
||||
then close the handle.
|
||||
This procedure closes the handle associated with a process. It **does not**
|
||||
terminate a process, in case it was running. In case a termination is
|
||||
desired, kill the process first, wait for the process to finish,
|
||||
then close the handle.
|
||||
*/
|
||||
@(require_results)
|
||||
process_close :: proc(process: Process) -> (Error) {
|
||||
@@ -402,10 +511,9 @@ process_close :: proc(process: Process) -> (Error) {
|
||||
}
|
||||
|
||||
/*
|
||||
Terminate a process.
|
||||
|
||||
This procedure terminates a process, specified by it's handle, `process`.
|
||||
Terminate a process.
|
||||
|
||||
This procedure terminates a process, specified by it's handle, `process`.
|
||||
*/
|
||||
@(require_results)
|
||||
process_kill :: proc(process: Process) -> (Error) {
|
||||
|
||||
@@ -523,7 +523,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
write_errno_to_parent_and_abort :: proc(parent_fd: linux.Fd, errno: linux.Errno) -> ! {
|
||||
error_byte: [1]u8 = { u8(errno) }
|
||||
linux.write(parent_fd, error_byte[:])
|
||||
intrinsics.trap()
|
||||
linux.exit(126)
|
||||
}
|
||||
|
||||
stdin_fd: linux.Fd
|
||||
|
||||
@@ -163,7 +163,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
#assert(len(posix.Errno) < max(u8))
|
||||
errno := u8(posix.errno())
|
||||
posix.write(parent_fd, &errno, 1)
|
||||
runtime.trap()
|
||||
posix.exit(126)
|
||||
}
|
||||
|
||||
null := posix.open("/dev/null", {.RDWR})
|
||||
@@ -223,7 +223,6 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
return
|
||||
}
|
||||
|
||||
process.pid = int(pid)
|
||||
process, _ = _process_open(int(pid), {})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ _process_list :: proc(allocator: runtime.Allocator) -> (list: []int, err: Error)
|
||||
}
|
||||
|
||||
_process_open :: proc(pid: int, flags: Process_Open_Flags) -> (process: Process, err: Error) {
|
||||
process.pid = pid
|
||||
err = .Unsupported
|
||||
return
|
||||
}
|
||||
|
||||
@@ -442,7 +442,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
stderr_handle = win32.HANDLE((^File_Impl)(desc.stderr.impl).fd)
|
||||
}
|
||||
if desc.stdin != nil {
|
||||
stdin_handle = win32.HANDLE((^File_Impl)(desc.stderr.impl).fd)
|
||||
stdin_handle = win32.HANDLE((^File_Impl)(desc.stdin.impl).fd)
|
||||
}
|
||||
|
||||
working_dir_w := (win32_utf8_to_wstring(desc.working_dir, temp_allocator()) or_else nil) if len(desc.working_dir) > 0 else nil
|
||||
@@ -650,26 +650,30 @@ _build_command_line :: proc(command: []string, allocator: runtime.Allocator) ->
|
||||
strings.write_byte(&builder, ' ')
|
||||
}
|
||||
j := 0
|
||||
strings.write_byte(&builder, '"')
|
||||
for j < len(arg) {
|
||||
backslashes := 0
|
||||
for j < len(arg) && arg[j] == '\\' {
|
||||
backslashes += 1
|
||||
if strings.contains_any(arg, "()[]{}^=;!'+,`~\" ") {
|
||||
strings.write_byte(&builder, '"')
|
||||
for j < len(arg) {
|
||||
backslashes := 0
|
||||
for j < len(arg) && arg[j] == '\\' {
|
||||
backslashes += 1
|
||||
j += 1
|
||||
}
|
||||
if j == len(arg) {
|
||||
_write_byte_n_times(&builder, '\\', 2*backslashes)
|
||||
break
|
||||
} else if arg[j] == '"' {
|
||||
_write_byte_n_times(&builder, '\\', 2*backslashes+1)
|
||||
strings.write_byte(&builder, arg[j])
|
||||
} else {
|
||||
_write_byte_n_times(&builder, '\\', backslashes)
|
||||
strings.write_byte(&builder, arg[j])
|
||||
}
|
||||
j += 1
|
||||
}
|
||||
if j == len(arg) {
|
||||
_write_byte_n_times(&builder, '\\', 2*backslashes)
|
||||
break
|
||||
} else if arg[j] == '"' {
|
||||
_write_byte_n_times(&builder, '\\', 2*backslashes+1)
|
||||
strings.write_byte(&builder, '"')
|
||||
} else {
|
||||
_write_byte_n_times(&builder, '\\', backslashes)
|
||||
strings.write_byte(&builder, arg[j])
|
||||
}
|
||||
j += 1
|
||||
strings.write_byte(&builder, '"')
|
||||
} else {
|
||||
strings.write_string(&builder, arg)
|
||||
}
|
||||
strings.write_byte(&builder, '"')
|
||||
}
|
||||
return strings.to_string(builder)
|
||||
}
|
||||
|
||||
@@ -200,22 +200,21 @@ _file_type_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: wi
|
||||
} else {
|
||||
mode |= 0o666
|
||||
}
|
||||
|
||||
is_sym := false
|
||||
if file_attributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
|
||||
is_sym = false
|
||||
} else {
|
||||
is_sym = ReparseTag == win32.IO_REPARSE_TAG_SYMLINK || ReparseTag == win32.IO_REPARSE_TAG_MOUNT_POINT
|
||||
}
|
||||
|
||||
if is_sym {
|
||||
type = .Symlink
|
||||
} else {
|
||||
if file_attributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
type = .Directory
|
||||
mode |= 0o111
|
||||
}
|
||||
if h != nil {
|
||||
type = file_type(h)
|
||||
}
|
||||
} else if file_attributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
type = .Directory
|
||||
mode |= 0o111
|
||||
} else if h != nil {
|
||||
type = file_type(h)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1061,7 +1061,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (path: string, err: Error) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
|
||||
rel := rel
|
||||
if rel == "" {
|
||||
rel = "."
|
||||
@@ -1076,9 +1076,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
}
|
||||
defer _unix_free(rawptr(path_ptr))
|
||||
|
||||
path = strings.clone(string(path_ptr))
|
||||
|
||||
return path, nil
|
||||
return strings.clone(string(path_ptr), allocator)
|
||||
}
|
||||
|
||||
access :: proc(path: string, mask: int) -> bool {
|
||||
|
||||
@@ -789,7 +789,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
|
||||
rel := rel
|
||||
if rel == "" {
|
||||
rel = "."
|
||||
@@ -804,10 +804,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
}
|
||||
defer _unix_free(rawptr(path_ptr))
|
||||
|
||||
|
||||
path = strings.clone(string(path_ptr))
|
||||
|
||||
return path, nil
|
||||
return strings.clone(string(path_ptr), allocator)
|
||||
}
|
||||
|
||||
access :: proc(path: string, mask: int) -> (bool, Error) {
|
||||
|
||||
@@ -431,7 +431,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
|
||||
rel := rel
|
||||
if rel == "" {
|
||||
rel = "."
|
||||
@@ -447,9 +447,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
defer _unix_free(path_ptr)
|
||||
|
||||
path_cstr := cstring(path_ptr)
|
||||
path = strings.clone(string(path_cstr))
|
||||
|
||||
return path, nil
|
||||
return strings.clone(string(path_cstr), allocator)
|
||||
}
|
||||
|
||||
access :: proc(path: string, mask: int) -> (bool, Error) {
|
||||
|
||||
+10
-9
@@ -242,10 +242,13 @@ F_SETFL: int : 4 /* Set file flags */
|
||||
|
||||
// NOTE(zangent): These are OS specific!
|
||||
// Do not mix these up!
|
||||
RTLD_LAZY :: 0x001
|
||||
RTLD_NOW :: 0x002
|
||||
RTLD_BINDING_MASK :: 0x3
|
||||
RTLD_GLOBAL :: 0x100
|
||||
RTLD_LAZY :: 0x0001
|
||||
RTLD_NOW :: 0x0002
|
||||
RTLD_BINDING_MASK :: 0x0003
|
||||
RTLD_GLOBAL :: 0x0100
|
||||
RTLD_NOLOAD :: 0x0004
|
||||
RTLD_DEEPBIND :: 0x0008
|
||||
RTLD_NODELETE :: 0x1000
|
||||
|
||||
socklen_t :: c.int
|
||||
|
||||
@@ -487,7 +490,7 @@ foreign libc {
|
||||
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---
|
||||
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
|
||||
|
||||
@(link_name="execvp") _unix_execvp :: proc(path: cstring, argv: [^]cstring) -> int ---
|
||||
@(link_name="execvp") _unix_execvp :: proc(path: cstring, argv: [^]cstring) -> c.int ---
|
||||
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
|
||||
@(link_name="putenv") _unix_putenv :: proc(cstring) -> c.int ---
|
||||
@(link_name="setenv") _unix_setenv :: proc(key: cstring, value: cstring, overwrite: c.int) -> c.int ---
|
||||
@@ -914,7 +917,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
|
||||
rel := rel
|
||||
if rel == "" {
|
||||
rel = "."
|
||||
@@ -929,9 +932,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
}
|
||||
defer _unix_free(rawptr(path_ptr))
|
||||
|
||||
path = strings.clone(string(path_ptr))
|
||||
|
||||
return path, nil
|
||||
return strings.clone(string(path_ptr), allocator)
|
||||
}
|
||||
|
||||
access :: proc(path: string, mask: int) -> (bool, Error) {
|
||||
|
||||
@@ -844,7 +844,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (path: string, err: Error) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
|
||||
rel := rel
|
||||
if rel == "" {
|
||||
rel = "."
|
||||
@@ -859,9 +859,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
}
|
||||
defer _unix_free(rawptr(path_ptr))
|
||||
|
||||
path = strings.clone(string(path_ptr))
|
||||
|
||||
return path, nil
|
||||
return strings.clone(string(path_ptr), allocator)
|
||||
}
|
||||
|
||||
access :: proc(path: string, mask: int) -> (bool, Error) {
|
||||
|
||||
@@ -758,7 +758,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
|
||||
rel := rel
|
||||
if rel == "" {
|
||||
rel = "."
|
||||
@@ -773,9 +773,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
}
|
||||
defer _unix_free(rawptr(path_ptr))
|
||||
|
||||
path = strings.clone(string(path_ptr))
|
||||
|
||||
return path, nil
|
||||
return strings.clone(string(path_ptr), allocator)
|
||||
}
|
||||
|
||||
access :: proc(path: string, mask: int) -> (bool, Error) {
|
||||
|
||||
Reference in New Issue
Block a user