package linux

/*
	Type for storage device handle.
*/
Dev :: distinct u64

/*
	Type for 32-bit User IDs.
*/
Uid :: distinct u32

/*
	Type for 32-bit Group IDs.
*/
Gid :: distinct u32

/*
	Type for Process IDs, Thread IDs, Thread group ID.
*/
Pid :: distinct i32

/*
	Type for any of: pid, pidfd, pgid.
*/
Id :: distinct uint

/*
	Represents a file descriptor.
*/
Fd  :: distinct i32

/*
	Represents a watch descriptor.
*/
Wd  :: distinct i32

/*
	Type for PID file descriptors.
*/
Pid_FD :: distinct i32

/*
	Type for 64-bit inode number for files.
	Used pretty much only in struct Stat64 for 32-bit platforms.
*/
Inode :: distinct u64

/*
	Shared memory identifiers used by shmget(2) and other calls.
*/
Key :: distinct i32

/*
	Represents timer IDs.
*/
Timer :: distinct i32

/*
	Represents time with nanosecond precision.
*/
Time_Spec :: struct {
	time_sec:  uint,
	time_nsec: uint,
}

/*
	Represents time with millisecond precision.
*/
Time_Val :: struct {
	seconds:      int,
	microseconds: int,
}

/*
	Access and modification times for files
*/
UTim_Buf :: struct {
	actime:  uint,
	modtime: uint,
}

/*
	Flags for open(2).
*/
Open_Flags :: bit_set[Open_Flags_Bits; u32]

/*
	Flags for the file descriptors.
*/
FD_Flags :: bit_set[FD_Flags_Bits; i32]

/*
	Represents file's permission and status bits
**Example:**
	When you're passing a value of this type the recommended usage is:

	```
	  linux.Mode{.S_IXOTH, .S_IROTH} | linux.S_IRWXU | linux.S_IRWXG
	```

	This would generate a mode that has full permissions for the
	file's owner and group, and only "read" and "execute" bits
	for others.
*/
Mode :: bit_set[Mode_Bits; u32]

when ODIN_ARCH == .amd64 {
	// x86-64 has mode and nlink swapped for some reason
	_Arch_Stat :: struct {
		dev:       Dev,
		ino:       Inode,
		nlink:     uint,
		mode:      Mode,
		uid:       Uid,
		gid:       Gid,
		_:         u32,
		rdev:      Dev,
		size:      uint,
		blksize:   uint,
		blocks:    uint,
		atime:     Time_Spec,
		mtime:     Time_Spec,
		ctime:     Time_Spec,
		_:         [3]uint,
	}
} else when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
	_Arch_Stat :: struct {
		dev:        Dev,
		ino:        Inode,
		mode:       Mode,
		nlink:      u32,
		uid:        Uid,
		gid:        Gid,
		rdev:       Dev,
		_:          u64,
		size:       int,
		blksize:    i32,
		_:          i32,
		blocks:     int,
		atime:      Time_Spec,
		mtime:      Time_Spec,
		ctime:      Time_Spec,
		_:          [2]u32,
	}
} else {
	_Arch_Stat :: struct {
		dev:        Dev,
		_:          [4]u8,
		_ino:       uint, // Old 32-bit inode number, don't use
		mode:       Mode,
		nlink:      u32,
		uid:        Uid,
		gid:        Gid,
		rdev:       Dev,
		_:          [4]u8,
		size:       i64,
		blksize:    uint,
		blocks:     u64,
		atime:      Time_Spec,
		mtime:      Time_Spec,
		ctime:      Time_Spec,
		ino:        Inode,
	}
}

/*
	Represents the file state.
	If you're on 32-bit platform, consider using Stat64 instead.
*/
Stat :: struct {
	using _impl_stat: _Arch_Stat,
}

/*
	Timestamp type used for Statx struct
*/
Statx_Timestamp :: struct {
	sec:  i64,
	nsec: u32,
	_:       i32,
}

/*
	Query params/results for `statx()`.
*/
Statx_Mask :: bit_set[Statx_Mask_Bits; u32]

/*
	File attributes, returned by statx. This bitset is also
	used to specify which attributes are present, not just
	their value.
*/
Statx_Attr :: bit_set[Statx_Attr_Bits; u64]

/*
	The extended Stat struct, the argument to statx(2) syscall.
*/
Statx :: struct {
	mask:                Statx_Mask,
	blksize:             u32,
	attributes:          Statx_Attr,
	nlink:               u32,
	uid:                 Uid,
	gid:                 Gid,
	// Note(flysand): mode is 16-bit on linux + there's
	// 16-bit padding following it. Since our mode is 32-bits,
	// we're using the padding. This should be fine because
	// the placement of that padding suggests it was going to
	// be used for the Mode bits anyway.
	mode:                Mode,
	ino:                 Inode,
	size:                u64,
	blocks:              u64,
	attributes_mask:     Statx_Attr,
	atime:               Statx_Timestamp,
	btime:               Statx_Timestamp,
	ctime:               Statx_Timestamp,
	mtime:               Statx_Timestamp,
	rdev_major:          u32,
	rdev_minor:          u32,
	dev_major:           u32,
	dev_minor:           u32,
	mnt_id:              u64,
	dio_mem_align:       u32,
	dio_offset_align:    u32,
	_:                   [12]u64,
}

/*
	Mount flags for filesystem.
*/
FS_Flags :: bit_set[FS_Flags_Bits; u32]

when size_of(int) == 8 {
	_Arch_Stat_FS :: struct {
		// Note(flysand): The FS_Magic bits are never above
		// 32-bits, so it should be fine for now...
		type:     FS_Magic,
		_:        u32,
		bsize:    i64,
		blocks:   i64,
		bfree:    i64,
		bavail:   i64,
		files:    i64,
		ffree:    i64,
		fsid :    [2]i32,
		namelen:  i64,
		frsize:   i64,
		// Note(flysand): Same story as type
		flags:    FS_Flags,
		_:        u32,
		spare:    [4]i64,
	}
} else {
	_Arch_Stat_FS :: struct {
		type:     FS_Magic,
		bsize:    u32,
		blocks:   u64,
		bfree:    u64,
		bavail:   u64,
		files:    u64,
		ffree:    u64,
		fsid:     [2]i32,
		namelen:  u32,
		frsize:   u32,
		flags:    FS_Flags,
		spare:    [4]u32,
	}
}

/*
	Struct for statfs(2).
*/
Stat_FS :: struct {
	using _impl_stat_fs: _Arch_Stat_FS,
}

/*
	Flags for close_range(2).
*/
Close_Range_Flags :: bit_set[Close_Range_Flags_Bits; u32]

/*
	Flags for rename(2).
*/
Rename_Flags :: bit_set[Rename_Flags_Bits; u32]

/*
	Directory entry record.
	Recommended to iterate these with `dirent_iterate_buf()`,
	and obtain the name via `dirent_name()`.
*/
Dirent :: struct {
	ino:    Inode,
	off:    i64,
	reclen: u16,
	type:   Dirent_Type,
	name:   [0]u8, // See dirent_name
}

