Browse Source

Merge pull request #4118 from andradei/posix-linux

Linux POSIX support
Laytan 10 months ago
parent
commit
0157ff1541

+ 7 - 4
core/os/os_linux.odin

@@ -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
 

+ 37 - 60
core/sys/linux/bits.odin

@@ -152,66 +152,43 @@ Errno :: enum i32 {
 	RDONLY flag is not present, because it has the value of 0, i.e. it is the
 	default, unless WRONLY or RDWR is specified.
 */
-when ODIN_ARCH != .arm64 && ODIN_ARCH != .arm32 {
-	Open_Flags_Bits :: enum {
-		WRONLY    = 0,
-		RDWR      = 1,
-		CREAT     = 6,
-		EXCL      = 7,
-		NOCTTY    = 8,
-		TRUNC     = 9,
-		APPEND    = 10,
-		NONBLOCK  = 11,
-		DSYNC     = 12,
-		ASYNC     = 13,
-		DIRECT    = 14,
-		LARGEFILE = 15,
-		DIRECTORY = 16,
-		NOFOLLOW  = 17,
-		NOATIME   = 18,
-		CLOEXEC   = 19,
-		PATH      = 21,
-	}
-	// https://github.com/torvalds/linux/blob/7367539ad4b0f8f9b396baf02110962333719a48/include/uapi/asm-generic/fcntl.h#L19
-	#assert(1 << uint(Open_Flags_Bits.WRONLY)    == 0o0000000_1)
-	#assert(1 << uint(Open_Flags_Bits.RDWR)      == 0o0000000_2)
-	#assert(1 << uint(Open_Flags_Bits.CREAT)     == 0o00000_100)
-	#assert(1 << uint(Open_Flags_Bits.EXCL)      == 0o00000_200)
-	#assert(1 << uint(Open_Flags_Bits.NOCTTY)    == 0o00000_400)
-	#assert(1 << uint(Open_Flags_Bits.TRUNC)     == 0o0000_1000)
-	#assert(1 << uint(Open_Flags_Bits.APPEND)    == 0o0000_2000)
-	#assert(1 << uint(Open_Flags_Bits.NONBLOCK)  == 0o0000_4000)
-	#assert(1 << uint(Open_Flags_Bits.DSYNC)     == 0o000_10000)
-	#assert(1 << uint(Open_Flags_Bits.ASYNC)     == 0o000_20000)
-	#assert(1 << uint(Open_Flags_Bits.DIRECT)    == 0o000_40000)
-	#assert(1 << uint(Open_Flags_Bits.LARGEFILE) == 0o00_100000)
-	#assert(1 << uint(Open_Flags_Bits.DIRECTORY) == 0o00_200000)
-	#assert(1 << uint(Open_Flags_Bits.NOFOLLOW)  == 0o00_400000)
-	#assert(1 << uint(Open_Flags_Bits.NOATIME)   == 0o0_1000000)
-	#assert(1 << uint(Open_Flags_Bits.CLOEXEC)   == 0o0_2000000)
-	#assert(1 << uint(Open_Flags_Bits.PATH)      == 0o_10000000)
-
-} else {
-	Open_Flags_Bits :: enum {
-		WRONLY    = 0,
-		RDWR      = 1,
-		CREAT     = 6,
-		EXCL      = 7,
-		NOCTTY    = 8,
-		TRUNC     = 9,
-		APPEND    = 10,
-		NONBLOCK  = 11,
-		DSYNC     = 12,
-		ASYNC     = 13,
-		DIRECTORY = 14,
-		NOFOLLOW  = 15,
-		DIRECT    = 16,
-		LARGEFILE = 17,
-		NOATIME   = 18,
-		CLOEXEC   = 19,
-		PATH      = 21,
-	}
-}
+Open_Flags_Bits :: enum {
+	WRONLY    = 0,
+	RDWR      = 1,
+	CREAT     = 6,
+	EXCL      = 7,
+	NOCTTY    = 8,
+	TRUNC     = 9,
+	APPEND    = 10,
+	NONBLOCK  = 11,
+	DSYNC     = 12,
+	ASYNC     = 13,
+	DIRECT    = 14,
+	LARGEFILE = 15,
+	DIRECTORY = 16,
+	NOFOLLOW  = 17,
+	NOATIME   = 18,
+	CLOEXEC   = 19,
+	PATH      = 21,
+}
+// https://github.com/torvalds/linux/blob/7367539ad4b0f8f9b396baf02110962333719a48/include/uapi/asm-generic/fcntl.h#L19
+#assert(1 << uint(Open_Flags_Bits.WRONLY)    == 0o0000000_1)
+#assert(1 << uint(Open_Flags_Bits.RDWR)      == 0o0000000_2)
+#assert(1 << uint(Open_Flags_Bits.CREAT)     == 0o00000_100)
+#assert(1 << uint(Open_Flags_Bits.EXCL)      == 0o00000_200)
+#assert(1 << uint(Open_Flags_Bits.NOCTTY)    == 0o00000_400)
+#assert(1 << uint(Open_Flags_Bits.TRUNC)     == 0o0000_1000)
+#assert(1 << uint(Open_Flags_Bits.APPEND)    == 0o0000_2000)
+#assert(1 << uint(Open_Flags_Bits.NONBLOCK)  == 0o0000_4000)
+#assert(1 << uint(Open_Flags_Bits.DSYNC)     == 0o000_10000)
+#assert(1 << uint(Open_Flags_Bits.ASYNC)     == 0o000_20000)
+#assert(1 << uint(Open_Flags_Bits.DIRECT)    == 0o000_40000)
+#assert(1 << uint(Open_Flags_Bits.LARGEFILE) == 0o00_100000)
+#assert(1 << uint(Open_Flags_Bits.DIRECTORY) == 0o00_200000)
+#assert(1 << uint(Open_Flags_Bits.NOFOLLOW)  == 0o00_400000)
+#assert(1 << uint(Open_Flags_Bits.NOATIME)   == 0o0_1000000)
+#assert(1 << uint(Open_Flags_Bits.CLOEXEC)   == 0o0_2000000)
+#assert(1 << uint(Open_Flags_Bits.PATH)      == 0o_10000000)
 
 /*
 	Bits for FD_Flags bitset

+ 15 - 5
core/sys/posix/dirent.odin

@@ -193,13 +193,23 @@ when ODIN_OS == .Darwin {
 } else when ODIN_OS == .NetBSD {
 
 	dirent :: struct {
-		d_ino:     ino_t,                    /* [PSX] file number of entry */
-		d_reclen:  c.uint16_t,               /* length of this record */
-		d_namelen: c.uint16_t,               /* length of string in d_name */
-		d_type:    D_Type,                   /* file type  */
-		d_name:    [512]c.char `fmt:"s,0"`,  /* [PSX] entry name */
+		d_ino:     ino_t,                   /* [PSX] file number of entry */
+		d_reclen:  c.uint16_t,              /* length of this record */
+		d_namelen: c.uint16_t,              /* length of string in d_name */
+		d_type:    D_Type,                  /* file type  */
+		d_name:    [512]c.char `fmt:"s,0"`, /* [PSX] entry name */
 	}
 
+} else when ODIN_OS == .Linux {
+
+		dirent :: struct {
+			d_ino:    u64,                     /* [PSX] file number of entry */
+			d_off:    i64,                     /* directory offset of the next entry */
+			d_reclen: u16,                     /* length of this record */
+			d_type:   D_Type,                  /* file type  */
+			d_name:   [256]c.char `fmt:"s,0"`, /* [PSX] entry name */
+		}
+
 } else {
 	#panic("posix is unimplemented for the current target")
 }

+ 9 - 0
core/sys/posix/dlfcn.odin

@@ -111,6 +111,15 @@ when ODIN_OS == .Darwin {
 
 	RTLD_LOCAL   :: RTLD_Flags{RTLD_Flag_Bits(log2(_RTLD_LOCAL))}
 
+} else when ODIN_OS == .Linux {
+
+	RTLD_LAZY    :: 0x001
+	RTLD_NOW     :: 0x002
+	RTLD_GLOBAL  :: 0x100
+
+	_RTLD_LOCAL  :: 0
+	RTLD_LOCAL   :: RTLD_Flags{}
+
 } else {
 	#panic("posix is unimplemented for the current target")
 }

+ 95 - 6
core/sys/posix/errno.odin

