aboutsummaryrefslogtreecommitdiff
path: root/core/sys/linux/wrappers.odin
blob: 53eb80f8625e193296e168e01b2219a783dd74e2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#+build linux
package linux

/// Low 8 bits of the exit code
/// Only retrieve the exit code if WIFEXITED(s) = true
WEXITSTATUS :: #force_inline proc "contextless" (s: u32) -> u32 {
	return (s & 0xff00) >> 8
}

/// Termination signal
/// Only retrieve the code if WIFSIGNALED(s) = true
WTERMSIG :: #force_inline proc "contextless" (s: u32) -> u32 {
	return s & 0x7f
}

/// The signal that stopped the child
/// Only retrieve if WIFSTOPPED(s) = true
WSTOPSIG :: #force_inline proc "contextless" (s: u32) -> u32 {
	return WEXITSTATUS(s)
}

/// Check if the process terminated normally (via exit.2)
WIFEXITED :: #force_inline proc "contextless" (s: u32) -> bool {
	return WTERMSIG(s) == 0
}

/// Check if the process signaled
WIFSIGNALED :: #force_inline proc "contextless" (s: u32) -> bool {
	return cast(i8)(((s) & 0x7f) + 1) >> 1 > 0
}

/// Check if the process has stopped
WIFSTOPPED :: #force_inline proc "contextless" (s: u32) -> bool {
	return (s & 0xff) == 0x7f
}

/// Check if the process is continued by the tracee
WIFCONTINUED :: #force_inline proc "contextless" (s: u32) -> bool {
	return s == 0xffff
}

/// Check if the process dumped core
WCOREDUMP :: #force_inline proc "contextless" (s: u32) -> bool {
	return s & 0x80 == 0x80
}

@private _sigmask :: proc "contextless" (sig: Signal) -> (uint) {
	return 1 << ((cast(uint)(sig) - 1) % (8*size_of(uint)))
}
@private _sigword :: proc "contextless" (sig: Signal) -> (uint) {
	return (cast(uint)sig - 1) / (8*size_of(uint))
}

// TODO: sigaddset etc


/*
Iterate the results of `getdents()`.

This procedure extracts a directory entry from `buf` at the offset `offs`.
`offs` will be modified to store an offset to the possible next directory entry
in `buf`. The procedure only iterates as much data as loaded in the buffer and
does not automatically make a request for the buffer to be refilled.

Inputs:
- buf: A byte buffer with data from `getdents()`
- offs: An offset to the next possible directory entry in `buf`

Returns:
- A pointer to a directory entry in `buf`, or `nil`
- A bool value denoting if a valid directory entry is returned

Example:

    import "core:fmt"
    import "core:sys/linux"

    print_names :: proc(dirfd: linux.Fd) {
        // Get dirents into a buffer.
        buf: [128]u8
        // Loop until there are no more entries.
        for {
            written, err := linux.getdents(dirfd, buf[:])
            if err != .NONE || written == 0 {
                break
            }
            // Print the names of the files.
            offset : int
            for dir in linux.dirent_iterate_buf(buf[:written], &offset) {
                name := linux.dirent_name(dir)
                fmt.println(name)
            }
        }
    }
*/
dirent_iterate_buf :: proc "contextless" (buf: []u8, offs: ^int) -> (d: ^Dirent, cont: bool) {
	// Stopped iterating when there's no space left
	if offs^ >= len(buf) {
		return nil, false
	}
	// Retrieve dirent form the current offset
	dirent := cast(^Dirent) &buf[offs^]
	// Add the stride of dirent struct to the current offset
	offs^ += cast(int) dirent.reclen
	return dirent, true
}

/*
Obtain the name of dirent as a string.

The lifetime of the returned string is bound to the lifetime of the provided dirent structure.

Inputs:
- dirent: A directory entry

Returns:
- A name of the entry
*/
dirent_name :: proc "contextless" (dirent: ^Dirent) -> string #no_bounds_check {
	str := ([^]u8)(&dirent.name)
	// Dirents are aligned to 8 bytes, so there is guaranteed to be a null
	// terminator in the last 8 bytes.
	str_size := int(dirent.reclen) - cast(int)offset_of(Dirent, name)

	trunc := min(str_size, 8)
	str_size -= trunc
	for _ in 0..<trunc {
		if str[str_size] == 0 {
			break
		}
		str_size += 1
	}
	return string(str[:str_size])
}

/// Constructor for the `futex_op` argument of a FUTEX_WAKE_OP call
futex_op :: proc "contextless" (arg_op: Futex_Arg_Op, cmp_op: Futex_Cmp_Op, op_arg: u32, cmp_arg: u32) -> u32 {
	arg_op := cast(u32) arg_op
	cmp_op := cast(u32) cmp_op
	return (arg_op << 28) | (cmp_op << 24) | ((op_arg & 0xfff) << 12) | (cmp_arg & 0xfff)
}

/// Helper function for constructing the config for caches
perf_cache_config :: #force_inline proc "contextless" (id: Perf_Hardware_Cache_Id,
	op: Perf_Hardware_Cache_Op_Id,
	res: Perf_Hardware_Cache_Result_Id) -> u64 {
	return u64(id) | (u64(op) << 8) | (u64(res) << 16)
}