/*
	Lock record for fcntl(2).
*/
FLock :: struct {
	type:   FLock_Type,
	whence: Seek_Whence,
	start:  i64,
	len:    i64,
	pid:    Pid,
}

/*
	File locking operations.
	Use one of `EX`, `RW` or `UN` to specify the operation, and add `UN` if
	you need a non-blocking operation.
*/
FLock_Op :: bit_set[FLock_Op_Bits; i32]

/*
	Flags for `fcntl_notify()`.
*/
FD_Notifications :: bit_set[FD_Notifications_Bits; i32]

/*
	Seals for `fcntl_add_seals()`.
*/
Seal :: bit_set[Seal_Bits; i32]

/*
	Represents owner that receives events on file updates.
*/
F_Owner :: struct {
	type: F_Owner_Type,
	pid:  Pid,
}

/*
	Events for ppoll(2).
*/
Fd_Poll_Events :: bit_set[Fd_Poll_Events_Bits; u16]

/*
	Struct for ppoll(2).
*/
Poll_Fd :: struct {
	fd:      Fd,
	events:  Fd_Poll_Events,
	revents: Fd_Poll_Events,
}

Inotify_Init_Flags :: bit_set[Inotify_Init_Bits; i32]

Inotify_Event :: struct {
	wd:     Wd,
	mask:   Inotify_Event_Mask,
	cookie: u32,
	len:    u32,
	name:   [0]u8,
}

Inotify_Event_Mask :: bit_set[Inotify_Event_Bits; u32]

/*
	Specifies protection for memory pages.
*/
Mem_Protection :: bit_set[Mem_Protection_Bits; i32]

/*
	Flags for mmap.

	See `constants.odin` for `MAP_SHARED_VALIDATE` and `MAP_HUGE_16KB`, et al.
*/
Map_Flags :: bit_set[Map_Flags_Bits; i32]

/*
	Flags for mlock(2).
*/
MLock_Flags :: bit_set[MLock_Flags_Bits; u32]

/*
	Flags for msync(2).
*/
MSync_Flags :: bit_set[MSync_Flags_Bits; i32]

/*
	Access rights for pkey_alloc(2).
*/
PKey_Access_Rights :: bit_set[PKey_Access_Bits; u32]

/*
	Flags for mremap(2).
*/
MRemap_Flags :: bit_set[MRemap_Flags_Bits; i32]

/*
	Flags for getrandom(2) syscall.
*/
Get_Random_Flags :: bit_set[Get_Random_Flags_Bits; i32]

/*
	Flags for perf_event_open(2) syscall.
*/
Perf_Flags :: bit_set[Perf_Flags_Bits; uint]

Perf_Event_Flags :: distinct bit_set[Perf_Event_Flags_Bits; u64]

Perf_Read_Format :: distinct bit_set[Perf_Read_Format_Bits; u64]

Perf_Cap_Flags :: distinct bit_set[Perf_Cap_Flags_Bits; u64]

Perf_Event_Sample_Type :: bit_set[Perf_Event_Sample_Type_Bits; u64]

/*
	Specifies which branches to include in branch record.
*/
Branch_Sample_Type :: bit_set[Branch_Sample_Type_Bits; u64]

/*
	The struct for perf_event_open.
*/
Perf_Event_Attr :: struct #packed {
	type:               Perf_Event_Type,
	size:               u32,
	config: struct #raw_union {
		hw: Perf_Hardware_Id,
		sw: Perf_Software_Id,
		cache: u64,
		other: u64,
	},
	sample: struct #raw_union {
		period:    u64,
		frequency: u64,
	},
	sample_type:        Perf_Event_Sample_Type,
	read_format:        Perf_Read_Format,
	flags:              Perf_Event_Flags,
	wakeup: struct #raw_union {
		events:    u32,
		watermark: u32,
	},
	breakpoint_type:    Hardware_Breakpoint_Type,
	using _: struct #raw_union {
		breakpoint_addr: u64,
		kprobe_func:     u64,
		uprobe_path:     u64,
		config1:         u64,
	},
	using _: struct #raw_union {
		breakpoint_len: u64,
		kprobe_addr:    u64,
		uprobe_offset:  u64,
		config2:        u64,
	},
	branch_sample_type: Branch_Sample_Type,
	sample_regs_user:   u64,
	sample_stack_user:  u32,
	clock_id:           i32, // TODO(flysand): clock_id
	sample_regs_intr:   u64,
	aux_watermark:      u32,
	sample_max_stack:   u16,
	_:                  u16,
}

/*
	The ring buffer structure when mmaping Perf_Event_Attr.
*/
Perf_Event_Mmap_Page :: struct #packed {
	version:        u32,
	compat_version: u32,
	lock:           u32,
	index:          u32,
	offset:         i64,
	time_enabled:   u64,
	time_running:   u64,
	cap: struct #raw_union {
		capabilities: u64,
		flags: Perf_Cap_Flags,
	},
	pmc_width:      u16,
	time_shift:     u16,
	time_mult:      u32,
	time_offset:    u64,
	time_zero:      u64,
	size:           u32,
	reserved1:      u32,
	time_cycles:    u64,
	time_mask:      u64,
	reserved2:      [116*8]u8,
	data_head:      u64,
	data_tail:      u64,
	data_offset:    u64,
	data_size:      u64,
	aux_head:       u64,
	aux_tail:       u64,
	aux_offset:     u64,
	aux_size:       u64,
}

// TODO(flysand): Its taking too much effort to bind the other data structures related to perf_event_open

/*
	Options for wait4(2) and waitpid(2).
*/
Wait_Options :: bit_set[Wait_Option; i32]

/*
	Flags for pidfd_open(2).
*/
Pid_FD_Flags :: bit_set[Pid_FD_Flags_Bits; i32]

// Note(flysand): these could, in principle be implemented with bitfields,
// however there are ABI differences between odin's bitfields and linux sigsets.
// Mainly:
//  1. Odin's bitfields start from 0, whereas signals start from 1
//  2. It's unclear how bitfields act in terms of ABI (are they an array of ints or an array of longs?).
//     it makes a difference because ARM is big endian.
@private _SIGSET_NWORDS :: (8 / size_of(uint))
Sig_Set :: [_SIGSET_NWORDS]uint

@private SI_MAX_SIZE       :: 128
@private SI_ARCH_PREAMBLE  :: 4 * size_of(i32) when size_of(rawptr) == 8 else 3 * size_of(i32)
@private SI_PAD_SIZE       :: SI_MAX_SIZE - SI_ARCH_PREAMBLE

Sig_Handler_Fn :: #type proc "c" (sig: Signal)
Sig_Restore_Fn :: #type proc "c" () -> !