@@ -141,7 +141,7 @@ when ODIN_OS == .Darwin {
 	EMLINK          :: 31
 	EPIPE           :: 32
 	EAGAIN          :: 35
-	EWOULDBLOCK     :: 35
+	EWOULDBLOCK     :: EAGAIN
 	EINPROGRESS     :: 36
 	EALREADY        :: 37
 	ENOTSOCK        :: 38
@@ -151,7 +151,7 @@ when ODIN_OS == .Darwin {
 	ENOPROTOOPT     :: 42
 	EPROTONOSUPPORT :: 43
 	ENOTSUP         :: 45
-	EOPNOTSUPP      :: 45
+	EOPNOTSUPP      :: ENOTSUP
 	EAFNOSUPPORT    :: 47
 	EADDRINUSE      :: 48
 	EADDRNOTAVAIL   :: 49
@@ -220,7 +220,7 @@ when ODIN_OS == .Darwin {
 	EMLINK          :: 31
 	EPIPE           :: 32
 	EAGAIN          :: 35
-	EWOULDBLOCK     :: 35
+	EWOULDBLOCK     :: EAGAIN
 	EINPROGRESS     :: 36
 	EALREADY        :: 37
 	ENOTSOCK        :: 38
@@ -230,7 +230,7 @@ when ODIN_OS == .Darwin {
 	ENOPROTOOPT     :: 42
 	EPROTONOSUPPORT :: 43
 	ENOTSUP         :: 45
-	EOPNOTSUPP      :: 45
+	EOPNOTSUPP      :: ENOTSUP
 	EAFNOSUPPORT    :: 47
 	EADDRINUSE      :: 48
 	EADDRNOTAVAIL   :: 49
@@ -301,7 +301,7 @@ when ODIN_OS == .Darwin {
 	EMLINK          :: 31
 	EPIPE           :: 32
 	EAGAIN          :: 35
-	EWOULDBLOCK     :: 35
+	EWOULDBLOCK     :: EAGAIN
 	EINPROGRESS     :: 36
 	EALREADY        :: 37
 	ENOTSOCK        :: 38
@@ -311,7 +311,7 @@ when ODIN_OS == .Darwin {
 	ENOPROTOOPT     :: 42
 	EPROTONOSUPPORT :: 43
 	ENOTSUP         :: 45
-	EOPNOTSUPP      :: 45
+	EOPNOTSUPP      :: ENOTSUP
 	EAFNOSUPPORT    :: 47
 	EADDRINUSE      :: 48
 	EADDRNOTAVAIL   :: 49
@@ -367,6 +367,95 @@ when ODIN_OS == .Darwin {
 		ETIME           :: -1
 	}
 
+} else when ODIN_OS == .Linux {
+	EPERM           :: 1
+	ENOENT          :: 2
+	ESRCH           :: 3
+	EINTR           :: 4
+	EIO             :: 5
+	ENXIO           :: 6
+	E2BIG           :: 7
+	ENOEXEC         :: 8
+	EBADF           :: 9
+	ECHILD          :: 10
+	EAGAIN          :: 11
+	EWOULDBLOCK     :: EAGAIN
+	ENOMEM          :: 12
+	EACCES          :: 13
+	EFAULT          :: 14
+	EBUSY           :: 16
+	EEXIST          :: 17
+	EXDEV           :: 18
+	ENODEV          :: 19
+	ENOTDIR         :: 20
+	EISDIR          :: 21
+	EINVAL          :: 22
+	ENFILE          :: 23
+	EMFILE          :: 24
+	ENOTTY          :: 25
+	ETXTBSY         :: 26
+	EFBIG           :: 27
+	ENOSPC          :: 28
+	ESPIPE          :: 29
+	EROFS           :: 30
+	EMLINK          :: 31
+	EPIPE           :: 32
+
+	EDEADLK         :: 35
+	ENAMETOOLONG    :: 36
+	ENOLCK          :: 37
+	ENOSYS          :: 38
+	ENOTEMPTY       :: 39
+	ELOOP           :: 40
+	ENOMSG          :: 42
+	EIDRM           :: 43
+
+	ENOSTR          :: 60
+	ENODATA         :: 61
+	ETIME           :: 62
+	ENOSR           :: 63
+
+	ENOLINK         :: 67
+
+	EPROTO          :: 71
+	EMULTIHOP       :: 72
+	EBADMSG         :: 74
+	EOVERFLOW       :: 75
+
+	ENOTSOCK        :: 88
+	EDESTADDRREQ    :: 89
+	EMSGSIZE        :: 90
+	EPROTOTYPE      :: 91
+	ENOPROTOOPT     :: 92
+	EPROTONOSUPPORT :: 93
+
+	EOPNOTSUPP      :: 95
+	ENOTSUP         :: EOPNOTSUPP
+	EAFNOSUPPORT    :: 97
+	EADDRINUSE      :: 98
+	EADDRNOTAVAIL   :: 99
+	ENETDOWN        :: 100
+	ENETUNREACH     :: 101
+	ENETRESET       :: 102
+	ECONNABORTED    :: 103
+	ECONNRESET      :: 104
+	ENOBUFS         :: 105
+	EISCONN         :: 106
+	ENOTCONN        :: 107
+
+	ETIMEDOUT       :: 110
+	ECONNREFUSED    :: 111
+
+	EHOSTUNREACH    :: 113
+	EALREADY        :: 114
+	EINPROGRESS     :: 115
+	ESTALE          :: 116
+
+	EDQUOT          :: 122
+	ECANCELED       :: 125
+
+	EOWNERDEAD      :: 130
+	ENOTRECOVERABLE :: 131
 } else {
 	#panic("posix is unimplemented for the current target")
 }

+ 106 - 43
core/sys/posix/fcntl.odin

@@ -92,9 +92,6 @@ Lock_Type :: enum c.short {
 	WRLCK = F_WRLCK,
 }
 
-// Assertions made to unify this bit set.
-#assert(O_RDONLY == 0)
-
 O_Flag_Bits :: enum c.int {
 	// Sets FD_CLOEXEC on the file descriptor.
 	CLOEXEC   = log2(O_CLOEXEC),
@@ -107,11 +104,11 @@ O_Flag_Bits :: enum c.int {
 	// If terminal device, do not make it the controlling terminal for the process.
 	NOCTTY    = log2(O_NOCTTY),
 	// Don't follow symbolic links, fail with errno ELOOP.
-	NOFOLLOW  = log2(O_NOFOLOW),
+	NOFOLLOW  = log2(O_NOFOLLOW),
 	// If exists and regular, truncate the length to 0.
 	TRUNC     = log2(O_TRUNC),
 
- 	// NOTE: use with `posix.O_TTY_INIT + { .OTHER_FLAG, .OTHER_FLAG }`, unfortunately can't be in
+	// NOTE: use with `posix.O_TTY_INIT + { .OTHER_FLAG, .OTHER_FLAG }`, unfortunately can't be in
 	// this bit set enum because it is 0 on some platforms and a value on others.
 	// TTY_INIT = O_TTY_INIT,
 
@@ -123,7 +120,8 @@ O_Flag_Bits :: enum c.int {
 	NONBLOCK  = log2(O_NONBLOCK),
 	// Write I/O shall complete as defined by synchronized I/O file integrity completion.
 	SYNC      = log2(O_SYNC),
- 	// NOTE: use with `posix.O_RSYNC + { .OTHER_FLAG, .OTHER_FLAG }`, unfortunately can't be in
+
+	// NOTE: use with `posix.O_RSYNC + { .OTHER_FLAG, .OTHER_FLAG }`, unfortunately can't be in
 	// this bit set enum because it is 0 on some platforms and a value on others.
 	// RSYNC = O_RSYNC,
 
@@ -135,11 +133,10 @@ O_Flag_Bits :: enum c.int {
 	WRONLY    = log2(O_WRONLY),
 	// Reading only.
 	// RDONLY = 0, // Default
-
 }
+
 O_Flags :: bit_set[O_Flag_Bits; c.int]
 
-// A mask of all the access mode bits.
 O_ACCMODE :: O_Flags{ .EXEC, .RDWR, .WRONLY }
 
 AT_Flag_Bits :: enum c.int {
@@ -152,8 +149,8 @@ AT_Flags :: bit_set[AT_Flag_Bits; c.int]
 
 when ODIN_OS == .Darwin {
 
-	off_t  :: distinct c.int64_t
-	pid_t  :: distinct c.int32_t
+	off_t :: distinct c.int64_t
+	pid_t :: distinct c.int32_t
 
 	F_DUPFD         :: 0
 	F_DUPFD_CLOEXEC :: 67
@@ -178,7 +175,7 @@ when ODIN_OS == .Darwin {
 	O_DIRECTORY :: 0x00100000
 	O_EXCL      :: 0x00000800
 	O_NOCTTY    :: 0x00020000
-	O_NOFOLOW   :: 0x00000100
+	O_NOFOLLOW  :: 0x00000100
 	O_TRUNC     :: 0x00000400
 
 	_O_TTY_INIT :: 0
@@ -189,16 +186,16 @@ when ODIN_OS == .Darwin {
 	O_NONBLOCK :: 0x00000004
 	O_SYNC     :: 0x0080
 
-	_O_RSYNC   :: 0
-	O_RSYNC    :: O_Flags{}
+	_O_RSYNC :: 0
+	O_RSYNC  :: O_Flags{}
 
-	O_EXEC    :: 0x40000000
-	O_RDONLY  :: 0
-	O_RDWR    :: 0x0002
-	O_WRONLY  :: 0x0001
+	O_EXEC   :: 0x40000000
+	O_RDONLY :: 0
+	O_RDWR   :: 0x0002
+	O_WRONLY :: 0x0001
 
 	_O_SEARCH :: O_EXEC | O_DIRECTORY
-	O_SEARCH  :: O_Flags{ .EXEC, .DIRECTORY }
+	O_SEARCH  :: O_Flags{.EXEC, .DIRECTORY}
 
 	AT_FDCWD: FD: -2
 
@@ -217,8 +214,8 @@ when ODIN_OS == .Darwin {
 
 } else when ODIN_OS == .FreeBSD {
 
-	off_t  :: distinct c.int64_t
-	pid_t  :: distinct c.int32_t
+	off_t :: distinct c.int64_t
+	pid_t :: distinct c.int32_t
 
 	F_DUPFD         :: 0
 	F_DUPFD_CLOEXEC :: 17
@@ -243,7 +240,7 @@ when ODIN_OS == .Darwin {
 	O_DIRECTORY :: 0x00020000
 	O_EXCL      :: 0x0800
 	O_NOCTTY    :: 0x8000
-	O_NOFOLOW   :: 0x0100
+	O_NOFOLLOW  :: 0x0100
 	O_TRUNC     :: 0x0400
 
 	_O_TTY_INIT :: 0x00080000
@@ -256,10 +253,10 @@ when ODIN_OS == .Darwin {
 	_O_RSYNC   :: 0
 	O_RSYNC    :: O_Flags{} // NOTE: not defined in headers
 
-	O_EXEC    :: 0x00040000
-	O_RDONLY  :: 0
-	O_RDWR    :: 0x0002
-	O_WRONLY  :: 0x0001
+	O_EXEC   :: 0x00040000
+	O_RDONLY :: 0
+	O_RDWR   :: 0x0002
+	O_WRONLY :: 0x0001
 
 	_O_SEARCH :: O_EXEC
 	O_SEARCH  :: O_Flags{ .EXEC }
@@ -282,8 +279,8 @@ when ODIN_OS == .Darwin {
 
 } else when ODIN_OS == .NetBSD {
 
-	off_t  :: distinct c.int64_t
-	pid_t  :: distinct c.int32_t
+	off_t :: distinct c.int64_t
+	pid_t :: distinct c.int32_t
 
 	F_DUPFD         :: 0
 	F_DUPFD_CLOEXEC :: 12
@@ -308,7 +305,7 @@ when ODIN_OS == .Darwin {
 	O_DIRECTORY :: 0x0020000
 	O_EXCL      :: 0x0800
 	O_NOCTTY    :: 0x8000
-	O_NOFOLOW   :: 0x0100
+	O_NOFOLLOW  :: 0x0100
 	O_TRUNC     :: 0x0400
 
 	_O_TTY_INIT :: 0
@@ -319,14 +316,14 @@ when ODIN_OS == .Darwin {
 	O_NONBLOCK :: 0x0004
 	O_SYNC     :: 0x0080
 
-	_O_RSYNC   :: 0x0002
-	O_RSYNC    :: O_Flags{O_Flag_Bits(log2(_O_RSYNC))}
+	_O_RSYNC :: 0x0002
+	O_RSYNC  :: O_Flags{O_Flag_Bits(log2(_O_RSYNC))}
 
 
-	O_EXEC    :: 0x04000000
-	O_RDONLY  :: 0
-	O_RDWR    :: 0x0002
-	O_WRONLY  :: 0x0001
+	O_EXEC   :: 0x04000000
+	O_RDONLY :: 0
+	O_RDWR   :: 0x0002
+	O_WRONLY :: 0x0001
 
 	_O_SEARCH :: 0x00800000
 	O_SEARCH  :: O_Flags{O_Flag_Bits(log2(_O_SEARCH))}
@@ -347,8 +344,8 @@ when ODIN_OS == .Darwin {
 	}
 } else when ODIN_OS == .OpenBSD {
 
-	off_t  :: distinct c.int64_t
-	pid_t  :: distinct c.int32_t
+	off_t :: distinct c.int64_t
+	pid_t :: distinct c.int32_t
 
 	F_DUPFD         :: 0
 	F_DUPFD_CLOEXEC :: 10
@@ -373,7 +370,7 @@ when ODIN_OS == .Darwin {
 	O_DIRECTORY :: 0x20000
 	O_EXCL      :: 0x0800
 	O_NOCTTY    :: 0x8000
-	O_NOFOLOW   :: 0x0100
+	O_NOFOLLOW  :: 0x0100
 	O_TRUNC     :: 0x0400
 
 	_O_TTY_INIT :: 0
@@ -384,13 +381,13 @@ when ODIN_OS == .Darwin {
 	O_NONBLOCK :: 0x0004
 	O_SYNC     :: 0x0080
 
-	_O_RSYNC   :: O_SYNC
-	O_RSYNC    :: O_Flags{ .SYNC }
+	_O_RSYNC :: O_SYNC
+	O_RSYNC  :: O_Flags{.SYNC}
 
-	O_EXEC    :: 0x04000000 // NOTE: not defined in the headers
-	O_RDONLY  :: 0
-	O_RDWR    :: 0x0002
-	O_WRONLY  :: 0x0001
+	O_EXEC   :: 0x04000000 // NOTE: not defined in the headers
+	O_RDONLY :: 0
+	O_RDWR   :: 0x0002
+	O_WRONLY :: 0x0001
 
 	_O_SEARCH :: 0
 	O_SEARCH  :: O_Flags{} // NOTE: not defined in the headers
@@ -410,6 +407,72 @@ when ODIN_OS == .Darwin {
 		l_whence: c.short,   /* [PSX] flag (Whence) of starting offset */
 	}
 
+} else when ODIN_OS == .Linux {
+
+	off_t :: distinct c.int64_t
+	pid_t :: distinct c.int
+
+	F_DUPFD  :: 0
+	F_GETFD  :: 1
+	F_SETFD  :: 2
+	F_GETFL  :: 3
+	F_SETFL  :: 4
+	F_GETLK  :: 5
+	F_SETLK  :: 6
+	F_SETLKW :: 7
+	F_SETOWN :: 8
+	F_GETOWN :: 9
+	F_RDLCK  :: 0
+	F_UNLCK  :: 2
+	F_WRLCK  :: 1
+
+	F_DUPFD_CLOEXEC :: 1030
+
+	FD_CLOEXEC :: 1
+
+	O_CREAT     :: 0o0_000_100
+	O_EXCL      :: 0o0_000_200
+	O_NOCTTY    :: 0o0_000_400
+	O_TRUNC     :: 0o0_001_000
+	O_DIRECTORY :: 0o0_200_000
+	O_NOFOLLOW  :: 0o0_400_000
+	O_CLOEXEC   :: 0o2_000_000
+
+	_O_TTY_INIT :: 0
+	O_TTY_INIT  :: O_Flags{}
+
+	O_APPEND   :: 0o0_002_000
+	O_NONBLOCK :: 0o0_004_000
+	O_DSYNC    :: 0o0_010_000
+	O_SYNC     :: 0o4_010_000
+
+	_O_RSYNC :: 0
+	O_RSYNC  :: O_Flags{}
+
+	O_EXEC   :: 0x04000000 // NOTE: not defined in the headers
+
+	O_RDONLY :: 0
+	O_WRONLY :: 0o1
+	O_RDWR   :: 0o2
+
+	_O_SEARCH :: 0
+	O_SEARCH  :: O_Flags{}
+
+	AT_FDCWD: FD: -100
+
+	AT_EACCESS          :: 0x200
+	AT_SYMLINK_NOFOLLOW :: 0x100
+	AT_SYMLINK_FOLLOW   :: 0x400
+	AT_REMOVEDIR        :: 0x200
+
+	flock :: struct {
+		l_start:  off_t,     /* [PSX] relative offset in bytes. */
+		l_len:    off_t,     /* [PSX] size; if 0 then until EOF. */
+		l_pid:    pid_t,     /* [PSX] process ID of the process holding the lock. */
+		l_type:   Lock_Type, /* [PSX] type of lock. */
+		l_whence: c.short,   /* [PSX] flag (Whence) of starting offset. */
+	}
+
 } else {
 	#panic("posix is unimplemented for the current target")
 }

+ 8 - 0
core/sys/posix/fnmatch.odin

@@ -53,6 +53,14 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 	FNM_PERIOD   :: 0x04
 	FNM_NOESCAPE :: 0x01
 
+} else when ODIN_OS == .Linux {
+
+	FNM_NOMATCH  :: 1
+
+	FNM_PATHNAME :: 0x01
+	FNM_NOESCAPE :: 0x02
+	FNM_PERIOD   :: 0x04
+
 } else {
 	#panic("posix is unimplemented for the current target")
 }

+ 31 - 2
core/sys/posix/glob.odin

@@ -112,7 +112,7 @@ when ODIN_OS == .Darwin {
 
 	glob_t :: struct {
 		gl_pathc:  c.size_t,                      /* [PSX] count of paths matched by pattern */
-		gl_matchc: c.size_t,                         /* count of paths matching pattern */
+		gl_matchc: c.size_t,                      /* count of paths matching pattern */
 		gl_offs:   c.size_t,                      /* [PSX] slots to reserve at the beginning of gl_pathv */
 		gl_flags:  Glob_Flags,                    /* copy of flags parameter to glob */
 		gl_pathv:  [^]cstring `fmt:"v,gl_pathc"`, /* [PSX] pointer to list of matched pathnames */
@@ -144,7 +144,7 @@ when ODIN_OS == .Darwin {
 
 	glob_t :: struct {
 		gl_pathc:  c.size_t,                      /* [PSX] count of paths matched by pattern */
-		gl_matchc: c.size_t,                         /* count of paths matching pattern */
+		gl_matchc: c.size_t,                      /* count of paths matching pattern */
 		gl_offs:   c.size_t,                      /* [PSX] slots to reserve at the beginning of gl_pathv */
 		gl_flags:  Glob_Flags,                    /* copy of flags parameter to glob */
 		gl_pathv:  [^]cstring `fmt:"v,gl_pathc"`, /* [PSX] pointer to list of matched pathnames */
@@ -174,6 +174,35 @@ when ODIN_OS == .Darwin {
 	GLOB_NOMATCH :: -3
 	GLOB_NOSPACE :: -1
 
+} else when ODIN_OS == .Linux {
+
+	glob_t :: struct {
+		gl_pathc:  c.size_t,                      /* [PSX] count of paths matched by pattern */
+		gl_pathv:  [^]cstring `fmt:"v,gl_pathc"`, /* [PSX] pointer to list of matched pathnames */
+		gl_offs:   c.size_t,                      /* [PSX] slots to reserve at the beginning of gl_pathv */
+		gl_flags:  Glob_Flags,                    /* copy of flags parameter to glob */
+
+		// Non-standard alternate file system access functions:
+
+		gl_closedir: proc "c" (dirp: DIR),
+		gl_readdir:  proc "c" (dirp: DIR) -> ^dirent,
+		gl_opendir:  proc "c" (path: cstring) -> DIR,
+		gl_lstat:    proc "c" (path: cstring, buf: ^stat_t) -> result,
+		gl_stat:     proc "c" (path: cstring, buf: ^stat_t) -> result,
+	}
+
+	GLOB_ERR      :: 1 << 0
+	GLOB_MARK     :: 1 << 1
+	GLOB_NOSORT   :: 1 << 2
+	GLOB_DOOFFS   :: 1 << 3
+	GLOB_NOCHECK  :: 1 << 4
+	GLOB_APPEND   :: 1 << 5
+	GLOB_NOESCAPE :: 1 << 6
+
+	GLOB_NOSPACE :: 1
+	GLOB_ABORTED :: 2
+	GLOB_NOMATCH :: 3
+
 } else {
 	#panic("posix is unimplemented for the current target")
 }

+ 1 - 1
core/sys/posix/grp.odin

@@ -114,7 +114,7 @@ foreign lib {
 	getgrnam_r :: proc(name: cstring, grp: ^group, buffer: [^]byte, bufsize: c.size_t, result: ^^group) -> Errno ---
 }
 
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
 
 	gid_t :: distinct c.uint32_t
 

+ 86 - 2
core/sys/posix/langinfo.odin

@@ -238,7 +238,7 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
 	ABDAY_4 :: 16
 	ABDAY_5 :: 17
 	ABDAY_6 :: 18
-	ABDAY_7 :: 19
+	ABDAY_7 :: 19	
 
 	MON_1  :: 20
 	MON_2  :: 21
@@ -278,7 +278,91 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
 	YESEXPR :: 47
 	NOEXPR  :: 49
 
-	CRNCYSTR :: 50
+	CRNCYSTR :: 50	
+
+} else when ODIN_OS == .Linux {
+
+	// NOTE: declared with `_t` so we can enumerate the real `nl_info`.
+	nl_item_t :: distinct c.int
+
+	// NOTE: All these values are set in an enum on the Linux implementation.
+	// Some depend on locale.h contants (bits/locale.h to be precise).
+
+	// NOTE: ABDAY_1 is set to LC_TIME << 16 (LC_TIME is 2) on the enum group of
+	// the Linux implementation.
+	ABDAY_1 :: 0x20_000
+	ABDAY_2 :: 0x20_001
+	ABDAY_3 :: 0x20_002
+	ABDAY_4 :: 0x20_003
+	ABDAY_5 :: 0x20_004
+	ABDAY_6 :: 0x20_005
+	ABDAY_7 :: 0x20_006
+
+	DAY_1 :: 0x20_007
+	DAY_2 :: 0x20_008
+	DAY_3 :: 0x20_009
+	DAY_4 :: 0x20_00A
+	DAY_5 :: 0x20_00B
+	DAY_6 :: 0x20_00C
+	DAY_7 :: 0x20_00D
+
+	ABMON_1  :: 0x20_00E
+	ABMON_2  :: 0x20_010
+	ABMON_3  :: 0x20_011
+	ABMON_4  :: 0x20_012
+	ABMON_5  :: 0x20_013
+	ABMON_6  :: 0x20_014
+	ABMON_7  :: 0x20_015
+	ABMON_8  :: 0x20_016
+	ABMON_9  :: 0x20_017
+	ABMON_10 :: 0x20_018
+	ABMON_11 :: 0x20_019
+	ABMON_12 :: 0x20_01A
+
+	MON_1  :: 0x20_01B
+	MON_2  :: 0x20_01C
+	MON_3  :: 0x20_01D
+	MON_4  :: 0x20_01E
+	MON_5  :: 0x20_020
+	MON_6  :: 0x20_021
+	MON_7  :: 0x20_022
+	MON_8  :: 0x20_023
+	MON_9  :: 0x20_024
+	MON_10 :: 0x20_025
+	MON_11 :: 0x20_026
+	MON_12 :: 0x20_027
+
+	AM_STR :: 0x20_028
+	PM_STR :: 0x20_029
+
+	D_T_FMT    :: 0x20_02A
+	D_FMT      :: 0x20_02B
+	T_FMT      :: 0x20_02C
+	T_FMT_AMPM :: 0x20_02D
+
+	ERA         :: 0x20_02E
+	ERA_D_FMT   :: 0x20_030
+	ALT_DIGITS  :: 0x20_031
+	ERA_D_T_FMT :: 0x20_032
+	ERA_T_FMT   :: 0x20_033
+
+	// NOTE: CODESET is the 16th member of the enum group starting with value
+	// LC_CTYPE << 16, LC_CTYPE is 0.
+	CODESET :: 0x0F
+
+	// NOTE: CRNCYSTR is the 16th member of the enum group starting with value
+	// LC_MONETARY << 16, LC_MONETARY is 4.
+	CRNCYSTR :: 0x40_00F
+
+	// NOTE: RADIXCHAR is the 1st member of the enum group starting with value
+	// LC_NUMERIC << 16, LC_NUMERIC is 1.
+	RADIXCHAR :: 0x10_000
+	THOUSEP   :: 0x10_001
+
+	// NOTE: YESEXPR is the 1st member of the enum group starting with value
+	// LC_MESSAGES << 16, LC_MESSAGES is 5.
+	YESEXPR :: 0x50_000
+	NOEXPR  :: 0x50_001
 
 } else {
 	#panic("posix is unimplemented for the current target")

+ 95 - 0
core/sys/posix/limits.odin

@@ -454,6 +454,101 @@ when ODIN_OS == .Darwin {
 	NL_TEXTMAX :: 255
 	NZERO      :: 20
 
+} else when ODIN_OS == .Linux {
+
+	// A definition of one of the symbolic constants in the following list shall be omitted from
+	// <limits.h> on specific implementations where the corresponding value is equal to or greater
+	// than the stated minimum, but is unspecified.
+	//
+	// This indetermination might depend on the amount of available memory space on a specific
+	// instance of a specific implementation. The actual value supported by a specific instance shall
+	// be provided by the sysconf() function.
+
+	// AIO_LISTIO_MAX             :: sysconf(._AIO_LISTIO_MAX)
+	// AIO_MAX                    :: sysconf(._AIO_MAX)
+	// AIO_PRIO_DELTA_MAX         :: sysconf(._AIO_PRIO_DELTA_MAX)
+	ARG_MAX                       :: 131_072
+	// ATEXIT_MAX                 :: sysconf(._ATEXIT_MAX)
+	// CHILD_MAX                  :: sysconf(._POSIX_ARG_MAX)
+	// DELAYTIMER_MAX             :: sysconf(._DELAYTIMER_MAX)
+	// HOST_NAME_MAX              :: sysconf(._HOST_NAME_MAX)
+	// IOV_MAX                    :: sysconf(._XOPEN_IOV_MAX)
+	// LOGIN_NAME_MAX             :: sysconf(._LOGIN_NAME_MAX)
+	// MQ_OPEN_MAX                :: sysconf(._MQ_OPEN_MAX)
+	// MQ_PRIO_MAX                :: sysconf(._MQ_PRIO_MAX)
+	// PAGESIZE                   :: PAGE_SIZE
+	// PAGE_SIZE                  :: sysconf(._PAGE_SIZE)
+	PTHREAD_DESTRUCTOR_ITERATIONS :: 4
+	// PTHREAD_KEYS_MAX           :: sysconf(._PTHREAD_KEYS_MAX)
+	// PTHREAD_STACK_MIN          :: sysconf(._PTHREAD_STACK_MIN)
+	// RTSIG_MAX                  :: sysconf(._RTSIG_MAX)
+	// SEM_NSEMS_MAX              :: sysconf(._SEM_NSEMS_MAX)
+	// SEM_VALUE_MAX              :: sysconf(._SEM_VALUE_MAX)
+	// SIGQUEUE_MAX               :: sysconf(._SIGQUEUE_MAX)
+	// SS_REPL_MAX                :: sysconf(._SS_REPL_MAX)
+	// STREAM_MAX                 :: sysconf(._STREAM_MAX)
+	// SYMLOOP_MAX                :: sysconf(._SYSLOOP_MAX)
+	// TIMER_MAX                  :: sysconf(._TIMER_MAX)
+	// TRACE_EVENT_NAME_MAX       :: sysconf(._TRACE_EVENT_NAME_MAX)
+	// TRACE_NAME_MAX             :: sysconf(._TRACE_NAME_MAX)
+	// TRACE_SYS_MAX              :: sysconf(._TRACE_SYS_MAX)
+	// TRACE_USER_EVENT_MAX       :: sysconf(._TRACE_USER_EVENT_MAX)
+	// TTY_NAME_MAX               :: sysconf(._TTY_NAME_MAX)
+	// TZNAME_MAX                 :: sysconf(._TZNAME_MAX)
+
+	// The values in the following list may be constants within an implementation or may vary from
+	// one pathname to another.
+	// For example, file systems or directories may have different characteristics.
+	//
+	// A definition of one of the symbolic constants in the following list shall be omitted from the 
+	// <limits.h> header on specific implementations where the corresponding value is equal to or
+	// greater than the stated minimum, but where the value can vary depending on the file to which
+	// it is applied.
+	// The actual value supported for a specific pathname shall be provided by the pathconf() function.
+
+	// FILESIZEBITS             :: pathconf(".", ._FILESIZEBITS)
+	LINK_MAX                    :: 127
+	MAX_CANON                   :: 255
+	MAX_INPUT                   :: 255
+	NAME_MAX                    :: 255
+	PATH_MAX                    :: 4096
+	PIPE_BUF                    :: 4096
+	// POSIX_ALLOC_SIZE_MIN     :: sysconf(._POSIX_ALLOC_SIZE_MIN)
+	// POSIX_REC_INCR_XFER_SIZE :: sysconf(._POSIX_REC_INCR_XFER_SIZE)
+	// POSIX_REC_MAX_XFER_SIZE  :: sysconf(._POSIX_REC_MAX_XFER_SIZE)
+	// POSIX_REC_MIN_XFER_SIZE  :: sysconf(._POSIX_REC_MIN_XFER_SIZE)
+	// POSIX_REC_XFER_ALIGN     :: sysconf(._POSIX_REC_XFER_ALIGN)
+	// SYMLINK_MAX              :: pathconf(".", ._SYMLINK_MAX)
+
+
+	// The magnitude limitations in the following list shall be fixed by specific implementations.
+	// An application should assume that the value of the symbolic constant defined by <limits.h>
+	// in a specific implementation is the minimum that pertains whenever the application is run
+	// under that implementation.
+	// A specific instance of a specific implementation may increase the value relative to that
+	// supplied by <limits.h> for that implementation.
+	// The actual value supported by a specific instance shall be provided by the sysconf() function.
+
+	BC_BASE_MAX         :: 99
+	BC_DIM_MAX          :: 2048
+	BC_SCALE_MAX        :: 99
+	BC_STRING_MAX       :: 1000
+	CHARCLASS_NAME_MAX  :: 14
+	COLL_WEIGHTS_MAX    :: 2
+	EXPR_NEST_MAX       :: 32
+	// LINE_MAX         :: sysconf(._LINE_MAX)
+	// NGROUPS_MAX      :: sysconf(._NGROUPS_MAX)
+	RE_DUP_MAX          :: 255
+
+	// Other limits.
+
+	NL_ARGMAX  :: 9
+	NL_LANGMAX :: 32 // 14 on glibc, 32 on musl
+	NL_MSGMAX  :: 32_767
+	NL_SETMAX  :: 255
+	NL_TEXTMAX :: 2048 // 255 on glibc, 2048 on musl
+	NZERO      :: 20
+
 } else {
 	#panic("posix is unimplemented for the current target")
 }

+ 38 - 0
core/sys/posix/locale.odin

@@ -88,6 +88,44 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD  || ODIN_OS
 	LC_NUMERIC  :: 4
 	LC_TIME     :: 5
 
+} else when ODIN_OS == .Linux {
+
+	// NOTE: All of these fields are standard ([PSX]).
+	lconv :: struct {
+		decimal_point:      cstring,
+		thousand_sep:       cstring,
+		grouping:           cstring,
+		int_curr_symbol:    cstring,
+		currency_symbol:    cstring,
+		mon_decimal_points: cstring,
+		mon_thousands_sep:  cstring,
+		mon_grouping:       cstring,
+		positive_sign:      cstring,
+		negative_sign:      cstring,
+		int_frac_digits:    c.char,
+		frac_digits:        c.char,
+		p_cs_precedes:      c.char,
+		p_sep_by_space:     c.char,
+		n_cs_precedes:      c.char,
+		n_sep_by_space:     c.char,
+		p_sign_posn:        c.char,
+		n_sign_posn:        c.char,
+		int_p_cs_precedes:  c.char,
+		int_n_cs_precedes:  c.char,
+		int_p_sep_by_space: c.char,
+		int_n_sep_by_space: c.char,
+		int_p_sign_posn:    c.char,
+		int_n_sign_posn:    c.char,
+	}
+
+	LC_CTYPE    :: 0
+	LC_NUMERIC  :: 1
+	LC_TIME     :: 2
+	LC_COLLATE  :: 3
+	LC_MONETARY :: 4
+	LC_MESSAGES :: 5
+	LC_ALL      :: 6
+
 } else {
 	#panic("posix is unimplemented for the current target")
 }

+ 1 - 1
core/sys/posix/net_if.odin

@@ -44,7 +44,7 @@ foreign lib {
 	if_freenameindex :: proc(ptr: ^if_nameindex_t) ---
 }
 
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
 
 	// NOTE: `_t` suffix added due to name conflict.
 

+ 36 - 2
core/sys/posix/netdb.odin

@@ -318,7 +318,7 @@ Info_Errno :: enum c.int {
 	OVERFLOW = EAI_OVERFLOW,
 }
 
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
 
 	hostent :: struct {
 		h_name:      cstring,                /* [PSX] official name of host */
@@ -412,9 +412,28 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 		NI_NUMERICSERV  :: 2
 		NI_NUMERICSCOPE :: 32
 		NI_DGRAM        :: 16
+
+	} else when ODIN_OS == .Linux {
+
+		AI_PASSIVE     :: 0x001
+		AI_CANONNAME   :: 0x002
+		AI_NUMERICHOST :: 0x004
+		AI_NUMERICSERV :: 0x400
+		AI_V4MAPPED    :: 0x008
+		AI_ALL         :: 0x010
+		AI_ADDRCONFIG  :: 0x020
+
+		NI_NOFQDN       :: 4
+		NI_NUMERICHOST  :: 1
+		NI_NAMEREQD     :: 8
+		NI_NUMERICSERV  :: 2
+		NI_NUMERICSCOPE :: 0x100
+		NI_DGRAM        :: 16
+
 	}
 
 	when ODIN_OS == .OpenBSD {
+
 		EAI_AGAIN    :: -3
 		EAI_BADFLAGS :: -1
 		EAI_FAIL     :: -4
@@ -425,7 +444,22 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 		EAI_SOCKTYPE :: -7
 		EAI_SYSTEM   :: -11
 		EAI_OVERFLOW :: -14
+
+	} else when ODIN_OS == .Linux {
+
+		EAI_AGAIN    :: -3
+		EAI_BADFLAGS :: -1
+		EAI_FAIL     :: -4
+		EAI_FAMILY   :: -6
+		EAI_MEMORY   :: -10
+		EAI_NONAME   :: -2
+		EAI_SERVICE  :: -8
+		EAI_SOCKTYPE :: -7
+		EAI_SYSTEM   :: -11
+		EAI_OVERFLOW :: -12
+
 	} else {
+
 		EAI_AGAIN    :: 2
 		EAI_BADFLAGS :: 3
 		EAI_FAIL     :: 4
@@ -438,6 +472,6 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 		EAI_OVERFLOW :: 14
 	}
 
-}else {
+} else {
 	#panic("posix is unimplemented for the current target")
 }

+ 57 - 28
core/sys/posix/netinet_in.odin

@@ -30,7 +30,7 @@ Protocol :: enum c.int {
 	UDP  = IPPROTO_UDP,
 }
 
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
 
 	in_addr :: struct {
 		s_addr: in_addr_t, /* [PSX] big endian address */
@@ -44,26 +44,63 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 		},
 	}
 
-	sockaddr_in :: struct {
-		sin_len:    c.uint8_t,
-		sin_family: sa_family_t, /* [PSX] AF_INET (but a smaller size) */
-		sin_port:   in_port_t,   /* [PSX] port number */
-		sin_addr:   in_addr,     /* [PSX] IP address */
-		sin_zero:   [8]c.char,
-	}
-
-	sockaddr_in6 :: struct {
-		sin6_len:      c.uint8_t,
-		sin6_family:   sa_family_t, /* [PSX] AF_INET6 (but a smaller size) */
-		sin6_port:     in_port_t,   /* [PSX] port number */
-		sin6_flowinfo: c.uint32_t,  /* [PSX] IPv6 traffic class and flow information */
-		sin6_addr:     in6_addr,    /* [PSX] IPv6 address */
-		sin6_scope_id: c.uint32_t,  /* [PSX] set of interfaces for a scope */
-	}
+	when ODIN_OS == .Linux {
+
+		sockaddr_in :: struct {
+			sin_family: sa_family_t, /* [PSX] AF_INET (but a smaller size) */
+			sin_port:   in_port_t,   /* [PSX] port number */
+			sin_addr:   in_addr,     /* [PSX] IP address */
+			sin_zero:   [8]c.char,
+		}
+
+		sockaddr_in6 :: struct {
+			sin6_family:   sa_family_t, /* [PSX] AF_INET6 (but a smaller size) */
+			sin6_port:     in_port_t,   /* [PSX] port number */
+			sin6_flowinfo: u32be,       /* [PSX] IPv6 traffic class and flow information */
+			sin6_addr:     in6_addr,    /* [PSX] IPv6 address */
+			sin6_scope_id: c.uint32_t,  /* [PSX] set of interfaces for a scope */
+		}
+
+		IPV6_MULTICAST_IF   :: 17
+		IPV6_UNICAST_HOPS   :: 16
+		IPV6_MULTICAST_HOPS :: 18
+		IPV6_MULTICAST_LOOP :: 19
+		IPV6_JOIN_GROUP     :: 20
+		IPV6_LEAVE_GROUP    :: 21
+		IPV6_V6ONLY         :: 26
+
+	} else {
+
+		sockaddr_in :: struct {
+			sin_len:    c.uint8_t,
+			sin_family: sa_family_t, /* [PSX] AF_INET (but a smaller size) */
+			sin_port:   in_port_t,   /* [PSX] port number */
+			sin_addr:   in_addr,     /* [PSX] IP address */
+			sin_zero:   [8]c.char,
+		}
+
+		sockaddr_in6 :: struct {
+			sin6_len:      c.uint8_t,
+			sin6_family:   sa_family_t, /* [PSX] AF_INET6 (but a smaller size) */
+			sin6_port:     in_port_t,   /* [PSX] port number */
+			sin6_flowinfo: c.uint32_t,  /* [PSX] IPv6 traffic class and flow information */
+			sin6_addr:     in6_addr,    /* [PSX] IPv6 address */
+			sin6_scope_id: c.uint32_t,  /* [PSX] set of interfaces for a scope */
+		}
+
+		ipv6_mreq :: struct {
+			ipv6mr_multiaddr: in6_addr, /* [PSX] IPv6 multicast address */
+			ipv6mr_interface: c.uint,   /* [PSX] interface index */
+		}
+
+		IPV6_JOIN_GROUP     :: 12
+		IPV6_LEAVE_GROUP    :: 13
+		IPV6_MULTICAST_HOPS :: 10
+		IPV6_MULTICAST_IF   :: 9
+		IPV6_MULTICAST_LOOP :: 11
+		IPV6_UNICAST_HOPS   :: 4
+		IPV6_V6ONLY         :: 27
 
-	ipv6_mreq :: struct {
-		ipv6mr_multiaddr: in6_addr, /* [PSX] IPv6 multicast address */
-		ipv6mr_interface: c.uint,   /* [PSX] interface index */
 	}
 
 	IPPROTO_IP   :: 0
@@ -76,14 +113,6 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 	INADDR_ANY       :: 0x00000000
 	INADDR_BROADCAST :: 0xFFFFFFFF
 
-	IPV6_JOIN_GROUP     :: 12
-	IPV6_LEAVE_GROUP    :: 13
-	IPV6_MULTICAST_HOPS :: 10
-	IPV6_MULTICAST_IF   :: 9
-	IPV6_MULTICAST_LOOP :: 11
-	IPV6_UNICAST_HOPS   :: 4
-	IPV6_V6ONLY         :: 27
-
 	IN6_IS_ADDR_UNSPECIFIED :: #force_inline proc "contextless" (a: in6_addr) -> b32 {
 		return a.s6_addr == 0
 	}

+ 1 - 1
core/sys/posix/netinet_tcp.odin

@@ -2,7 +2,7 @@ package posix
 
 // netinet/tcp.h - definitions for the Internet Transmission Control Protocol (TCP)
 
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
 
 	TCP_NODELAY :: 0x01
 

+ 20 - 0
core/sys/posix/poll.odin

@@ -72,6 +72,26 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 	POLLHUP    :: 0x0010
 	POLLNVAL   :: 0x0020
 
+} else when ODIN_OS == .Linux {
+
+	pollfd :: struct {
+		fd:      FD,         /* [PSX] the following descriptor being polled */
+		events:  Poll_Event, /* [PSX] the input event flags */
+		revents: Poll_Event, /* [PSX] the output event flags */
+	}
+
+	POLLIN     :: 0x0001
+	POLLRDNORM :: 0x0040
+	POLLRDBAND :: 0x0080
+	POLLPRI    :: 0x0002
+	POLLOUT    :: 0x0004
+	POLLWRNORM :: 0x0100
+	POLLWRBAND :: 0x0200
+
+	POLLERR    :: 0x0008
+	POLLHUP    :: 0x0010
+	POLLNVAL   :: 0x0020
+
 } else {
 	#panic("posix is unimplemented for the current target")
 }

+ 3 - 1
core/sys/posix/posix.odin

@@ -1,5 +1,7 @@
 /*
-Bindings for most POSIX APIs.
+Raw bindings for most POSIX APIs.
+
+Targets glibc and musl compatibility.
 
 APIs that have been left out are due to not being useful,
 being fully replaced (and better) by other Odin packages,

+ 44 - 0
core/sys/posix/pthread.odin

@@ -513,6 +513,50 @@ when ODIN_OS == .Darwin {
 		sched_priority: c.int,     /* [PSX] process or thread execution scheduling priority */
 	}
 
+} else when ODIN_OS == .Linux {
+
+	PTHREAD_CANCEL_DEFERRED     :: 0
+	PTHREAD_CANCEL_ASYNCHRONOUS :: 1
+
+	PTHREAD_CANCEL_ENABLE       :: 0
+	PTHREAD_CANCEL_DISABLE      :: 1
+
+	PTHREAD_CANCELED :: rawptr(~uintptr(0))
+
+	PTHREAD_CREATE_JOINABLE :: 0
+	PTHREAD_CREATE_DETACHED :: 1
+
+	PTHREAD_INHERIT_SCHED  :: 0
+	PTHREAD_EXPLICIT_SCHED :: 1
+
+	PTHREAD_PRIO_NONE    :: 0
+	PTHREAD_PRIO_INHERIT :: 1
+	PTHREAD_PRIO_PROTECT :: 2
+
+	PTHREAD_PROCESS_PRIVATE :: 0
+	PTHREAD_PROCESS_SHARED  :: 1
+
+	PTHREAD_SCOPE_SYSTEM    :: 0
+	PTHREAD_SCOPE_PROCESS   :: 1
+
+	pthread_t :: distinct c.ulong
+
+	pthread_attr_t :: struct #raw_union {
+		__size: [56]c.char, // NOTE: may be smaller depending on libc or arch, but never larger.
+		__align: c.long,
+	}
+
+	pthread_key_t :: distinct c.uint
+
+	sched_param :: struct {
+		sched_priority: c.int,     /* [PSX] process or thread execution scheduling priority */
+
+		// NOTE: may be smaller depending on libc or arch, but never larger.
+		__reserved1: c.int,
+		__reserved2: [4]c.long,
+		__reserved3: c.int,
+	}
+
 } else {
 	#panic("posix is unimplemented for the current target")
 }

+ 1 - 1
core/sys/posix/sched.odin

@@ -94,7 +94,7 @@ when ODIN_OS == .Darwin {
 	SCHED_RR       :: 3
 	SCHED_OTHER    :: 2
 
-} else when ODIN_OS == .NetBSD {
+} else when ODIN_OS == .NetBSD || ODIN_OS == .Linux {
 
 	SCHED_OTHER    :: 0
 	SCHED_FIFO     :: 1

+ 174 - 15
core/sys/posix/signal.odin

@@ -480,11 +480,6 @@ when ODIN_OS == .Darwin {
 	uid_t :: distinct c.uint32_t
 	sigset_t :: distinct c.uint32_t
 
-	// MOTE: unimplemented on darwin.
-	//
-	// SIGRTMIN :: 
-	// SIGRTMAX ::
-
 	SIGHUP    :: 1
 	SIGQUIT   :: 3
 	SIGTRAP   :: 5
@@ -625,11 +620,6 @@ when ODIN_OS == .Darwin {
 		__bits: [4]c.uint32_t,
 	}
 
-	// MOTE: unimplemented on darwin.
-	//
-	// SIGRTMIN :: 65
-	// SIGRTMAX :: 126
-
 	SIGHUP    :: 1
 	SIGQUIT   :: 3
 	SIGTRAP   :: 5
@@ -794,11 +784,6 @@ when ODIN_OS == .Darwin {
 		__bits: [4]c.uint32_t,
 	}
 
-	// MOTE: unimplemented on darwin.
-	//
-	// SIGRTMIN :: 33
-	// SIGRTMAX :: 63
-
 	SIGHUP    :: 1
 	SIGQUIT   :: 3
 	SIGTRAP   :: 5
@@ -1126,6 +1111,180 @@ when ODIN_OS == .Darwin {
 	SI_ASYNCIO :: -4 // NOTE: not implemented
 	SI_MESGQ   :: -5 // NOTE: not implemented
 
+} else when ODIN_OS == .Linux {
+
+	// Request that signal be held
+	SIG_HOLD :: rawptr(uintptr(2))
+
+	uid_t :: distinct c.uint32_t
+	sigset_t :: struct {
+		[1024/(8 * size_of(c.ulong))]val,
+	}
+
+	SIGHUP    :: 1
+	SIGQUIT   :: 3
+	SIGTRAP   :: 5
+	SIGBUS    :: 7
+	SIGKILL   :: 9
+	SIGUSR1   :: 10
+	SIGUSR2   :: 12
+	SIGPIPE   :: 13
+	SIGALRM   :: 14
+	SIGCHLD   :: 17
+	SIGCONT   :: 18
+	SIGSTOP   :: 19
+	SIGTSTP   :: 20
+	SIGTTIN   :: 21
+	SIGTTOU   :: 22
+	SIGURG    :: 23
+	SIGXCPU   :: 24
+	SIGXFSZ   :: 25
+	SIGVTALRM :: 26
+	SIGPROF   :: 27
+	SIGPOLL   :: 29
+	SIGSYS    :: 31
+
+	// NOTE: this is actually defined as `sigaction`, but due to the function with the same name
+	// `_t` has been added.
+
+	sigaction_t :: struct {
+		using _: struct #raw_union {
+			sa_handler:   proc "c" (Signal),                     /* [PSX] signal-catching function or one of the SIG_IGN or SIG_DFL */
+			sa_sigaction: proc "c" (Signal, ^siginfo_t, rawptr), /* [PSX] signal-catching function */
+		},
+		sa_mask:     sigset_t, /* [PSX] set of signals to be blocked during execution of the signal handling function */
+		sa_flags:    SA_Flags, /* [PSX] special flags */
+		sa_restorer: proc "c" (),
+	}
+
+	SIG_BLOCK   :: 0
+	SIG_UNBLOCK :: 1
+	SIG_SETMASK :: 2
+
+	SA_NOCLDSTOP :: 1
+	SA_NOCLDWAIT :: 2
+	SA_SIGINFO   :: 4
+	SA_ONSTACK   :: 0x08000000
+	SA_RESTART   :: 0x10000000
+	SA_NODEFER   :: 0x40000000
+	SA_RESETHAND :: 0x80000000
+
+	SS_ONSTACK :: 1
+	SS_DISABLE :: 2
+
+	when ODIN_ARCH == .arm64 {
+		MINSIGSTKSZ :: 6144
+		SIGSTKSZ    :: 12288
+	} else {
+		MINSIGSTKSZ :: 2048
+		SIGSTKSZ    :: 8192
+	}
+
+	stack_t :: struct {
+		ss_sp:    rawptr,   /* [PSX] stack base or pointer */
+		ss_flags: SS_Flags, /* [PSX] flags */
+		ss_size:  c.size_t, /* [PSX] stack size */
+	}
+
+	@(private)
+	__SI_MAX_SIZE :: 128
+
+	when size_of(int) == 8 { 
+		@(private)
+		_pad0 :: struct {
+			_pad0: c.int,
+		}
+		@(private)
+		__SI_PAD_SIZE :: (__SI_MAX_SIZE / size_of(c.int)) - 4
+
+	} else {
+		@(private)
+		_pad0 :: struct {}
+		@(private)
+		__SI_PAD_SIZE :: (__SI_MAX_SIZE / size_of(c.int)) - 3
+	}
+
+	siginfo_t :: struct #align(8) {
+		si_signo:  Signal, /* [PSX] signal number */
+		si_errno:  Errno,  /* [PSX] errno value associated with this signal */
+		si_code: struct #raw_union { /* [PSX] specific more detailed codes per signal */
+			ill:  ILL_Code,
+			fpe:  FPE_Code,
+			segv: SEGV_Code,
+			bus:  BUS_Code,
+			trap: TRAP_Code,
+			chld: CLD_Code,
+			poll: POLL_Code,
+			any:  Any_Code,
+		},
+		__pad0: _pad0,
+		using _sifields: struct #raw_union {
+			_pad: [__SI_PAD_SIZE]c.int,
+
+			using _: struct {
+				si_pid: pid_t, /* [PSX] sending process ID */
+				si_uid: uid_t, /* [PSX] real user ID of sending process */
+				using _: struct #raw_union {
+					si_status: c.int,  /* [PSX] exit value or signal */
+					si_value:  sigval, /* [PSX] signal value */
+				},
+			},
+			using _: struct {
+				si_addr: rawptr, /* [PSX] address of faulting instruction */
+			},
+			using _: struct {
+				si_band: c.long, /* [PSX] band event for SIGPOLL */
+			},
+		},
+	}
+
+	ILL_ILLOPC :: 1
+	ILL_ILLOPN :: 2
+	ILL_ILLADR :: 3
+	ILL_ILLTRP :: 4
+	ILL_PRVOPC :: 5
+	ILL_PRVREG :: 6
+	ILL_COPROC :: 7
+	ILL_BADSTK :: 8
+
+	FPE_INTDIV :: 1
+	FPE_INTOVF :: 2
+	FPE_FLTDIV :: 3
+	FPE_FLTOVF :: 4
+	FPE_FLTUND :: 5
+	FPE_FLTRES :: 6
+	FPE_FLTINV :: 7
+	FPE_FLTSUB :: 8
+
+	SEGV_MAPERR :: 1
+	SEGV_ACCERR :: 2
+
+	BUS_ADRALN :: 1
+	BUS_ADRERR :: 2
+	BUS_OBJERR :: 3
+
+	TRAP_BRKPT :: 1
+	TRAP_TRACE :: 2
+
+	CLD_EXITED    :: 1
+	CLD_KILLED    :: 2
+	CLD_DUMPED    :: 3
+	CLD_TRAPPED   :: 4
+	CLD_STOPPED   :: 5
+	CLD_CONTINUED :: 6
+
+	POLL_IN  :: 1
+	POLL_OUT :: 2
+	POLL_MSG :: 3
+	POLL_ERR :: 4
+	POLL_PRI :: 5
+	POLL_HUP :: 6
+
+	SI_USER    :: 0
+	SI_QUEUE   :: -1
+	SI_TIMER   :: -2
+	SI_MESGQ   :: -3
+	SI_ASYNCIO :: -4
 } else {
 	#panic("posix is unimplemented for the current target")
 }

+ 26 - 0
core/sys/posix/sys_ipc.odin

@@ -84,6 +84,32 @@ when ODIN_OS == .Darwin {
 	IPC_SET  :: 1
 	IPC_STAT :: 2
 
+} else when ODIN_OS == .Linux {
+
+	key_t :: distinct c.int32_t
+
+	ipc_perm :: struct {
+		__ipc_perm_key: key_t,
+		uid:            uid_t,     /* [PSX] owner's user ID */
+		gid:            gid_t,     /* [PSX] owner's group ID */
+		cuid:           uid_t,     /* [PSX] creator's user ID */
+		cgid:           gid_t,     /* [PSX] creator's group ID */
+		mode:           mode_t,    /* [PSX] read/write perms */
+		__ipc_perm_seq: c.int,
+		__pad1:         c.long,
+		__pad2:         c.long,
+	}
+
+	IPC_CREAT  :: 0o01000
+	IPC_EXCL   :: 0o02000
+	IPC_NOWAIT :: 0o04000
+
+	IPC_PRIVATE :: key_t(0)
+
+	IPC_RMID :: 0
+	IPC_SET  :: 1
+	IPC_STAT :: 2
+
 } else {
 	#panic("posix is unimplemented for the current target")
 }

+ 3 - 2
core/sys/posix/sys_mman.odin

@@ -163,7 +163,7 @@ when ODIN_OS == .NetBSD {
 	@(private) LMSYNC :: "msync"
 }
 
-when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
 
 	PROT_EXEC   :: 0x04
 	_PROT_NONE  :: 0x00
@@ -174,7 +174,7 @@ when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
 	MAP_PRIVATE :: 0x0002
 	MAP_SHARED  :: 0x0001
 
-	when ODIN_OS == .Darwin {
+	when ODIN_OS == .Darwin || ODIN_OS == .Linux {
 		MS_INVALIDATE :: 0x0002
 		_MS_SYNC      :: 0x0010
 	} else when ODIN_OS == .NetBSD {
@@ -184,6 +184,7 @@ when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
 		MS_INVALIDATE :: 0x0004
 		_MS_SYNC      :: 0x0002
 	}
+
 	MS_ASYNC :: 0x0001
 	MS_SYNC  :: Sync_Flags{Sync_Flags_Bits(log2(_MS_SYNC))}
 

+ 11 - 4
core/sys/posix/sys_resource.odin

@@ -95,7 +95,7 @@ when ODIN_OS == .NetBSD {
 	@(private) LGETRUSAGE :: "getrusage"
 }
 
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
 
 	PRIO_PROCESS :: 0
 	PRIO_PGRP    :: 1
@@ -103,7 +103,7 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 
 	rlim_t :: distinct c.uint64_t
 
-	RLIM_INFINITY  :: (rlim_t(1) << 63) - 1
+	RLIM_INFINITY  :: ~rlim_t(0) when ODIN_OS == .Linux else (rlim_t(1) << 63) - 1
 	RLIM_SAVED_MAX :: RLIM_INFINITY
 	RLIM_SAVED_CUR :: RLIM_INFINITY
 
@@ -143,9 +143,16 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 	RLIMIT_CPU    :: 0
 	RLIMIT_DATA   :: 2
 	RLIMIT_FSIZE  :: 1
-	RLIMIT_NOFILE :: 8
+	RLIMIT_NOFILE :: 7 when ODIN_OS == .Linux else 8
 	RLIMIT_STACK  :: 3
-	RLIMIT_AS     :: 5 when ODIN_OS == .Darwin || ODIN_OS == .OpenBSD else 10
+
+	when ODIN_OS == .Linux {
+		RLIMIT_AS :: 9
+	} else when ODIN_OS == .Darwin || ODIN_OS == .OpenBSD {
+		RLIMIT_AS :: 5
+	} else {
+		RLIMIT_AS :: 10
+	}
 
 } else {
 	#panic("posix is unimplemented for the current target")

+ 1 - 1
core/sys/posix/sys_select.odin

@@ -55,7 +55,7 @@ when ODIN_OS == .NetBSD {
 	LSELECT  :: "select"
 }
 
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
 
 	suseconds_t :: distinct (c.int32_t when ODIN_OS == .Darwin || ODIN_OS == .NetBSD else c.long)
 

+ 30 - 0
core/sys/posix/sys_sem.odin

@@ -123,6 +123,36 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 		sem_flg: c.short,  /* [PSX] operation flags */
 	}
 
+} else when ODIN_OS == .Linux {
+
+	SEM_UNDO :: 0x1000 // undo the operation on exit
+
+	// Commands for `semctl'.
+	GETPID  :: 11
+	GETVAL  :: 12
+	GETALL  :: 13
+	GETNCNT :: 14
+	GETZCNT :: 15
+	SETVAL  :: 16
+	SETALL  :: 17
+
+	semid_ds :: struct {
+		sem_perm:  ipc_perm,  // [PSX] operation permission structure
+		sem_otime: time_t,    // [PSX] last semop()
+		__sem_otime_high: c.ulong,
+		sem_ctime: time_t,    // [PSX] last time changed by semctl()
+		__sem_ctime_high: c.ulong,
+		sem_nsems: c.ulong, // [PSX] number of semaphores in set
+		__glibc_reserved3: c.ulong,
+		__glibc_reserved4: c.ulong,
+	}
+
+	sembuf :: struct {
+		sem_num: c.ushort, /* [PSX] semaphore number */
+		sem_op:  c.short,  /* [PSX] semaphore operation */
+		sem_flg: c.short,  /* [PSX] operation flags */
+	}
+
 } else {
 	#panic("posix is unimplemented for the current target")
 }

+ 142 - 71
core/sys/posix/sys_socket.odin

@@ -321,16 +321,23 @@ when ODIN_OS == .NetBSD {
 	@(private) LSOCKET :: "socket"
 }
 
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
 
 	socklen_t :: distinct c.uint
 
 	_sa_family_t :: distinct c.uint8_t
 
-	sockaddr :: struct {
-		sa_len:    c.uint8_t,   /* total length */
-		sa_family: sa_family_t, /* [PSX] address family */
-		sa_data:   [14]c.char,  /* [PSX] socket address */
+	when ODIN_OS == .Linux {
+		sockaddr :: struct {
+			sa_family: sa_family_t, /* [PSX] address family */
+			sa_data:   [14]c.char,  /* [PSX] socket address */
+		}
+	} else {
+		sockaddr :: struct {
+			sa_len:    c.uint8_t,   /* total length */
+			sa_family: sa_family_t, /* [PSX] address family */
+			sa_data:   [14]c.char,  /* [PSX] socket address */
+		}
 	}
 
 
@@ -339,6 +346,11 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 		_SS_PAD1SIZE :: 6
 		@(private)
 		_SS_PAD2SIZE :: 240
+	} else when ODIN_OS == .Linux {
+		@(private)
+		_SS_SIZE :: 128
+		@(private)
+		_SS_PADSIZE :: _SS_SIZE - size_of(c.uint16_t) - size_of(c.uint64_t)
 	} else {
 		@(private)
 		_SS_MAXSIZE   :: 128
@@ -350,28 +362,52 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 		_SS_PAD2SIZE  :: _SS_MAXSIZE - size_of(c.uint8_t) - size_of(sa_family_t) - _SS_PAD1SIZE - _SS_ALIGNSIZE
 	}
 
-	sockaddr_storage :: struct {
-		ss_len:     c.uint8_t,            /* address length */
-		ss_family:  sa_family_t,          /* [PSX] address family */
-		__ss_pad1:  [_SS_PAD1SIZE]c.char,
-		__ss_align: c.int64_t,            /* force structure storage alignment */
-		__ss_pad2:  [_SS_PAD2SIZE]c.char,
-	}
+	when ODIN_OS == .Linux {
+		sockaddr_storage :: struct {
+			ss_family:    sa_family_t,          /* [PSX] address family */
+			__ss_padding: [_SS_PADSIZE]c.char,
+			__ss_align:   c.uint64_t,           /* force structure storage alignment */
+		}
 
-	msghdr :: struct {
-		msg_name:       rawptr,    /* [PSX] optional address */
-		msg_namelen:    socklen_t, /* [PSX] size of address */
-		msg_iov:        [^]iovec,  /* [PSX] scatter/gather array */
-		msg_iovlen:     c.int,     /* [PSX] members in msg_iov */
-		msg_control:    rawptr,    /* [PSX] ancillary data */
-		msg_controllen: socklen_t, /* [PSX] ancillary data buffer length */
-		msg_flags:      Msg_Flags, /* [PSX] flags on received message */
-	}
+		msghdr :: struct {
+			msg_name:       rawptr,    /* [PSX] optional address */
+			msg_namelen:    socklen_t, /* [PSX] size of address */
+			msg_iov:        [^]iovec,  /* [PSX] scatter/gather array */
+			msg_iovlen:     c.size_t,  /* [PSX] members in msg_iov */
+			msg_control:    rawptr,    /* [PSX] ancillary data */
+			msg_controllen: c.size_t,  /* [PSX] ancillary data buffer length */
+			msg_flags:      Msg_Flags, /* [PSX] flags on received message */
+		}
 
-	cmsghdr :: struct {
-		cmsg_len:   socklen_t, /* [PSX] data byte count, including cmsghdr */
-		cmsg_level: c.int,     /* [PSX] originating protocol */
-		cmsg_type:  c.int,     /* [PSX] protocol-specific type */
+		cmsghdr :: struct {
+			cmsg_len:   c.size_t, /* [PSX] data byte count, including cmsghdr */
+			cmsg_level: c.int,     /* [PSX] originating protocol */
+			cmsg_type:  c.int,     /* [PSX] protocol-specific type */
+		}
+	} else {
+		sockaddr_storage :: struct {
+			ss_len:     c.uint8_t,            /* address length */
+			ss_family:  sa_family_t,          /* [PSX] address family */
+			__ss_pad1:  [_SS_PAD1SIZE]c.char,
+			__ss_align: c.int64_t,            /* force structure storage alignment */
+			__ss_pad2:  [_SS_PAD2SIZE]c.char,
+		}
+
+		msghdr :: struct {
+			msg_name:       rawptr,    /* [PSX] optional address */
+			msg_namelen:    socklen_t, /* [PSX] size of address */
+			msg_iov:        [^]iovec,  /* [PSX] scatter/gather array */
+			msg_iovlen:     c.int,     /* [PSX] members in msg_iov */
+			msg_control:    rawptr,    /* [PSX] ancillary data */
+			msg_controllen: socklen_t, /* [PSX] ancillary data buffer length */
+			msg_flags:      Msg_Flags, /* [PSX] flags on received message */
+		}
+
+		cmsghdr :: struct {
+			cmsg_len:   socklen_t, /* [PSX] data byte count, including cmsghdr */
+			cmsg_level: c.int,     /* [PSX] originating protocol */
+			cmsg_type:  c.int,     /* [PSX] protocol-specific type */
+		}
 	}
 
 	SCM_RIGHTS :: 0x01
@@ -421,57 +457,90 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 	SOCK_STREAM    :: 1
 
 	// Options to be accessed at socket level, not protocol level.
-	SOL_SOCKET :: 0xffff
-
-	SO_ACCEPTCONN :: 0x0002
-	SO_BROADCAST  :: 0x0020
-	SO_DEBUG      :: 0x0001
-	SO_DONTROUTE  :: 0x0010
-	SO_ERROR      :: 0x1007
-	SO_KEEPALIVE  :: 0x0008
-	SO_OOBINLINE  :: 0x0100
-	SO_RCVBUF     :: 0x1002
-	SO_RCVLOWAT   :: 0x1004
-	SO_REUSEADDR  :: 0x0004
-	SO_SNDBUF     :: 0x1001
-	SO_SNDLOWAT   :: 0x1003
-	SO_TYPE       :: 0x1008
-
-	when ODIN_OS == .Darwin {
-		SO_LINGER   :: 0x1080
-		SO_RCVTIMEO :: 0x1006
-		SO_SNDTIMEO :: 0x1005
-	} else when ODIN_OS == .FreeBSD {
-		SO_LINGER   :: 0x0080
-		SO_RCVTIMEO :: 0x1006
-		SO_SNDTIMEO :: 0x1005
-	} else when ODIN_OS == .NetBSD {
-		SO_LINGER   :: 0x0080
-		SO_RCVTIMEO :: 0x100c
-		SO_SNDTIMEO :: 0x100b
-	} else when ODIN_OS == .OpenBSD {
-		SO_LINGER   :: 0x0080
-		SO_RCVTIMEO :: 0x1006
-		SO_SNDTIMEO :: 0x1005
+	when ODIN_OS == .Linux {
+		SOL_SOCKET :: 1
+
+		SO_ACCEPTCONN :: 30
+		SO_BROADCAST  :: 6
+		SO_DEBUG      :: 1
+		SO_DONTROUTE  :: 5
+		SO_ERROR      :: 4
+		SO_KEEPALIVE  :: 9
+		SO_OOBINLINE  :: 10
+		SO_RCVBUF     :: 8
+		SO_RCVLOWAT   :: 18
+		SO_REUSEADDR  :: 2
+		SO_SNDBUF     :: 7
+		SO_SNDLOWAT   :: 19
+		SO_TYPE       :: 3
+		SO_LINGER     :: 13
+
+		SO_RCVTIMEO   :: 66
+		SO_SNDTIMEO   :: 67
+	} else {
+		SOL_SOCKET :: 0xffff
+
+		SO_ACCEPTCONN :: 0x0002
+		SO_BROADCAST  :: 0x0020
+		SO_DEBUG      :: 0x0001
+		SO_DONTROUTE  :: 0x0010
+		SO_ERROR      :: 0x1007
+		SO_KEEPALIVE  :: 0x0008
+		SO_OOBINLINE  :: 0x0100
+		SO_RCVBUF     :: 0x1002
+		SO_RCVLOWAT   :: 0x1004
+		SO_REUSEADDR  :: 0x0004
+		SO_SNDBUF     :: 0x1001
+		SO_SNDLOWAT   :: 0x1003
+		SO_TYPE       :: 0x1008
+
+		when ODIN_OS == .Darwin {
+			SO_LINGER   :: 0x1080
+			SO_RCVTIMEO :: 0x1006
+			SO_SNDTIMEO :: 0x1005
+		} else when ODIN_OS == .FreeBSD {
+			SO_LINGER   :: 0x0080
+			SO_RCVTIMEO :: 0x1006
+			SO_SNDTIMEO :: 0x1005
+		} else when ODIN_OS == .NetBSD {
+			SO_LINGER   :: 0x0080
+			SO_RCVTIMEO :: 0x100c
+			SO_SNDTIMEO :: 0x100b
+		} else when ODIN_OS == .OpenBSD {
+			SO_LINGER   :: 0x0080
+			SO_RCVTIMEO :: 0x1006
+			SO_SNDTIMEO :: 0x1005
+		}
 	}
 
 	// The maximum backlog queue length for listen().
 	SOMAXCONN :: 128
 
-	MSG_CTRUNC    :: 0x20
-	MSG_DONTROUTE :: 0x4
-	MSG_EOR       :: 0x8
-	MSG_OOB       :: 0x1
-	MSG_PEEK      :: 0x2
-	MSG_TRUNC     :: 0x10
-	MSG_WAITALL   :: 0x40
-
-	when ODIN_OS == .Darwin {
-		MSG_NOSIGNAL :: 0x80000
-	} else when ODIN_OS == .FreeBSD {
-		MSG_NOSIGNAL :: 0x00020000
-	} else when ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
-		MSG_NOSIGNAL :: 0x0400
+	when ODIN_OS == .Linux {
+		MSG_CTRUNC    :: 0x008
+		MSG_DONTROUTE :: 0x004
+		MSG_EOR       :: 0x080
+		MSG_OOB       :: 0x001
+		MSG_PEEK      :: 0x002
+		MSG_TRUNC     :: 0x020
+		MSG_WAITALL   :: 0x100
+		MSG_NOSIGNAL  :: 0x4000
+	} else {
+		MSG_CTRUNC    :: 0x20
+		MSG_DONTROUTE :: 0x4
+		MSG_EOR       :: 0x8
+		MSG_OOB       :: 0x1
+		MSG_PEEK      :: 0x2
+		MSG_TRUNC     :: 0x10
+		MSG_WAITALL   :: 0x40
+
+		when ODIN_OS == .Darwin {
+			MSG_NOSIGNAL :: 0x80000
+		} else when ODIN_OS == .FreeBSD {
+			MSG_NOSIGNAL :: 0x00020000
+		} else when ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+			MSG_NOSIGNAL :: 0x0400
+		}
 	}
 
 	AF_INET   :: 2
@@ -483,6 +552,8 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 		AF_INET6 :: 28
 	} else when ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
 		AF_INET6 :: 24
+	} else when ODIN_OS == .Linux {
+		AF_INET6 :: 10
 	}
 
 	SHUT_RD   :: 0

+ 1 - 1
core/sys/posix/sys_time.odin

@@ -66,7 +66,7 @@ when ODIN_OS == .NetBSD {
 	@(private) LUTIMES       :: "utimes"
 }
 
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
 
 	itimerval :: struct {
 		it_interval: timeval, /* [PSX] timer interval */

+ 1 - 1
core/sys/posix/sys_times.odin

@@ -24,7 +24,7 @@ when ODIN_OS == .NetBSD {
 	@(private) LTIMES :: "times"
 }
 
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
 
 	tms :: struct {
 		tms_utime:  clock_t, /* [PSX] user CPU time */

+ 1 - 1
core/sys/posix/sys_uio.odin

@@ -30,7 +30,7 @@ foreign libc {
 	writev :: proc(fildes: FD, iov: [^]iovec, iovcnt: c.int) -> c.ssize_t ---
 }
 
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
 
 	iovec :: struct {
 		iov_base: rawptr,   /* [PSX] base address of I/O memory region */

+ 7 - 0
core/sys/posix/sys_un.odin

@@ -12,6 +12,13 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 		sun_path:   [104]c.char, /* [PSX] socket pathname */
 	}
 
+} else when ODIN_OS == .Linux {
+
+	sockaddr_un :: struct {
+		sun_family: sa_family_t, /* [PSX] address family */
+		sun_path:   [108]c.char, /* [PSX] socket pathname */
+	}
+
 } else {
 	#panic("posix is unimplemented for the current target")
 }

+ 9 - 4
core/sys/posix/sys_utsname.odin

@@ -37,10 +37,15 @@ foreign lib {
 	uname :: proc(uname: ^utsname) -> c.int ---
 }
 
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
-
-	@(private)
-	_SYS_NAMELEN :: 256
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
+
+	when ODIN_OS == .Linux {
+		@(private)
+		_SYS_NAMELEN :: 65
+	} else {
+		@(private)
+		_SYS_NAMELEN :: 256
+	}
 
 	utsname :: struct {
 		sysname:  [_SYS_NAMELEN]c.char `fmt:"s,0"`, /* [PSX] name of OS */

+ 1 - 1
core/sys/posix/ulimit.odin

@@ -31,7 +31,7 @@ Ulimit_Cmd :: enum c.int {
 	SETFSIZE = UL_SETFSIZE,
 }
 
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD  || ODIN_OS == .Linux {
 
 	UL_GETFSIZE :: 1
 	UL_SETFSIZE :: 2

+ 243 - 57
core/sys/posix/unistd.odin

@@ -1181,20 +1181,20 @@ when ODIN_OS == .Darwin {
 	F_TLOCK :: 2
 	F_ULOCK :: 0
 
-	_CS_PATH                            :: 1
-	_CS_POSIX_V6_ILP32_OFF32_CFLAGS		:: 2
-	_CS_POSIX_V6_ILP32_OFF32_LDFLAGS	:: 3
-	_CS_POSIX_V6_ILP32_OFF32_LIBS		:: 4
-	_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS	:: 5
-	_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS	:: 6
-	_CS_POSIX_V6_ILP32_OFFBIG_LIBS		:: 7
-	_CS_POSIX_V6_LP64_OFF64_CFLAGS		:: 8
-	_CS_POSIX_V6_LP64_OFF64_LDFLAGS		:: 9
-	_CS_POSIX_V6_LP64_OFF64_LIBS		:: 10
-	_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS	:: 11
-	_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS	:: 12
-	_CS_POSIX_V6_LPBIG_OFFBIG_LIBS		:: 13
-	_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS	:: 14
+	_CS_PATH                           :: 1
+	_CS_POSIX_V6_ILP32_OFF32_CFLAGS    :: 2
+	_CS_POSIX_V6_ILP32_OFF32_LDFLAGS   :: 3
+	_CS_POSIX_V6_ILP32_OFF32_LIBS      :: 4
+	_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS   :: 5
+	_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS  :: 6
+	_CS_POSIX_V6_ILP32_OFFBIG_LIBS     :: 7
+	_CS_POSIX_V6_LP64_OFF64_CFLAGS     :: 8
+	_CS_POSIX_V6_LP64_OFF64_LDFLAGS    :: 9
+	_CS_POSIX_V6_LP64_OFF64_LIBS       :: 10
+	_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS   :: 11
+	_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS  :: 12
+	_CS_POSIX_V6_LPBIG_OFFBIG_LIBS     :: 13
+	_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS :: 14
 
 	_PC_LINK_MAX           :: 1
 	_PC_MAX_CANON          :: 2
@@ -1362,20 +1362,20 @@ when ODIN_OS == .Darwin {
 	F_TLOCK :: 2
 	F_ULOCK :: 0
 
-	_CS_PATH                            :: 1
-	_CS_POSIX_V6_ILP32_OFF32_CFLAGS		:: 2
-	_CS_POSIX_V6_ILP32_OFF32_LDFLAGS	:: 3
-	_CS_POSIX_V6_ILP32_OFF32_LIBS		:: 4
-	_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS	:: 5
-	_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS	:: 6
-	_CS_POSIX_V6_ILP32_OFFBIG_LIBS		:: 7
-	_CS_POSIX_V6_LP64_OFF64_CFLAGS		:: 8
-	_CS_POSIX_V6_LP64_OFF64_LDFLAGS		:: 9
-	_CS_POSIX_V6_LP64_OFF64_LIBS		:: 10
-	_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS	:: 11
-	_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS	:: 12
-	_CS_POSIX_V6_LPBIG_OFFBIG_LIBS		:: 13
-	_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS	:: 14
+	_CS_PATH                           :: 1
+	_CS_POSIX_V6_ILP32_OFF32_CFLAGS    :: 2
+	_CS_POSIX_V6_ILP32_OFF32_LDFLAGS   :: 3
+	_CS_POSIX_V6_ILP32_OFF32_LIBS      :: 4
+	_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS   :: 5
+	_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS  :: 6
+	_CS_POSIX_V6_ILP32_OFFBIG_LIBS     :: 7
+	_CS_POSIX_V6_LP64_OFF64_CFLAGS     :: 8
+	_CS_POSIX_V6_LP64_OFF64_LDFLAGS    :: 9
+	_CS_POSIX_V6_LP64_OFF64_LIBS       :: 10
+	_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS   :: 11
+	_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS  :: 12
+	_CS_POSIX_V6_LPBIG_OFFBIG_LIBS     :: 13
+	_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS :: 14
 
 	_PC_LINK_MAX           :: 1
 	_PC_MAX_CANON          :: 2
@@ -1543,20 +1543,20 @@ when ODIN_OS == .Darwin {
 	F_TLOCK :: 2
 	F_ULOCK :: 0
 
-	_CS_PATH                            :: 1
-	_CS_POSIX_V6_ILP32_OFF32_CFLAGS		:: 2
-	_CS_POSIX_V6_ILP32_OFF32_LDFLAGS	:: 3
-	_CS_POSIX_V6_ILP32_OFF32_LIBS		:: 4
-	_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS	:: 5
-	_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS	:: 6
-	_CS_POSIX_V6_ILP32_OFFBIG_LIBS		:: 7
-	_CS_POSIX_V6_LP64_OFF64_CFLAGS		:: 8
-	_CS_POSIX_V6_LP64_OFF64_LDFLAGS		:: 9
-	_CS_POSIX_V6_LP64_OFF64_LIBS		:: 10
-	_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS	:: 11
-	_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS	:: 12
-	_CS_POSIX_V6_LPBIG_OFFBIG_LIBS		:: 13
-	_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS	:: 14
+	_CS_PATH                           :: 1
+	_CS_POSIX_V6_ILP32_OFF32_CFLAGS    :: 2
+	_CS_POSIX_V6_ILP32_OFF32_LDFLAGS   :: 3
+	_CS_POSIX_V6_ILP32_OFF32_LIBS      :: 4
+	_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS   :: 5
+	_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS  :: 6
+	_CS_POSIX_V6_ILP32_OFFBIG_LIBS     :: 7
+	_CS_POSIX_V6_LP64_OFF64_CFLAGS     :: 8
+	_CS_POSIX_V6_LP64_OFF64_LDFLAGS    :: 9
+	_CS_POSIX_V6_LP64_OFF64_LIBS       :: 10
+	_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS   :: 11
+	_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS  :: 12
+	_CS_POSIX_V6_LPBIG_OFFBIG_LIBS     :: 13
+	_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS :: 14
 
 	_PC_LINK_MAX           :: 1
 	_PC_MAX_CANON          :: 2
@@ -1655,7 +1655,6 @@ when ODIN_OS == .Darwin {
 	_SC_TTY_NAME_MAX                 :: 68
 	_SC_HOST_NAME_MAX                :: 69
 
-	_SC_PASS_MAX                     :: 70
 	_SC_REGEXP                       :: 71
 	_SC_SHELL                        :: 72
 	_SC_SYMLOOP_MAX                  :: 73
@@ -1729,20 +1728,20 @@ when ODIN_OS == .Darwin {
 	F_TLOCK :: 2
 	F_ULOCK :: 0
 
-	_CS_PATH                            :: 1
-	_CS_POSIX_V6_ILP32_OFF32_CFLAGS		:: 2
-	_CS_POSIX_V6_ILP32_OFF32_LDFLAGS	:: 3
-	_CS_POSIX_V6_ILP32_OFF32_LIBS		:: 4
-	_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS	:: 5
-	_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS	:: 6
-	_CS_POSIX_V6_ILP32_OFFBIG_LIBS		:: 7
-	_CS_POSIX_V6_LP64_OFF64_CFLAGS		:: 8
-	_CS_POSIX_V6_LP64_OFF64_LDFLAGS		:: 9
-	_CS_POSIX_V6_LP64_OFF64_LIBS		:: 10
-	_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS	:: 11
-	_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS	:: 12
-	_CS_POSIX_V6_LPBIG_OFFBIG_LIBS		:: 13
-	_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS	:: 14
+	_CS_PATH                           :: 1
+	_CS_POSIX_V6_ILP32_OFF32_CFLAGS    :: 2
+	_CS_POSIX_V6_ILP32_OFF32_LDFLAGS   :: 3
+	_CS_POSIX_V6_ILP32_OFF32_LIBS      :: 4
+	_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS   :: 5
+	_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS  :: 6
+	_CS_POSIX_V6_ILP32_OFFBIG_LIBS     :: 7
+	_CS_POSIX_V6_LP64_OFF64_CFLAGS     :: 8
+	_CS_POSIX_V6_LP64_OFF64_LDFLAGS    :: 9
+	_CS_POSIX_V6_LP64_OFF64_LIBS       :: 10
+	_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS   :: 11
+	_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS  :: 12
+	_CS_POSIX_V6_LPBIG_OFFBIG_LIBS     :: 13
+	_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS :: 14
 
 	_PC_LINK_MAX           :: 1
 	_PC_MAX_CANON          :: 2
@@ -1911,6 +1910,193 @@ when ODIN_OS == .Darwin {
 
 	_POSIX_VDISABLE :: '\377'
 
+} else when ODIN_OS == .Linux {
+
+	_F_OK :: 0
+	X_OK :: 1
+	W_OK :: 2
+	R_OK :: 4
+
+	F_LOCK  :: 1
+	F_TEST  :: 3
+	F_TLOCK :: 2
+	F_ULOCK :: 0
+
+	_CS_PATH                           :: 1
+	_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS :: 2
+
+	_CS_POSIX_V6_ILP32_OFF32_CFLAGS   :: 1116
+	_CS_POSIX_V6_ILP32_OFF32_LDFLAGS  :: 1117
+	_CS_POSIX_V6_ILP32_OFF32_LIBS     :: 1118
+	_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS  :: 1120
+	_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS :: 1121
+	_CS_POSIX_V6_ILP32_OFFBIG_LIBS    :: 1122
+	_CS_POSIX_V6_LP64_OFF64_CFLAGS    :: 1124
+	_CS_POSIX_V6_LP64_OFF64_LDFLAGS   :: 1125
+	_CS_POSIX_V6_LP64_OFF64_LIBS      :: 1126
+	_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS  :: 1128
+	_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS :: 1129
+	_CS_POSIX_V6_LPBIG_OFFBIG_LIBS    :: 1130
+
+	_PC_LINK_MAX           :: 1
+	_PC_MAX_CANON          :: 2
+	_PC_MAX_INPUT          :: 3
+	_PC_NAME_MAX           :: 4
+	_PC_PATH_MAX           :: 5
+	_PC_PIPE_BUF           :: 6
+	_PC_CHOWN_RESTRICTED   :: 7
+	_PC_NO_TRUNC           :: 8
+	_PC_VDISABLE           :: 9
+	_PC_SYNC_IO            :: 10
+	_PC_ASYNC_IO           :: 11
+	_PC_PRIO_IO            :: 12
+	_PC_FILESIZEBITS       :: 14
+	_PC_REC_INCR_XFER_SIZE :: 15
+	_PC_REC_MAX_XFER_SIZE  :: 16
+	_PC_REC_MIN_XFER_SIZE  :: 17
+	_PC_REC_XFER_ALIGN     :: 18
+	_PC_ALLOC_SIZE_MIN     :: 19
+	_PC_SYMLINK_MAX        :: 20
+	_PC_2_SYMLINK          :: 21
+
+	_SC_ARG_MAX               :: 1
+	_SC_CHILD_MAX             :: 2
+	_SC_CLK_TCK               :: 3
+	_SC_NGROUPS_MAX           :: 4
+	_SC_OPEN_MAX              :: 5
+	_SC_STREAM_MAX            :: 6
+	_SC_TZNAME_MAX            :: 7
+	_SC_JOB_CONTROL           :: 8
+	_SC_SAVED_IDS             :: 9
+	_SC_REALTIME_SIGNALS      :: 10
+	_SC_PRIORITY_SCHEDULING   :: 11
+	_SC_TIMERS                :: 12
+	_SC_ASYNCHRONOUS_IO       :: 13
+	_SC_PRIORITIZED_IO        :: 14
+	_SC_SYNCHRONIZED_IO       :: 15
+	_SC_FSYNC                 :: 16
+	_SC_MAPPED_FILES          :: 17
+	_SC_MEMLOCK               :: 18
+	_SC_MEMLOCK_RANGE         :: 19
+	_SC_MEMORY_PROTECTION     :: 20
+	_SC_MESSAGE_PASSING       :: 21
+	_SC_SEMAPHORES            :: 22
+	_SC_SHARED_MEMORY_OBJECTS :: 23
+	_SC_AIO_LISTIO_MAX        :: 24
+	_SC_AIO_MAX               :: 25
+	_SC_AIO_PRIO_DELTA_MAX    :: 26
+	_SC_DELAYTIMER_MAX        :: 27
+	_SC_MQ_OPEN_MAX           :: 28
+	_SC_MQ_PRIO_MAX           :: 29
+	_SC_VERSION               :: 30
+	_SC_PAGESIZE              :: 31
+	_SC_PAGE_SIZE             :: _SC_PAGESIZE
+	_SC_RTSIG_MAX             :: 32
+	_SC_SEM_NSEMS_MAX         :: 33
+	_SC_SEM_VALUE_MAX         :: 34
+	_SC_SIGQUEUE_MAX          :: 35
+	_SC_TIMER_MAX             :: 36
+	_SC_BC_BASE_MAX           :: 37
+	_SC_BC_DIM_MAX            :: 38
+	_SC_BC_SCALE_MAX          :: 39
+	_SC_BC_STRING_MAX         :: 40
+	_SC_COLL_WEIGHTS_MAX      :: 41
+	_SC_EXPR_NEST_MAX         :: 43
+	_SC_LINE_MAX              :: 44
+	_SC_RE_DUP_MAX            :: 45
+	_SC_2_VERSION             :: 47
+	_SC_2_C_BIND              :: 48
+	_SC_2_C_DEV               :: 49
+	_SC_2_FORT_DEV            :: 50
+	_SC_2_FORT_RUN            :: 51
+	_SC_2_SW_DEV              :: 52
+	_SC_2_LOCALEDEF           :: 53
+
+	_SC_IOV_MAX                      :: 62
+	_SC_THREADS                      :: 69
+	_SC_THREAD_SAFE_FUNCTIONS        :: 70
+	_SC_GETGR_R_SIZE_MAX             :: 71
+	_SC_GETPW_R_SIZE_MAX             :: 72
+	_SC_LOGIN_NAME_MAX               :: 73
+	_SC_TTY_NAME_MAX                 :: 74
+	_SC_THREAD_DESTRUCTOR_ITERATIONS :: 75
+	_SC_THREAD_KEYS_MAX              :: 76
+	_SC_THREAD_STACK_MIN             :: 77
+	_SC_THREAD_THREADS_MAX           :: 78
+	_SC_THREAD_ATTR_STACKADDR        :: 79
+	_SC_THREAD_ATTR_STACKSIZE        :: 80
+	_SC_THREAD_PRIORITY_SCHEDULING   :: 81
+	_SC_THREAD_PRIO_INHERIT          :: 82
+	_SC_THREAD_PRIO_PROTECT          :: 83
+	_SC_THREAD_PROCESS_SHARED        :: 84
+	_SC_NPROCESSORS_CONF             :: 85
+	_SC_NPROCESSORS_ONLN             :: 86
+	_SC_PHYS_PAGES                   :: 87
+	_SC_AVPHYS_PAGES                 :: 88
+	_SC_ATEXIT_MAX                   :: 89
+	_SC_PASS_MAX                     :: 90
+	_SC_XOPEN_VERSION                :: 91
+	_SC_XOPEN_UNIX                   :: 92
+	_SC_XOPEN_CRYPT                  :: 93
+	_SC_XOPEN_ENH_I18N               :: 94
+	_SC_XOPEN_SHM                    :: 95
+	_SC_2_CHAR_TERM                  :: 96
+	_SC_2_UPE                        :: 97
+
+	_SC_XOPEN_LEGACY           :: 129
+	_SC_XOPEN_REALTIME         :: 130
+	_SC_XOPEN_REALTIME_THREADS :: 131
+	_SC_ADVISORY_INFO          :: 132
+	_SC_BARRIERS               :: 133
+	_SC_CLOCK_SELECTION        :: 137
+	_SC_CPUTIME                :: 138
+	_SC_THREAD_CPUTIME         :: 139
+	_SC_MONOTONIC_CLOCK        :: 149
+	_SC_READER_WRITER_LOCKS    :: 153
+	_SC_SPIN_LOCKS             :: 154
+	_SC_REGEXP                 :: 155
+	_SC_SHELL                  :: 157
+	_SC_SPAWN                  :: 159
+	_SC_SPORADIC_SERVER        :: 160
+	_SC_THREAD_SPORADIC_SERVER :: 161
+	_SC_TIMEOUTS               :: 164
+	_SC_TYPED_MEMORY_OBJECTS   :: 165
+	_SC_2_PBS                  :: 168
+	_SC_2_PBS_ACCOUNTING       :: 169
+	_SC_2_PBS_MESSAGE          :: 171
+	_SC_2_PBS_TRACK            :: 172
+	_SC_SYMLOOP_MAX            :: 173
+	_SC_2_PBS_CHECKPOINT       :: 174
+	_SC_V6_ILP32_OFF32         :: 175
+	_SC_V6_ILP32_OFFBIG        :: 176
+	_SC_V6_LP64_OFF64          :: 177
+	_SC_V6_LPBIG_OFFBIG        :: 178
+	_SC_HOST_NAME_MAX          :: 179
+	_SC_TRACE                  :: 180
+	_SC_TRACE_EVENT_FILTER     :: 181
+	_SC_TRACE_INHERIT          :: 182
+	_SC_TRACE_LOG              :: 183
+
+	_SC_IPV6                       :: 234
+	_SC_RAW_SOCKETS                :: 235
+	_SC_V7_ILP32_OFF32             :: 236
+	_SC_V7_ILP32_OFFBIG            :: 237
+	_SC_V7_LP64_OFF64              :: 238
+	_SC_V7_LPBIG_OFFBIG            :: 239
+	_SC_SS_REPL_MAX                :: 240
+	_SC_TRACE_EVENT_NAME_MAX       :: 241
+	_SC_TRACE_NAME_MAX             :: 242
+	_SC_TRACE_SYS_MAX              :: 243
+	_SC_TRACE_USER_EVENT_MAX       :: 244
+	_SC_XOPEN_STREAMS              :: 245
+	_SC_THREAD_ROBUST_PRIO_INHERIT :: 246
+	_SC_THREAD_ROBUST_PRIO_PROTECT :: 247
+
+	// NOTE: Not implemented.
+	_SC_XOPEN_UUCP :: 0
+	// NOTE: Not implemented.
+	_POSIX_VDISABLE :: 0
+
 } else {
 	#panic("posix is unimplemented for the current target")
 }

+ 1 - 1
core/sys/posix/utime.odin

@@ -24,7 +24,7 @@ when ODIN_OS == .NetBSD {
 	@(private) LUTIME :: "utime"
 }
 
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD	{
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD	|| ODIN_OS == .Linux {
 
 	utimbuf :: struct {
 		actime:  time_t, /* [PSX] access time (seconds since epoch) */

+ 21 - 0
core/sys/posix/wordexp.odin

@@ -102,6 +102,27 @@ when ODIN_OS == .Darwin {
 	WRDE_NOSPACE :: 4
 	WRDE_SYNTAX  :: 6
 
+} else when ODIN_OS == .Linux {
+
+	wordexp_t :: struct {
+		we_wordc: c.size_t,   /* [PSX] count of words matched by words */
+		we_wordv: [^]cstring, /* [PSX] pointer to list of expanded words */
+		we_offs:  c.size_t,   /* [PSX] slots to reserve at the beginning of we_wordv */
+	}
+
+	WRDE_DOOFFS  :: 1 << 0	/* Insert PWORDEXP->we_offs NULLs.  */
+	WRDE_APPEND  :: 1 << 1	/* Append to results of a previous call.  */
+	WRDE_NOCMD   :: 1 << 2	/* Don't do command substitution.  */
+	WRDE_REUSE   :: 1 << 3	/* Reuse storage in PWORDEXP.  */
+	WRDE_SHOWERR :: 1 << 4	/* Don't redirect stderr to /dev/null.  */
+	WRDE_UNDEF   :: 1 << 5	/* Error for expanding undefined variables.  */
+
+	WRDE_NOSPACE :: 1
+	WRDE_BADCHAR :: 2
+	WRDE_BADVAL  :: 3
+	WRDE_CMDSUB  :: 4
+	WRDE_SYNTAX  :: 5
+
 } else {
 	#panic("posix is unimplemented for the current target")
 }