-
Notifications
You must be signed in to change notification settings - Fork 0
/
unix.dart
430 lines (357 loc) · 20.3 KB
/
unix.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
import 'dart:ffi'
show
Abi,
Char,
DynamicLibrary,
Int,
Native,
NativeFunction,
NativeFunctionPointer,
Pointer,
Uint16,
Uint32,
Uint64,
Uint8,
UnsignedInt,
UnsignedLong,
UnsignedShort,
VarArgs;
import 'dart:io' show Platform;
// sizeof(sem_t) = 4 bytes on MacOS Arm64 and x86_64
// final class sem_t extends Opaque {}
/// Data Type: [mode_t] is typically defined as an unsigned integer type.
/// The exact size can vary between systems, but it is commonly 16 bits wide
/// on many UNIX and UNIX-like systems. Usage: It specifies file permissions
/// and type when creating new files or directories
/// (for example, with the open or mkdir system calls) and also
/// when changing permissions (with chmod or fchmod).
/// Size of [mode_t]: 2 bytes on MacOS Arm64 and x86_64
/// [mode_t] is often a 16-bit unsigned integer (uint16_t), but this can vary.
/// Dart FFI provides several integer types, such as [Uint8], [Uint16], [Uint32], and [Uint64],
/// to match the native types used by the C language.
/// Given the commonality of [mode_t] being a 16-bit value, you would likely use [Uint16] in Dart FFI,
/// but you should verify this against the documentation or header
/// files of the system you're working on. If you're interfacing with functions
/// that require [mode_t], you would declare them in Dart using [Uint16] for those parameters.
/// On [x86_64], [mode_t] is a [UnsignedShort] and on [arm64], [mode_t] is an [UnsignedLong]
typedef mode_t<T> = T;
typedef sem_t = Int;
class MODE_T_PERMISSIONS {
static int x = 1;
static int w = 2;
static int r = 4;
static int rw = r | w;
static int rx = r | x;
static int wx = w | x;
static int rwx = r | w | x;
static int perm({int u = 0, int g = 0, int o = 0, int user = 0, int group = 0, int others = 0}) =>
((u | user) << 6) | ((g | group) << 3) | (o | others);
// Most common for named semaphores
static int RECOMMENDED = MODE_T_PERMISSIONS.OWNER_READ_WRITE_GROUP_READ;
// 0644 - Owner can read and write; the group can read; others can read.
// static int OWNER_READ_WRITE_GROUP_READ = toOctal(0, 6, 4, 4);
static int OWNER_READ_WRITE_GROUP_READ = perm(u: rw, g: r, o: r);
// 0666 - Owner can read and write; the group can read and write; others can read and write.
// static int OWNER_READ_WRITE_GROUP_READ_WRITE = toOctal(0, 6, 6, 6);
static int OWNER_READ_WRITE_GROUP_AND_OTHERS_READ_WRITE = perm(u: rw, g: rw, o: rw);
// 0600 - Owner can read and write; the group cannot access; others cannot access.
// static int OWNER_READ_WRITE_GROUP_NO_ACCESS = toOctal(0, 6, 0, 0);
static int OWNER_READ_WRITE_GROUP_NO_ACCESS = perm(u: rw, g: 0, o: 0);
// 0700 - Owner can read, write, and execute; the group cannot access; others cannot access.
// static int OWNER_READ_WRITE_EXECUTE_GROUP_NO_ACCESS = toOctal(0, 7, 0, 0);
static int OWNER_READ_WRITE_EXECUTE_GROUP_NO_ACCESS = perm(u: rwx, g: 0, o: 0);
// 0755 - Owner can read, write, and execute; the group can read and execute; others can read and execute.
// static int OWNER_READ_WRITE_EXECUTE_GROUP_AND_OTHERS_READ_EXECUTE = toOctal(0, 7, 5, 5);
static int OWNER_READ_WRITE_EXECUTE_GROUP_AND_OTHERS_READ_EXECUTE = perm(u: rwx, g: rx, o: rx);
// 0777 - Owner can read, write, and execute; the group can read, write, and execute; others can read, write, and execute.
// static int ALL_READ_WRITE_EXECUTE = toOctal(0, 7, 7, 7);
static int ALL_READ_WRITE_EXECUTE = perm(u: rwx, g: rwx, o: rwx);
}
/// [sem_open] creates a new POSIX semaphore or opens an existing
/// semaphore. The semaphore is identified by [name]. For details of
/// the construction of [name], see sem_overview(7).
/// The [oflag] argument specifies flags that control the operation of
/// the call. (Definitions of the flags values can be obtained by
/// including <fcntl.h>.) If [O_CREAT] is specified in [oflag], then the
/// semaphore is created if it does not already exist. The owner
/// (user ID) of the semaphore is set to the effective user ID of the
/// calling process. The group ownership (group ID) is set to the
/// effective group ID of the calling process. If both [O_CREAT] and
/// [O_EXCL] are specified in [oflag], then an error is returned if a
/// semaphore with the given name already exists.
/// If [O_CREAT] is specified in [oflag], then two additional arguments
/// must be supplied. The mode argument specifies the permissions to
/// be placed on the new semaphore, as for open(2). (Symbolic
/// definitions for the permissions bits can be obtained by including
/// <sys/stat.h>.) The permissions settings are masked against the
/// process umask. Both read and write permission should be granted
/// to each class of user that will access the semaphore. The value
/// argument specifies the initial value for the new semaphore. If
/// [O_CREAT] is specified, and a semaphore with the given name already
/// exists, then [mode_t] and [value] are ignored.
/// When [value] is greater than 0: It indicates the number of times processes
/// or threads can successfully [sem_wait] (decrement) the semaphore without blocking.
/// For example, if [value] is set to 3, up to three processes or threads can decrement
/// the semaphore without being blocked, effectively allowing simultaneous access to a
/// resource that can support that many concurrent accesses.
///
/// When [value] is 0: It means that the semaphore is initially "locked" or unavailable.
/// Any process or thread that tries to decrement the semaphore with [sem_wait] will block
/// until another process or thread increments the semaphore with [sem_post], making
/// the resource available.
typedef sem_open_function_type<M> = Pointer<sem_t> Function(Pointer<Char>, Int, VarArgs<(mode_t<M>, UnsignedInt)>);
typedef dart_sem_open_function_type = Pointer<sem_t> Function(Pointer<Char> name, int oflag, int mode, int value);
Pointer<sem_t> sem_open(Pointer<Char> name, int oflags, [int? mode, int? value]) =>
sem_open_callable(name, oflags, mode ?? MODE_T_PERMISSIONS.RECOMMENDED, value ?? 1);
// if we are on MacOS Arm64, then our mode_t is UnsignedLong otherwise it is UnsignedShort
final sem_open_pointer = Abi.current() == Abi.macosArm64
? DynamicLibrary.process().lookup<NativeFunction<sem_open_function_type<UnsignedLong>>>('sem_open')
: DynamicLibrary.process().lookup<NativeFunction<sem_open_function_type<UnsignedShort>>>('sem_open');
final dart_sem_open_function_type sem_open_callable = Abi.current() == Abi.macosArm64
? sem_open_pointer.cast<NativeFunction<sem_open_function_type<UnsignedLong>>>().asFunction()
: sem_open_pointer.cast<NativeFunction<sem_open_function_type<UnsignedShort>>>().asFunction();
/// [sem_wait] Decrements (locks) the semaphore pointed to by sem.
/// If the semaphore's value is greater than zero, then the decrement
/// proceeds, and the function returns, immediately. If the
/// semaphore currently has the value zero, then the call blocks
/// until either it becomes possible to perform the decrement (i.e.,
/// the semaphore value rises above zero), or a signal handler
/// interrupts the call.
///
/// The [sem_wait] function shall lock the semaphore referenced by sem by
/// performing a semaphore lock operation on that semaphore. If the semaphore
/// value is currently [zero], then the calling thread shall not return from the
/// call to [sem_wait] until it either locks the semaphore or the call is
/// interrupted by a signal. Upon successful return, the state of the semaphore
/// shall be locked and shall remain locked until the [sem_post] function is
/// executed and returns successfully. The [sem_wait] function is interruptible
/// by the delivery of a signal.
@Native<Int Function(Pointer<sem_t> sem_t)>()
external int sem_wait(Pointer<sem_t> sem_t);
/// [sem_trywait] is the same as [sem_wait], except that if the
/// decrement cannot be immediately performed, the call returns an
/// error ([errno] set to [EAGAIN]) instead of blocking.
///
/// The [sem_trywait] function shall lock the semaphore referenced by
/// sem only if the semaphore is currently not locked; that is, if the
/// semaphore value is currently positive. Otherwise,
/// it shall not lock the semaphore.
@Native<Int Function(Pointer<sem_t>)>()
external int sem_trywait(Pointer<sem_t> sem_t);
/// [sem_post] function shall unlock the semaphore referenced by sem by
/// performing a semaphore unlock operation on that semaphore.
/// If the semaphore value resulting from this operation is positive,
/// then no threads were blocked waiting for the semaphore to become unlocked;
/// the semaphore value is simply incremented.
///
/// If the value of the semaphore resulting from this operation is zero,
/// then one of the threads blocked waiting for the semaphore shall be
/// allowed to return successfully from its call to [sem_wait].
@Native<Int Function(Pointer<sem_t>)>()
external int sem_post(Pointer<sem_t> sem_t);
/// [sem_close] closes the named semaphore referred to by sem,
/// allowing any resources that the system has allocated to the
/// calling process for this semaphore to be freed.
/// On success [sem_close] returns 0; on error, -1 is returned, with
/// [errno] set to indicate the error.
@Native<Int Function(Pointer<sem_t>)>()
external int sem_close(Pointer<sem_t> sem_t);
/// [sem_unlink] removes the named semaphore referred to by name.
/// The semaphore name is removed immediately. The semaphore is
/// destroyed once all other processes that have the semaphore open
/// close it.
@Native<Int Function(Pointer<Char>)>()
external int sem_unlink(Pointer<Char> name);
/// The <errno.h> header file defines the integer variable errno,
/// which is set by system calls and some library functions in the
/// event of an error to indicate what went wrong.
///
/// The value in errno is significant only when the return value of
/// the call indicated an error (i.e., -1 from most system calls; -1
/// or NULL from most library functions); a function that succeeds is
/// allowed to change [errno]. The value of errno is never set to zero
/// by any system call or library function.
@Native<Pointer<Int> Function()>()
external Pointer<Int> __error();
// Pointer<Int> error() {
// return ___error();
// }
//
// late final ___errorPtr =
// _lookup<NativeFunction<Pointer<Int> Function()>>('__error');
// late final ___error =
// ___errorPtr.asFunction<Pointer<Int> Function()>();
@Native<Pointer<Int> Function()>()
external Pointer<Int> __errno_location();
Pointer<Int> Function() _errno = () => Platform.isMacOS ? __error() : __errno_location();
Pointer<Int> get errno => _errno();
class UnixSemLimits {
static bool isBSD = Platform.isMacOS;
// Uint8
// Size in bytes for a path (including null terminator): 1025
static int PATH_MAX = 1024;
// SEM_VALUE_MAX is of length 32767 on MacOS Arm64 and x86_64
// Size of SEM_VALUE_MAX is 4 bytes on MacOS Arm64 and x86_64
static int SEM_VALUE_MAX = 32767;
// Size of NAME_MAX (size of an int): 4 bytes on MacOS Arm64 and x86_64
static int NAME_MAX = 255;
static int NAME_MAX_CHARACTERS = 30;
}
class UnixSemOpenMacros {
// https://man7.org/linux/man-pages/man3/sem_open.3.html
// https://pubs.opengroup.org/onlinepubs/009695399/functions/sem_open.html
// MacOS will be BSD and Linux will be GNU
static bool isBSD = Platform.isMacOS;
// The semaphore exists, but the caller does not have permission to open it.
// The named semaphore exists and the permissions specified by oflag are denied, or the named semaphore does not exist and permission to create the named semaphore is denied.
static int EACCES = isBSD ? 13 : 13;
// The sem_open() operation was interrupted by a signal.
static int EINTR = isBSD ? 4 : 4;
// Both O_CREAT and O_EXCL were specified in oflag, but a semaphore with this name already exists.
// O_CREAT and O_EXCL are set and the named semaphore already exists.
static int EEXIST = isBSD ? 17 : 17;
/// [value] was greater than [SEM_VALUE_MAX].
/// [name] consists of just "/", followed by no other characters.
/// The [sem_open] operation is not supported for the given [name],
/// or [O_CREAT] was specified in [oflag] and value was greater than [SEM_VALUE_MAX].
static int EINVAL = isBSD ? 22 : 22;
// The per-process limit on the number of open file descriptors has been reached.
// You can check this by running `ulimit -Sn`
// Too many semaphore descriptors or file descriptors are currently in use by this process.
static int EMFILE = isBSD ? 24 : 24;
/// [name] was too long.
/// The length of the name argument exceeds [PATH_MAX]
/// or a pathname component is longer than [NAME_MAX].
static int ENAMETOOLONG = isBSD ? 63 : 36;
// The system-wide limit on the total number of open files has been reached.
// Too many semaphores are currently open in the system.
static int ENFILE = isBSD ? 23 : 23;
/// The [O_CREAT] flag was not specified in [oflag] and no semaphore with this [name] exists;
/// or, [O_CREAT] was specified, but [name] wasn't well formed.
/// [O_CREAT] is not set and the named semaphore does not exist.
static int ENOENT = isBSD ? 2 : 2;
// Insufficient memory.
static int ENOMEM = isBSD ? 12 : 12;
// There is insufficient space for the creation of the new named semaphore.
static int ENOSPC = isBSD ? 28 : 28;
// "Invalid memory address encountered. Please ensure all pointers and addresses are correctly set and accessible."
static int EFAULT = isBSD ? 14 : 14;
// Upon successful completion, the [sem_open] function shall return the address of the semaphore. Otherwise, it shall return a value of SEM_FAILED and set errno to indicate the error. The symbol SEM_FAILED is defined in the <semaphore.h> header. No successful return from sem_open() shall return the value SEM_FAILED.
/// [SEM_FAILED] = 0xffffffffffffffff (as a pointer), 18446744073709551615 (as an unsigned integer)
/// Size of SEM_FAILED (size of a pointer): 8 bytes on MacOS Arm64 and MacOS x86_64
static Pointer<Uint64> SEM_FAILED =
Platform.isMacOS ? Pointer.fromAddress(0xffffffffffffffff) : Pointer.fromAddress(0x0);
/// This flag is used to create a semaphore if it does not already exist. If [O_CREAT] is set and the semaphore
/// already exists, then [O_CREAT] has no effect, except as noted under [O_EXCL]. Otherwise, [sem_open] creates
/// a named semaphore. The [O_CREAT] flag requires a third and a fourth argument: [mode], which is of type [mode_t],
/// and [value], which is of type unsigned. The semaphore is created with an initial value of [value]. Valid initial
/// values for semaphores are less than or equal to [SEM_VALUE_MAX]. The user ID of the semaphore is set to the
/// effective user ID of the process; the group ID of the semaphore is set to a system default group ID or to the
/// effective group ID of the process. The permission bits of the semaphore are set to the value of the [mode]
/// argument except those set in the file mode creation mask of the process. When bits in [mode] other than the file
/// permission bits are specified, the effect is unspecified. After the semaphore named [name] has been created by
/// [sem_open] with the [O_CREAT] flag, other processes can connect to the semaphore by calling [sem_open]
/// with the same value of [name].
/// Size of the flags (size of an int): 4 bytes on MacOS Arm64 and MacOS x86_64
static int O_CREAT = isBSD ? 512 : 64;
/// If [O_EXCL] and [O_CREAT] are set, [sem_open] fails if the semaphore name exists. The check for the existence
/// of the semaphore and the creation of the semaphore if it does not exist are atomic with respect to other processes
/// executing [sem_open] with [O_EXCL] and [O_CREAT] set. If [O_EXCL] is set and [O_CREAT] is not set, the effect
/// is undefined. If flags other than [O_CREAT] and [O_EXCL] are specified in the [oflag] parameter,
/// the effect is unspecified.
/// Size of the flags (size of an int): 4 bytes on MacOS Arm64 and MacOS x86_64
///
static int _O_EXCL = isBSD ? 2048 : 128;
/// O_EXCL can only be used along side O_CREAT otherwise it has no effect and will likely throw an error
static int O_EXCL = O_CREAT | _O_EXCL;
static int VALUE_RECOMMENDED = 1;
}
class UnixSemOpenError extends Error {
final int code;
final String message;
final String? identifier;
late final String? description = toString();
UnixSemOpenError(this.code, this.message, this.identifier);
@override
String toString() => 'SemOpenError: [Error: $identifier Code: $code]: $message';
static UnixSemOpenError fromErrno(int errno) {
if (errno == UnixSemOpenMacros.EACCES)
return UnixSemOpenError(
errno, "The semaphore exists, but the caller does not have permission to open it.", 'EACCES');
if (errno == UnixSemOpenMacros.EINTR)
return UnixSemOpenError(errno, "The sem_open() operation was interrupted by a signal.", 'EINTR');
if (errno == UnixSemOpenMacros.EEXIST)
return UnixSemOpenError(errno,
"Both O_CREAT and O_EXCL were specified in oflag, but a semaphore with this name already exists.", 'EEXIST');
if (errno == UnixSemOpenMacros.EINVAL)
return UnixSemOpenError(
errno,
"Invalid argument. The name was just '/' or the value was greater than SEM_VALUE_MAX, or the operation is not supported for the given name.",
'EINVAL');
if (errno == UnixSemOpenMacros.EMFILE)
return UnixSemOpenError(
errno, "The per-process limit on the number of open file descriptors has been reached.", 'EMFILE');
if (errno == UnixSemOpenMacros.ENAMETOOLONG)
return UnixSemOpenError(
errno,
"The name was too long, or a pathname component is longer than NAME_MAX i.e. 30 character dart String including the leading slash.",
'ENAMETOOLONG');
if (errno == UnixSemOpenMacros.ENFILE)
return UnixSemOpenError(
errno, "The system-wide limit on the total number of open files has been reached.", 'ENFILE');
if (errno == UnixSemOpenMacros.ENOENT)
return UnixSemOpenError(errno, "No semaphore with this name exists; or, name wasn't well formed.", 'ENOENT');
if (errno == UnixSemOpenMacros.ENOMEM) return UnixSemOpenError(errno, "Insufficient memory.", 'ENOMEM');
if (errno == UnixSemOpenMacros.ENOSPC)
return UnixSemOpenError(
errno, "There is insufficient space for the creation of the new named semaphore.", 'ENOSPC');
if (errno == UnixSemOpenMacros.EFAULT)
return UnixSemOpenError(
errno,
"Invalid memory address encountered. Please ensure all pointers and addresses are correctly set and accessible.",
'EFAULT');
else
return UnixSemOpenError(errno, "Unknown error.", 'UNKNOWN');
}
}
class UnixSemWaitOrTryWaitMacros {
// https://man7.org/linux/man-pages/man3/sem_wait.3.html
// https://pubs.opengroup.org/onlinepubs/009695399/functions/sem_wait.html
// MacOS will be BSD and Linux will be GNU
static bool isBSD = Platform.isMacOS;
/// The semaphore was already locked, so it cannot be
/// immediately locked by the [sem_trywait]
/// operation ( [sem_trywait] only).
static int EAGAIN = isBSD ? 35 : 11;
// A deadlock condition was detected.
static int EDEADLK = isBSD ? 11 : 35;
// A signal interrupted this function.
// The call was interrupted by a signal handler;
static int EINTR = isBSD ? 4 : 4;
/// The [sem_t] argument does not refer to a valid semaphore.
/// [sem_t] is not a valid semaphore.
static int EINVAL = isBSD ? 22 : 22;
}
class UnixSemCloseMacros {
static bool isBSD = Platform.isMacOS;
/// [sem_t] is not a valid semaphore.
static int EINVAL = isBSD ? 22 : 22;
}
class UnixSemUnlinkMacros {
static bool isBSD = Platform.isMacOS;
/// The named semaphore referred to by [name] does not exist.
static int ENOENT = isBSD ? 2 : 2;
/// The named semaphore referred to by [name] exists, but the caller does not have permission to unlink it.
/// Permission is denied to unlink the named semaphore.
static int EACCES = isBSD ? 13 : 13;
/// [name] was too long.
/// The length of the [name] argument exceeds [PATH_MAX] or a pathname component is longer than [NAME_MAX]
static int ENAMETOOLONG = isBSD ? 63 : 36;
}
class UnixSemPostMacros {
static bool isBSD = Platform.isMacOS;
/// The semaphore referred to by [sem] is not a valid semaphore.
static int EINVAL = isBSD ? 22 : 22;
// The maximum allowable value for a semaphore would be exceeded.
static int EOVERFLOW = isBSD ? 84 : 75;
}