when size_of(rawptr) == 8 {
	Sig_Info :: struct #packed {
		signo: Signal,
		errno: Errno,
		code: i32,
		_pad0: i32,
		using _union: struct #raw_union {
			_pad1: [SI_PAD_SIZE]u8,
			using _kill: struct {
				pid: Pid, /* sender's pid */
				uid: Uid, /* sender's uid */
			},
			using _timer: struct {
				timerid: i32,   /* timer id */
				overrun: i32,   /* overrun count */
				value: Sig_Val, /* timer value */
			},
			/* POSIX.1b signals */
			using _rt: struct {
				_pid0: Pid, /* sender's pid */
				_uid0: Uid, /* sender's uid */
			},
			/* SIGCHLD */
			using _sigchld: struct {
				_pid1: Pid,  /* which child */
				_uid1: Uid,  /* sender's uid */
				status: i32, /* exit code */
				utime: uint,
				stime: uint, //clock_t
			},
			/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
			using _sigfault: struct {
				addr: rawptr, /* faulting insn/memory ref. */
				using _: struct #raw_union {
					trapno: i32,   /* Trap number that caused signal */
					addr_lsb: i16, /* LSB of the reported address */
					using _addr_bnd: struct {
						_pad2: u64,
						lower: rawptr, /* lower bound during fault */
						upper: rawptr, /* upper bound during fault */
					},
					using _addr_pkey: struct {
						_pad3: u64,
						pkey: u32, /* protection key on PTE that faulted */
					},
					using _perf: struct {
						perf_data: u64,
						perf_type: u32,
						perf_flags: u32,
					},
				},
			},
			/* SIGPOLL */
			using _sigpoll: struct {
				band: int, /* POLL_IN, POLL_OUT, POLL_MSG */
				fd: Fd,
			},
			/* SIGSYS */
			using _sigsys: struct {
				call_addr: rawptr, /* calling user insn */
				syscall: i32,      /* triggering system call number */
				arch: u32,         /* AUDIT_ARCH_* of syscall */
			},
		},
	}

	#assert(size_of(Sig_Info) == 128)
	#assert(offset_of(Sig_Info, signo)      == 0x00)
	#assert(offset_of(Sig_Info, errno)      == 0x04)
	#assert(offset_of(Sig_Info, code)       == 0x08)
	#assert(offset_of(Sig_Info, pid)        == 0x10)
	#assert(offset_of(Sig_Info, uid)        == 0x14)
	#assert(offset_of(Sig_Info, timerid)    == 0x10)
	#assert(offset_of(Sig_Info, overrun)    == 0x14)
	#assert(offset_of(Sig_Info, value)      == 0x18)
	#assert(offset_of(Sig_Info, status)     == 0x18)
	#assert(offset_of(Sig_Info, utime)      == 0x20)
	#assert(offset_of(Sig_Info, stime)      == 0x28)
	#assert(offset_of(Sig_Info, addr)       == 0x10)
	#assert(offset_of(Sig_Info, addr_lsb)   == 0x18)
	#assert(offset_of(Sig_Info, trapno)     == 0x18)
	#assert(offset_of(Sig_Info, lower)      == 0x20)
	#assert(offset_of(Sig_Info, upper)      == 0x28)
	#assert(offset_of(Sig_Info, pkey)       == 0x20)
	#assert(offset_of(Sig_Info, perf_data)  == 0x18)
	#assert(offset_of(Sig_Info, perf_type)  == 0x20)
	#assert(offset_of(Sig_Info, perf_flags) == 0x24)
	#assert(offset_of(Sig_Info, band)       == 0x10)
	#assert(offset_of(Sig_Info, fd)         == 0x18)
	#assert(offset_of(Sig_Info, call_addr)  == 0x10)
	#assert(offset_of(Sig_Info, syscall)    == 0x18)
	#assert(offset_of(Sig_Info, arch)       == 0x1C)
} else {
	Sig_Info :: struct {
		signo: Signal,
		errno: Errno,
		code: i32,
		using _union: struct #raw_union {
			_pad1: [SI_PAD_SIZE]u8,
			using _kill: struct {
				pid: Pid, /* sender's pid */
				uid: Uid, /* sender's uid */
			},
			using _timer: struct {
				timerid: i32,   /* timer id */
				overrun: i32,   /* overrun count */
				value: Sig_Val, /* timer value */
			},
			/* POSIX.1b signals */
			using _rt: struct {
				_pid0: Pid, /* sender's pid */
				_uid0: Uid, /* sender's uid */
			},
			/* SIGCHLD */
			using _sigchld: struct {
				_pid1: Pid,  /* which child */
				_uid1: Uid,  /* sender's uid */
				status: i32, /* exit code */
				utime: uint,
				stime: uint, //clock_t
			},
			/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
			using _sigfault: struct {
				addr: rawptr, /* faulting insn/memory ref. */
				using _: struct #raw_union {
					trapno: i32,   /* Trap number that caused signal */
					addr_lsb: i16, /* LSB of the reported address */
					using _addr_bnd: struct {
						_pad2: u32,
						lower: rawptr, /* lower bound during fault */
						upper: rawptr, /* upper bound during fault */
					},
					using _addr_pkey: struct {
						_pad3: u32,
						pkey: u32, /* protection key on PTE that faulted */
					},
					using _perf: struct {
						perf_data: u32,
						perf_type: u32,
						perf_flags: u32,
					},
				},
			},
			/* SIGPOLL */
			using _sigpoll: struct {
				band: int, /* POLL_IN, POLL_OUT, POLL_MSG */
				fd: Fd,
			},
			/* SIGSYS */
			using _sigsys: struct {
				call_addr: rawptr, /* calling user insn */
				syscall: i32,      /* triggering system call number */
				arch: u32,         /* AUDIT_ARCH_* of syscall */
			},
		},
	}

	#assert(size_of(Sig_Info) == 128)
	#assert(offset_of(Sig_Info, signo)      == 0x00)
	#assert(offset_of(Sig_Info, errno)      == 0x04)
	#assert(offset_of(Sig_Info, code)       == 0x08)
	#assert(offset_of(Sig_Info, pid)        == 0x0c)
	#assert(offset_of(Sig_Info, uid)        == 0x10)
	#assert(offset_of(Sig_Info, timerid)    == 0x0c)
	#assert(offset_of(Sig_Info, overrun)    == 0x10)
	#assert(offset_of(Sig_Info, value)      == 0x14)
	#assert(offset_of(Sig_Info, status)     == 0x14)
	#assert(offset_of(Sig_Info, utime)      == 0x18)
	#assert(offset_of(Sig_Info, stime)      == 0x1c)
	#assert(offset_of(Sig_Info, addr)       == 0x0c)
	#assert(offset_of(Sig_Info, addr_lsb)   == 0x10)
	#assert(offset_of(Sig_Info, trapno)     == 0x10)
	#assert(offset_of(Sig_Info, lower)      == 0x14)
	#assert(offset_of(Sig_Info, upper)      == 0x18)
	#assert(offset_of(Sig_Info, pkey)       == 0x14)
	#assert(offset_of(Sig_Info, perf_data)  == 0x10)
	#assert(offset_of(Sig_Info, perf_type)  == 0x14)
	#assert(offset_of(Sig_Info, perf_flags) == 0x18)
	#assert(offset_of(Sig_Info, band)       == 0x0c)
	#assert(offset_of(Sig_Info, fd)         == 0x10)
	#assert(offset_of(Sig_Info, call_addr)  == 0x0c)
	#assert(offset_of(Sig_Info, syscall)    == 0x10)
	#assert(offset_of(Sig_Info, arch)       == 0x14)
}

SIGEV_MAX_SIZE :: 64
SIGEV_PAD_SIZE :: ((SIGEV_MAX_SIZE-size_of(i32)*2+size_of(Sig_Val))/size_of(i32))

Sig_Val :: struct #raw_union {
	sival_int: i32,
	sival_ptr: rawptr,
}

Sig_Event :: struct {
	value:  Sig_Val,
	signo:  i32,
	notify: i32,
	using _: struct #raw_union {
		_: [SIGEV_PAD_SIZE]u32,
		thread_id: Pid,
		using _: struct {
			notify_function:  #type proc "c" (val: Sig_Val),
			notify_attribute: rawptr,
		},
	},
}

Sig_Stack_Flags :: bit_set[Sig_Stack_Flag; i32]

Sig_Stack :: struct {
	sp: rawptr,
	flags: Sig_Stack_Flags,
	size: uintptr,
}

Sig_Action_Special :: enum uint {
	SIG_DFL = 0,
	SIG_IGN = 1,
	SIG_ERR = ~uint(0),
}

Sig_Action_Flags :: bit_set[Sig_Action_Flag; uint]
Sig_Action :: struct($T: typeid) {
	using _u: struct #raw_union {
		handler: Sig_Handler_Fn,
		sigaction: #type proc "c" (sig: Signal, si: ^Sig_Info, ctx: ^T),
		special: Sig_Action_Special,
	},
	flags: Sig_Action_Flags,
	restorer: Sig_Restore_Fn,
	mask: Sig_Set,
}

/*
	Flags for the socket file descriptor.
	Note, on linux these are technically passed by OR'ing together
	with Socket_Type, our wrapper does this under the hood.
*/
Socket_FD_Flags :: bit_set[Socket_FD_Flags_Bits; i32]

/*
	Address family for the socket.
	Typically there's one address family for every protocol family.
*/
Address_Family :: distinct Protocol_Family

/*
	Flags for the socket for send/recv calls.
*/
Socket_Msg :: bit_set[Socket_Msg_Bits; i32]

/*
	Struct representing a generic socket address.
*/
Sock_Addr :: struct #packed {
	sa_family: Address_Family,
	sa_data:   [14]u8,
}

/*
	Struct representing IPv4 socket address.
*/
Sock_Addr_In :: struct #packed {
	sin_family: Address_Family,
	sin_port:   u16be,
	sin_addr:   [4]u8,
	sin_zero:   [size_of(Sock_Addr) - size_of(Address_Family) - size_of(u16be) - size_of([4]u8)]u8,
}

/*
	Struct representing IPv6 socket address.
*/
Sock_Addr_In6 :: struct #packed {
	sin6_family:   Address_Family,
	sin6_port:     u16be,
	sin6_flowinfo: u32,
	sin6_addr:     [16]u8,
	sin6_scope_id: u32,
}

/*
	Struct representing Unix Domain Socket address
*/
Sock_Addr_Un :: struct #packed {
	sun_family: Address_Family,
	sun_path:   [108]u8,
}

/*
	Struct representing an arbitrary socket address.
*/
Sock_Addr_Any :: struct #raw_union {
	using _: struct {
		family: Address_Family,
		port:   u16be,
	},
	using generic: Sock_Addr,
	using ipv4: Sock_Addr_In,
	using ipv6: Sock_Addr_In6,
	using uds: Sock_Addr_Un,
}

/*
	Message header for sendmsg/recvmsg
*/
Msg_Hdr :: struct {
	name:       rawptr,
	namelen:    i32,
	iov:        []IO_Vec, // ptr followed by length, abi matches
	control:    []u8,
	flags:      Socket_Msg,
}

/*
	Multiple message header for sendmmsg/recvmmsg
*/
MMsg_Hdr :: struct {
	hdr: Msg_Hdr,
	len: u32,
}

/*
	Just an alias to make futex-values more visible
*/
Futex :: u32

/*
	Flags for the futex (they are kept separately)
*/
Futex_Flags :: bit_set[Futex_Flags_Bits; u32]

/*
	Times
*/
Tms :: struct {
	tms_utime:  int,
	tms_stime:  int,
	tms_cutime: int,
	tms_cstime: int,
}

/*
	"Unix time-sharing system name", allegedly.
	Basically system info.
*/
UTS_Name :: struct {
	sysname:    [65]u8 `fmt:"s,0"`,
	nodename:   [65]u8 `fmt:"s,0"`,
	release:    [65]u8 `fmt:"s,0"`,
	version:    [65]u8 `fmt:"s,0"`,
	machine:    [65]u8 `fmt:"s,0"`,
	domainname: [65]u8 `fmt:"s,0"`,
}

/*
	Return buffer for the sysinfo syscall
*/
Sys_Info :: struct {
	uptime:    int,
	loads:     [3]int,
	totalram:  uint,
	freeram:   uint,
	sharedram: uint,
	bufferram: uint,
	totalswap: uint,
	freeswap:  uint,
	procs:     u16,
	totalhigh: uint,
	freehigh:  uint,
	mem_unit:  i32,
	_padding:  [20 - (2 * size_of(int)) - size_of(i32)]u8,
}

/*
	Resource limit
*/
RLimit :: struct {
	cur: uint,
	max: uint,
}

/*
	Structure representing how much of each resource got used.
*/
RUsage :: struct {
	utime:         Time_Val,
	stime:         Time_Val,
	maxrss_word:   int,
	ixrss_word:    int,
	idrss_word:    int,
	isrss_word:    int,
	minflt_word:   int,
	majflt_word:   int,
	nswap_word:    int,
	inblock_word:  int,
	oublock_word:  int,
	msgsnd_word:   int,
	msgrcv_word:   int,
	nsignals_word: int,
	nvcsw_word:    int,
	nivcsw_word:   int,
}

/*
	Struct used for IO operations
*/
IO_Vec :: struct {
	base: [^]byte,
	len:  uint,
}

/*
	Access modes and flags used by SystemV IPC procedures.
*/
IPC_Flags :: bit_set[IPC_Flags_Bits; i16]

/*
	Permissions for SystemV IPC primitives.
*/
IPC_Perm :: struct {
	key:  Key,
	uid:  u32,
	gid:  u32,
	cuid: u32,
	cgid: u32,
	mode: IPC_Flags, // Only contains mode flags.
	seq:  u16,
	_:    [2 + 2*size_of(int)]u8,
}

when size_of(int) == 8 || ODIN_ARCH == .i386 {
	// 32-bit and 64-bit x86, 64-bit arm
	_Arch_Shmid_DS :: struct {
		perm:   IPC_Perm,
		segsz:  uint,
		atime:  int,
		dtime:  int,
		ctime:  int,
		cpid:   Pid,
		lpid:   Pid,
		nattch: uint,
		_:      [2]uint,
	}
} else {
	// Other 32-bit platforms
	// NOTE(flysand): I'm not risking assuming it's little endian...
	_Arch_Shmid_DS :: struct {
		perm:       IPC_Perm,
		segsz:      uint,
		atime:      uint,
		atime_high: uint,
		dtime:      uint,
		dtime_high: uint,
		ctime:      uint,
		ctime_high: uint,
		cpid:       Pid,
		lpid:       Pid,
		nattach:    uint,
		_:          [2]uint,
	}
}

/*
	SystemV shared memory data.
*/
Shmid_DS :: _Arch_Shmid_DS

/*
	SystemV shared memory info.
*/
Shm_Info :: struct {
	used_ids:       i32,
	shm_tot:        uint,
	shm_rss:        uint,
	shm_swp:        uint,
	swap_attempts:  uint,
	swap_successes: uint,
}

/*
	SystemV semaphore operation.
*/
Sem_Buf :: struct {
	num: u16,
	op:  IPC_Cmd,
	flg: IPC_Flags,
}

when ODIN_ARCH == .i386 {
	_Arch_Semid_DS :: struct {
		perm:       IPC_Perm,
		otime:      uint,
		otime_high: uint,
		ctime:      uint,
		ctime_high: uint,
		nsems:      uint,
		_:          [2]uint,
	}
} else when ODIN_ARCH == .amd64 {
	_Arch_Semid_DS :: struct {
		perm:       IPC_Perm,
		otime:      int,
		ctime:      int,
		nsems:      uint,
		_:          [2]uint,
	}
} else when ODIN_ARCH == .arm32 {
	_Arch_Semid_DS :: struct {
		perm:       IPC_Perm,
		otime:      uint,
		otime_high: uint,
		ctime:      uint,
		ctime_high: uint,
		nsems:      uint,
		_:          [2]uint,
	}
} else when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
	_Arch_Semid_DS :: struct {
		perm:       IPC_Perm,
		otime:      int,
		ctime:      int,
		sem_nsems:  uint,
		__unused3:  uint,
		__unused4:  uint,
	}
}

/*
	Architecture-specific semaphore data.
*/
Semid_DS :: _Arch_Semid_DS

/*
	Argument for semctl functions
*/
Sem_Un :: struct #raw_union {
	val:   i32,
	buf:   rawptr,
	array: u16,
	__buf: Sem_Info,
	_:     uintptr,
}

/*
	SystenV semaphore info.
*/
Sem_Info :: struct {
	semmap: i32,
	mni: i32,
	mns: i32,
	mnu: i32,
	msl: i32,
	opm: i32,
	ume: i32,
	usz: i32,
	vmx: i32,
	aem: i32,
}

/*
	Template for the struct used for sending and receiving messages
*/
Msg_Buf :: struct {
	type: int,
	text: [0]u8,
}

/*
	SystemV message queue data.
*/
Msqid_DS :: struct {
	perm:   IPC_Perm,
	stime:  uint,
	rtime:  uint,
	ctime:  uint,
	cbytes: uint,
	qnum:   uint,
	qbytes: uint,
	lspid:  Pid,
	lrpid:  Pid,
	_:      [2]uint,
}

/*
	Interval timer types
*/
ITimer_Which :: enum {
	REAL    = 0,
	VIRTUAL = 1,
	PROF    = 2,
}

/*
	Interval timer value
*/
ITimer_Val :: struct {
	interval: Time_Val,
	value:    Time_Val,
}

ITimer_Spec :: struct {
	interval: Time_Spec,
	value:    Time_Spec,
}

/*
	Flags for POSIX interval timers.
*/
ITimer_Flags :: bit_set[ITimer_Flags_Bits; u32]

when ODIN_ARCH == .arm32 {
	_Arch_User_Regs :: struct {
		cpsr:             uint,
		pc:               uint,
		lr:               uint,
		sp:               uint,
		ip:               uint,
		fp:               uint,
		r10:              uint,
		r9:               uint,
		r8:               uint,
		r7:               uint,
		r6:               uint,
		r5:               uint,
		r4:               uint,
		r3:               uint,
		r2:               uint,
		r1:               uint,
		r0:               uint,
		ORIG_r0:          uint,
	}
	// TODO(flysand): Idk what to do about these, couldn't find their
	// definitions
	_Arch_User_FP_Regs :: struct {}
	_Arch_User_FPX_Regs :: struct {}
} else when ODIN_ARCH == .arm64 {
	_Arch_User_Regs :: struct {
		regs:             [31]uint,
		sp:               uint,
		pc:               uint,
		pstate:           uint,
	}
	_Arch_User_FP_Regs :: struct {
		vregs:            [32]u128,
		fpsr:             u32,
		fpcr:             u32,
		_:                [2]u32,
	}
	_Arch_User_FPX_Regs :: struct {}
} else when ODIN_ARCH == .i386 {
	_Arch_User_Regs :: struct {
		ebx:              uint,
		ecx:              uint,
		edx:              uint,
		esi:              uint,
		edi:              uint,
		ebp:              uint,
		eax:              uint,
		xds:              uint,
		xes:              uint,
		xfs:              uint,
		xgs:              uint,
		orig_eax:         uint,
		eip:              uint,
		xcs:              uint,
		eflags:           uint,
		esp:              uint,
		xss:              uint,
	}
	// x87 FPU state
	_Arch_User_FP_Regs :: struct {
		cwd:              uint,
		swd:              uint,
		twd:              uint,
		fip:              uint,
		fcs:              uint,
		foo:              uint,
		fos:              uint,
		st_space:         [20]uint,
	}
	// FXSR instruction set state
	_Arch_User_FPX_Regs :: struct {
		cwd:              u16,
		swd:              u16,
		twd:              u16,
		fop:              u16,
		fip:              uint,
		fcs:              uint,
		foo:              uint,
		fos:              uint,
		mxcsr:            uint,
		_:                uint,
		st_space:         [32]uint,
		xmm_space:        [32]uint,
		padding:          [56]uint,
	}
} else when ODIN_ARCH == .amd64 {
	_Arch_User_Regs :: struct {
		// Callee-preserved, may not be correct if the syscall doesn't need
		// these registers
		r15:              uint,
		r14:              uint,
		r13:              uint,
		r12:              uint,
		rbp:              uint,
		rbx:              uint,
		// Always saved
		r11:              uint,
		r10:              uint,
		r9:               uint,
		r8:               uint,
		rax:              uint,
		rcx:              uint,
		rdx:              uint,
		rsi:              uint,
		rdi:              uint,
		// On syscall entry this is the syscall number, on CPU exception this
		// is the error code, on hardware interrupt this is its IRQ number
		orig_rax:         uint,
		// Return frame for iretq
		rip:              uint,
		cs:               uint,
		eflags:           uint,
		rsp:              uint,
		ss:               uint,
		fs_base:          uint,
		gs_base:          uint,
		ds:               uint,
		es:               uint,
		fs:               uint,
		gs:               uint,
	}
	// All floating point state
	_Arch_User_FP_Regs :: struct {
		cwd:              u16,
		swd:              u16,
		twd:              u16,
		fop:              u16,
		rip:              uint,
		rdp:              uint,
		mxcsr:            u32,
		mxcsr_mask:       u32,
		st_space:         [32]u32,
		xmm_space:        [64]u32,
		_:                [24]u32,
	}
	// FXSR instruction set state
	_Arch_User_FPX_Regs :: struct {
		cwd:              u16,
		swd:              u16,
		twd:              u16,
		fop:              u16,
		fip:              uint,
		fcs:              uint,
		foo:              uint,
		fos:              uint,
		mxcsr:            uint,
		_:                uint,
		st_space:         [32]uint,
		xmm_space:        [32]uint,
		padding:          [56]uint,
	}
} else when ODIN_ARCH == .riscv64 {
	_Arch_User_Regs :: struct {
		pc, ra, sp, gp, tp,
		t0, t1, t2,
		s0, s1,
		a0, a1, a2, a3, a4, a5, a6, a7,
		s2, s3, s4, s5, s6, s7, s8, s9, s10, s11,
		t3, t4, t5, t6: uint,
	}
	_Arch_User_FP_Regs :: struct #raw_union {
		f_ext: struct {
			f:    [32]u32,
			fcsr: u32,
		},
		d_ext: struct {
			f:    [32]u64,
			fcsr: u32,
		},
		q_ext: struct {
			using _: struct #align(16) {
				f:    [64]u64,
			},
			fcsr:     u32,
			reserved: [3]u32,
		},
	}
	_Arch_User_FPX_Regs :: struct {}
}

/*
	Architecture-specific registers struct.
*/
User_Regs :: _Arch_User_Regs

/*
	Architecture-specific floating-point registers
*/
User_FP_Regs :: _Arch_User_FP_Regs

/*
	Architecture-specific extended floating-point registers.
	Currently only used for x86 CPU's.
*/
User_FPX_Regs :: _Arch_User_FPX_Regs

/*
	ptrace options.
*/
PTrace_Options :: bit_set[PTrace_Options_Bits; u32]

/*
	ptrace's PEEKSIGINFO argument.
*/
PTrace_Peek_Sig_Info_Args :: struct {
	off:   u64,
	flags: PTrace_Peek_Sig_Info_Flags,
	nr:    i32,
}

/*
	ptrace's PEEKSIGINFO flags.
*/
PTrace_Peek_Sig_Info_Flags :: bit_set[PTrace_Peek_Sig_Info_Flags_Bits; u32]

/*
	ptrace's SECCOMP metadata.
*/
PTrace_Seccomp_Metadata :: struct {
	filter_off: u64,
	flags:      u64,
}

/*
	ptrace's results of GET_SYSCALL_INFO.
*/
PTrace_Syscall_Info :: struct {
	op:                  PTrace_Get_Syscall_Info_Op,
	arch:                u32, // TODO: AUDIT_ARCH*
	instruction_pointer: u64,
	stack_pointer:       u64,
	using _: struct #raw_union {
		entry: struct {
			nr:       u64,
			args:     [6]u64,
		},
		exit: struct {
			rval:     i64,
			is_error: b8,
		},
		seccomp: struct {
			nr:       u64,
			args:     [6]u64,
			ret_data: u32,
		},
	},
}

/*
	ptrace's results of GET_RSEQ_CONFIGURATION.
*/
PTrace_RSeq_Configuration :: struct {
	rseq_abi_pointer: u64,
	rseq_abi_size:    u32,
	signature:        u32,
	flags:            u32,
	_:                u32,
}

/*
	Note types for PTRACE_GETREGSET. Mirrors constants in `elf` definition,
	files though this enum only contains the constants defined for architectures
	Odin can compile to.
*/
PTrace_Note_Type :: enum {
	NT_PRSTATUS             = 1,
	NT_PRFPREG              = 2,
	NT_PRPSINFO             = 3,
	NT_TASKSTRUCT           = 4,
	NT_AUXV                 = 6,
	NT_SIGINFO              = 0x53494749,
	NT_FILE                 = 0x46494c45,
	NT_PRXFPREG             = 0x46e62b7f,
	NT_386_TLS              = 0x200,
	NT_386_IOPERM           = 0x201,
	NT_X86_XSTATE           = 0x202,
	NT_X86_SHSTK            = 0x204,
	NT_ARM_VFP              = 0x400,
	NT_ARM_TLS              = 0x401,
	NT_ARM_HW_BREAK         = 0x402,
	NT_ARM_HW_WATCH         = 0x403,
	NT_ARM_SYSTEM_CALL      = 0x404,
	NT_ARM_SVE              = 0x405,
	NT_ARM_PAC_MASK         = 0x406,
	NT_ARM_PACA_KEYS        = 0x407,
	NT_ARM_PACG_KEYS        = 0x408,
	NT_ARM_TAGGED_ADDR_CTRL = 0x409,
	NT_ARM_PAC_ENABLED_KEYS = 0x40a,
	NT_ARM_SSVE             = 0x40b,
	NT_ARM_ZA               = 0x40c,
	NT_ARM_ZT               = 0x40d,
}

/*
	Flags for splice(2) and tee(2) syscalls.
*/
Splice_Flags :: bit_set[Splice_Flags_Bits; u32]

/*
	Flags for epoll_create(2) syscall.
*/
EPoll_Flags :: bit_set[EPoll_Flags_Bits; i32]

EPoll_Data :: struct #raw_union {
	ptr: rawptr,
	fd:  Fd,
	u32: u32,
	u64: u64,
}

/*
	Linux kernel only packs this struct on x86_64.
	include/uapi/linux/eventpoll.h:	
		#ifdef __x86_64__
		#define EPOLL_PACKED __attribute__((packed))
		#else
		#define EPOLL_PACKED
		#endif
*/
when ODIN_ARCH == .amd64 {
	EPoll_Event :: struct #packed {
		events: EPoll_Event_Set,
		data:   EPoll_Data,
	}
} else {
	EPoll_Event :: struct {
		events: EPoll_Event_Set,
		data:   EPoll_Data,
	}
}

/*
	Flags for execveat(2) syscall.
*/
Execveat_Flags :: bit_set[Execveat_Flags_Bits; i32]

RISCV_HWProbe_Flags         :: bit_set[RISCV_HWProbe_Flags_Bits; u32]
RISCV_HWProbe_CPU_Perf_0    :: bit_set[RISCV_HWProbe_Misaligned_Scalar_Perf; u64]
RISCV_HWProbe_Base_Behavior :: bit_set[RISCV_HWProbe_Base_Behavior_Bits; u64]
RISCV_HWProbe_IMA_Ext_0     :: bit_set[RISCV_HWProbe_IMA_Ext_0_Bits; u64]

RISCV_HWProbe :: struct {
 	// set to `.UNSUPPORTED` by the kernel if that is the case.
	key:   RISCV_HWProbe_Key,
	value: struct #raw_union {
		base_behavior:          RISCV_HWProbe_Base_Behavior,
		ima_ext_0:              RISCV_HWProbe_IMA_Ext_0,
		cpu_perf_0:             RISCV_HWProbe_CPU_Perf_0,
		misaligned_scalar_perf: RISCV_HWProbe_Misaligned_Scalar_Perf,
		raw:                    u64,
	},
}

IO_Uring_Params :: struct {
	sq_entries:     u32,
	cq_entries:     u32,
	flags:          IO_Uring_Setup_Flags,
	sq_thread_cpu:  u32,
	sq_thread_idle: u32,
	features:       IO_Uring_Features,
	wq_fd:          u32,
	resv:           [3]u32,
	sq_off:         IO_SQ_Ring_Offsets,
	cq_off:         IO_CQ_Ring_Offsets,
}

IO_Uring_Setup_Flags :: bit_set[IO_Uring_Setup_Flags_Bits; u32]

IO_Uring_Features :: bit_set[IO_Uring_Features_Bits; u32]

IO_SQ_Ring_Offsets :: struct {
	head:         u32,
	tail:         u32,
	ring_mask:    u32,
	ring_entries: u32,
	flags:        u32,
	dropped:      u32,
	array:        u32,
	resv1:        u32,
	user_addr:    u64,
}

IO_CQ_Ring_Offsets :: struct {
	head:         u32,
	tail:         u32,
	ring_mask:    u32,
	ring_entries: u32,
	overflow:     u32,
	cqes:         u32,
	flags:        u32,
	resv1:        u32,
	user_addr:    u64,
}

IO_Uring_Enter_Flags :: bit_set[IO_Uring_Enter_Flags_Bits; u32]

IO_Uring_Getevents_Arg :: struct #min_field_align(8) {
	sigmask:    ^Sig_Set,
	sigmask_sz: u32,
	// pad:     u32,
	ts:         ^Time_Spec,
}
#assert(align_of(IO_Uring_Getevents_Arg) == 8)

IO_Uring_Rsrc_Register :: struct($T: typeid) {
	nr:    u32,
	resv:  u32,
	resv2: u64,
	using _: struct #min_field_align(8) {
		data:  [^]T,
		tags:  [^]u64,
	},
}

IO_Uring_Rsrc_Update2 :: struct($T: typeid) {
	offset: u32,
	resv:   u32,
	using _: struct #min_field_align(8) {
		data: [^]T,
		tags: [^]u64,
	},
	nr:    u32,
	resv2: u32,
}

// The completion queue entry when the .CQE32 flag is not set on setup.
IO_Uring_CQE :: struct {
	// sq.data submission passed back.
	user_data: u64,
	// result code for this event.
	res:       i32,
	flags:     IO_Uring_CQE_Flags,
}
#assert(size_of(IO_Uring_CQE) == 16)

// The completion queue entry when the .CQE32 flag is set on setup.
IO_Uring_CQE32 :: struct {
	using _: IO_Uring_CQE,
	pad:     u64,
	pad2:    u64,
}
#assert(size_of(IO_Uring_CQE32) == 32)

IO_Uring_CQE_Flags :: bit_set[IO_Uring_CQE_Flags_Bits; u32]
IO_Uring_SQE_Flags :: bit_set[IO_Uring_SQE_Flags_Bits; u8]

// The submission queue entry when the .SQE128 flag is not set on setup.
IO_Uring_SQE :: struct {
	opcode:           IO_Uring_OP,
	flags:            IO_Uring_SQE_Flags,
	using __ioprio: struct #raw_union {
		ioprio:             u16,
		sq_accept_flags:    IO_Uring_Accept_Flags,
		sq_send_recv_flags: IO_Uring_Send_Recv_Flags `raw_union_tag:"opcode=.SENDMSG, opcode=.RECVMSG, opcode=.SEND, opcode=.RECV"`,
	},
	fd: Fd,
	using __offset: struct #raw_union {
		// Offset into file.
		off:     u64 `raw_union_tag:"opcode=.READV, opcode=.WRITEV, opcode=.SPLICE, opcode=.POLL_REMOVE, opcode=.EPOLL_CTL, opcode=.TIMEOUT, opcode=.ACCEPT, opcode=.CONNECT, opcode=.READ, opcode=.WRITE, opcode=.FILES_UPDATE, opcode=.SOCKET"`,
		addr2:   u64 `raw_union_tag:"opcode=.SEND, opcode=.BIND"`,
		using _: struct {
			cmd_op: u32,
			__pad1: u32,
		},
		statx: ^Statx `raw_union_tag:"opcode=.STATX"`,
	},
	using __iovecs:   struct #raw_union {
		// Pointer to buffer or iovecs.
		addr:          u64 `raw_union_tag:"opcode=.READV, opcode=.WRITEV, opcode=.POLL_REMOVE, opcode=.EPOLL_CTL, opcode=.SENDMSG, opcode=.RECVMSG, opcode=.SEND, opcode=.RECV, opcode=.TIMEOUT, opcode=.TIMEOUT_REMOVE, opcode=.ACCEPT, opcode=.ASYNC_CANCEL, opcode=.LINK_TIMEOUT, opcode=.CONNECT, opcode=.MADVISE, opcode=.OPENAT, opcode=.STATX, opcode=.READ, opcode=.WRITE, opcode=.FILES_UPDATE, opcode=.BIND, opcode=.LISTEN"`,
		splice_off_in: u64 `raw_union_tag:"opcode=.SPLICE"`,
		using _: struct {
			level:   u32,
			optname: u32,
		},
	},
	using __len: struct #raw_union {
		// Buffer size or number of iovecs.
		len:          u32                     `raw_union_tag:"opcode=.READV, opcode=.WRITEV, opcode=.SPLICE, opcode=.SEND, opcode=.RECV, opcode=.TIMEOUT, opcode=.LINK_TIMEOUT, opcode=.MADVISE, opcode=.OPENAT, opcode=.READ, opcode=.WRITE, opcode=.TEE, opcode=.FILES_UPDATE, opcode=.SOCKET"`,
		poll_flags:   IO_Uring_Poll_Add_Flags `raw_union_tag:"opcode=.POLL_ADD, opcode=.POLL_REMOVE"`,
		statx_mask:   Statx_Mask              `raw_union_tag:"opcode=.STATX"`,
		epoll_ctl_op: EPoll_Ctl_Opcode        `raw_union_tag:"opcode=.EPOLL_CTL"`,
		shutdown_how: Shutdown_How            `raw_union_tag:"opcode=.SHUTDOWN"`,
	},
	using __contents: struct #raw_union {
		rw_flags:         i32                     `raw_union_tag:"opcode=.READV, opcode=.WRITEV, opcode=.SOCKET"`,
		fsync_flags:      IO_Uring_Fsync_Flags    `raw_union_tag:"opcode=.FSYNC"`,
		// compatibility.
		poll_events:      Fd_Poll_Events          `raw_union_tag:"opcode=.POLL_ADD, opcode=.POLL_REMOVE"`,
		// word-reversed for BE.
		poll32_events:    u32,
		sync_range_flags: u32,
		msg_flags:        Socket_Msg              `raw_union_tag:"opcode=.SENDMSG, opcode=.RECVMSG, opcode=.SEND, opcode=.RECV"`,
		timeout_flags:    IO_Uring_Timeout_Flags  `raw_union_tag:"opcode=.TIMEOUT, opcode=.TIMEOUT_REMOVE, opcode=.LINK_TIMEOUT"`,
		accept_flags:     Socket_FD_Flags         `raw_union_tag:"opcode=.ACCEPT"`,
		cancel_flags:     u32,
		open_flags:       Open_Flags              `raw_union_tag:"opcode=.OPENAT"`,
		statx_flags:      FD_Flags                `raw_union_tag:"opcode=.STATX"`,
		fadvise_advice:   u32                     `raw_union_tag:"opcode=.MADVISE"`,
		splice_flags:     IO_Uring_Splice_Flags   `raw_union_tag:"opcode=.SPLICE, opcode=.TEE"`,
		rename_flags:     u32,
		unlink_flags:     u32,
		hardlink_flags:   u32,
		xattr_flags:      u32,
		msg_ring_flags:   u32,
		uring_cmd_flags:  IO_Uring_Cmd_Flags,
	},
	// Data to be passed back at completion time.
	user_data: u64,
	using __buffer: struct #raw_union {
		// Index into fixed buffers, if used.
		buf_index: u16,
		// For grouped buffer selection.
		buf_group: u16,
	},
	// Personality to use, if used.
	personality: u16,
	using _: struct #raw_union {
		splice_fd_in: Fd  `raw_union_tag:"opcode=.SPLICE, opcode=.TEE"`,
		file_index:   u32 `raw_union_tag:"opcode=.ACCEPT, opcode=.OPENAT, opcode=.CLOSE, opcode=.SOCKET"`,
		using _:      struct {
			addr_len: u16    `raw_union_tag:"opcode=.SEND"`,
			__pad3:   [1]u16,
		},
	},
	using __: struct #raw_union {
		using _: struct {
			addr3:  u64,
			__pad2: [1]u64,
		},
	},
}
#assert(size_of(IO_Uring_SQE) == 64)

// The submission queue entry when the .SQE128 flag is set on setup.
IO_Uring_SQE128 :: struct {
	using _: IO_Uring_SQE,
	cmd:     [64]byte,
}
#assert(size_of(IO_Uring_SQE128) == 128)

IO_Uring_Poll_Add_Flags :: bit_set[IO_Uring_Poll_Add_Flags_Bits; u32]

IO_Uring_Fsync_Flags :: bit_set[IO_Uring_Fsync_Flags_Bits; u32]

IO_Uring_Timeout_Flags :: bit_set[IO_Uring_Timeout_Flags_Bits; u32]

IO_Uring_Cmd_Flags :: bit_set[IO_Uring_Cmd_Flags_Bits; u32]

IO_Uring_Splice_Flags :: bit_set[IO_Uring_Splice_Flags_Bits; u32]

IO_Uring_Accept_Flags :: bit_set[IO_Uring_Accept_Flags_Bits; u16]

IO_Uring_Send_Recv_Flags :: bit_set[IO_Uring_Send_Recv_Flags_Bits; u16]

IO_Uring_Submission_Queue_Flags :: bit_set[IO_Uring_Submission_Queue_Flags_Bits; u32]

Clock_State :: enum i32 {
	TIME_OK = 0,
	TIME_INS = 1,
	TIME_DEL = 2,
	TIME_OOP = 3,
	TIME_WAIT = 4,
	TIME_ERROR = 5,
}

Timex :: struct {
	modes:     u32,
	offset:    int,
	freq:      int,
	maxerror:  int,
	esterror:  int,
	status:    i32,
	constant:  int,
	precision: int,
	tolerance: int,
	time:      Time_Val,
	tick:      int,
	ppsfreq:   int,
	jitter:    int,
	shift:     i32,
	stabil:    int,
	jitcnt:    int,
	calcnt:    int,
	errcnt:    int,
	stbcnt:    int,
	tai:       i32,
}

Reboot_Magic :: enum u64 {
	RB_MAGIC_1     = 0xfee1dead,
	RB_MAGIC_2     = 672274793,
	RB_MAGIC_2A    = 85072278, // Since Linux 2.1.17
	RB_MAGIC_2B    = 369367448, // Since Linux 2.1.97
	RB_MAGIC_2C    = 537993216, // Since Linux 2.5.71
}

Reboot_Operation :: enum u64 {
	RB_DISABLE_CAD = 0,
	RB_ENABLE_CAD  = 0x89abcdef,
	RB_HALT_SYSTEM = 0xcdef0123,
	RB_KEXEC       = 0x45584543,
	RB_POWER_OFF   = 0x4321fedc,
	RB_AUTOBOOT    = 0x01234567,
	RB_AUTOBOOT_2  = 0xa1b2c3d4,
	RB_SW_SUSPEND  = 0xd000fce2,
}

Mount_Flags :: bit_set[Mount_Flags_Bits; uint]

Umount2_Flags :: bit_set[Umount2_Flags_Bits; u32]

Swap_Flags :: bit_set[Swap_Flags_Bits; u32]

Eventfd_Flags :: bit_set[Eventfd_Flags_Bits; i32]

Cpu_Set :: bit_set[0 ..< 128]

Sched_Param :: struct {
	sched_priority: i32,
}

Sched_Flags :: bit_set[Sched_Flag_Bits; u32]

Sched_Attr :: struct {
	size:           u32,

	sched_policy:   Sched_Policy,
	sched_flags:    Sched_Flags,
	sched_nice:     i32,
	sched_priority: u32,

	/* For the DEADLINE policy */
	sched_runtime:  u64,
	sched_deadline: u64,
	sched_period:   u64,

	/* Utilization hints */
	sched_util_min: u32,
	sched_util_max: u32,
}

Sched_Attr_Flags :: bit_set[Sched_Attr_Flag_Bits; u32]

Memfd_Create_Flags :: bit_set[Memfd_Create_Flag_Bits; u32]
