Haiku: set thread priority
[ajla.git] / os_posix.c
blob8976fbfda398d538d307553b24e003700a66c3a3
1 /*
2 * Copyright (C) 2024 Mikulas Patocka
4 * This file is part of Ajla.
6 * Ajla is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 3 of the License, or (at your option) any later
9 * version.
11 * Ajla is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along with
16 * Ajla. If not, see <https://www.gnu.org/licenses/>.
19 #include "ajla.h"
21 #if !defined(OS_OS2) && !defined(OS_WIN32)
23 #include "str.h"
24 #include "tree.h"
25 #include "rwlock.h"
26 #include "args.h"
27 #include "obj_reg.h"
28 #include "addrlock.h"
29 #include "iomux.h"
30 #include "timer.h"
31 #include "os_util.h"
33 #include "os.h"
35 #include <stdio.h>
36 #include <fcntl.h>
37 #include <unistd.h>
38 #include <dirent.h>
39 #ifdef HAVE_SYS_SYSMACROS_H
40 #include <sys/sysmacros.h>
41 #endif
42 #ifdef HAVE_LINUX_FALLOC_H
43 #include <linux/falloc.h>
44 #endif
45 #include <time.h>
46 #include <sys/time.h>
47 #include <sys/wait.h>
48 #ifdef HAVE_SYS_SELECT_H
49 #include <sys/select.h>
50 #endif
51 #ifdef HAVE_NETWORK
52 #include <sys/socket.h>
53 #include <netinet/in.h>
54 #include <netdb.h>
55 #endif
56 #ifdef OS_HAS_DLOPEN
57 #include <dlfcn.h>
58 #endif
59 #include <signal.h>
60 #include <sys/ioctl.h>
61 #if defined(HAVE_SYS_PARAM_H)
62 #include <sys/param.h>
63 #endif
64 #if defined(HAVE_SYS_UCRED_H)
65 #include <sys/ucred.h>
66 #endif
67 #if defined(HAVE_SYS_MOUNT_H)
68 #include <sys/mount.h>
69 #endif
70 #if defined(HAVE_LINUX_FS_H) && !defined(__TINYC__)
71 #include <linux/fs.h>
72 #endif
74 #define SOCKADDR_MAX_LEN 65535
75 #define SOCKADDR_ALIGN 16
77 #ifndef wake_up_wait_list
78 void u_name(wake_up_wait_list)(struct list *wait_list, mutex_t *mutex_to_lock, bool can_allocate_memory);
79 void c_name(wake_up_wait_list)(struct list *wait_list, mutex_t *mutex_to_lock, bool can_allocate_memory);
80 #endif
82 #if !defined(THREAD_NONE) && defined(USE_SIGPROCMASK) && defined(HAVE_PTHREAD) && defined(HAVE_PTHREAD_SIGMASK)
83 #include <pthread.h>
84 #define USE_PTHREAD_SIGMASK
85 #endif
87 #ifdef OS_USE_LARGEFILE64_SOURCE
88 #define fstat fstat64
89 #define fstatvfs fstatvfs64
90 #define ftruncate ftruncate64
91 #define lseek lseek64
92 #define lstat lstat64
93 #define mmap mmap64
94 #define open open64
95 #define pread pread64
96 #define pwrite pwrite64
97 #define stat stat64
98 #define statvfs statvfs64
99 #define truncate truncate64
100 #endif
102 static rwmutex_t fork_lock;
103 static bool os_threads_initialized = false;
105 #if !defined(NO_DIR_HANDLES) && !defined(OS_USE_LARGEFILE64_SOURCE) && defined(O_CLOEXEC) && defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_READLINKAT) && defined(HAVE_UNLINKAT) && defined(HAVE_MKDIRAT) && defined(HAVE_MKNODAT) && defined(HAVE_SYMLINKAT) && defined(HAVE_LINKAT) && defined(HAVE_RENAMEAT) && defined(HAVE_FCHMODAT) && defined(HAVE_FCHOWNAT) && defined(HAVE_UTIMENSAT)
106 static bool have_O_CLOEXEC_openat = false;
107 #define HAVE_AT_FUNCTIONS
108 #endif
110 dir_handle_t os_cwd;
113 #include "os_com.inc"
116 static void os_lock_fork(bool for_write)
118 if (os_threads_initialized) {
119 if (!for_write)
120 rwmutex_lock_read(&fork_lock);
121 else
122 rwmutex_lock_write(&fork_lock);
126 static void os_unlock_fork(bool for_write)
128 if (os_threads_initialized) {
129 if (!for_write)
130 rwmutex_unlock_read(&fork_lock);
131 else
132 rwmutex_unlock_write(&fork_lock);
136 uint32_t os_get_last_error(void)
138 return 0;
141 uint32_t os_get_last_socket_error(void)
143 return 0;
146 #ifdef OS_HAS_MMAP
148 int os_getpagesize(void)
150 #if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
152 long ps;
153 EINTR_LOOP(ps, sysconf(_SC_PAGESIZE));
154 if (unlikely(ps == -1)) {
155 int er = errno;
156 warning("sysconf(_SC_PAGESIZE) returned error: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
157 } else {
158 return ps;
161 #elif defined(HAVE_GETPAGESIZE)
163 int ps;
164 EINTR_LOOP(ps, getpagesize());
165 if (unlikely(ps == -1)) {
166 int er = errno;
167 warning("getpagesize() returned error: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
168 } else {
169 return ps;
172 #endif
173 return 512;
176 void *os_mmap(void *ptr, size_t size, int prot, int flags, int h, os_off_t off, ajla_error_t *err)
178 void *p;
179 #ifdef PROT_MPROTECT
180 prot |= PROT_MPROTECT(PROT_EXEC);
181 #endif
182 #ifndef HAVE_MPROTECT
183 prot |= PROT_EXEC;
184 #endif
185 EINTR_LOOP_VAL(p, MAP_FAILED, mmap(ptr, size, prot, flags, h, off));
186 if (unlikely(p == MAP_FAILED)) {
187 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
188 fatal_mayfail(e, err, "can't map memory: %s", error_decode(e));
189 return MAP_FAILED;
191 return p;
194 void os_munmap(void *ptr, size_t size, bool attr_unused file)
196 int r;
197 EINTR_LOOP(r, munmap(ptr, size));
198 if (unlikely(r == -1)) {
199 int er = errno;
200 internal(file_line, "os_munmap: munmap(%p, %"PRIxMAX") returned error: %d, %s", ptr, (uintmax_t)size, er, error_decode(error_from_errno(EC_SYSCALL, er)));
204 bool os_mprotect(void attr_unused *ptr, size_t attr_unused size, int attr_unused prot, ajla_error_t *err)
206 #ifdef HAVE_MPROTECT
207 int r;
208 EINTR_LOOP(r, mprotect(ptr, size, prot));
209 if (unlikely(r == -1)) {
210 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
211 fatal_mayfail(e, err, "can't protect memory: %s", error_decode(e));
212 return false;
214 return true;
215 #else
216 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "the system doesn't support mprotect");
217 return false;
218 #endif
221 #ifdef OS_HAS_MREMAP
222 void *os_mremap(void *old_ptr, size_t old_size, size_t new_size, int flags, void attr_unused *new_ptr, ajla_error_t *err)
224 void *p;
225 #ifdef MREMAP_FIXED
226 EINTR_LOOP_VAL(p, MAP_FAILED, mremap(old_ptr, old_size, new_size, flags, new_ptr));
227 #else
228 EINTR_LOOP_VAL(p, MAP_FAILED, mremap(old_ptr, old_size, new_size, flags));
229 #endif
230 if (unlikely(p == MAP_FAILED)) {
231 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
232 fatal_mayfail(e, err, "can't remap memory: %s", error_decode(e));
233 return MAP_FAILED;
235 return p;
237 #endif
239 #endif
242 void os_code_invalidate_cache(uint8_t attr_unused *code, size_t attr_unused code_size, bool attr_unused set_exec)
244 #if defined(ARCH_PARISC) && defined(HAVE_GCC_ASSEMBLER)
245 size_t i;
246 size_t cl_size = cpu_test_feature(CPU_FEATURE_pa20) ? 64 : 16;
247 size_t align = ptr_to_num(code) & (cl_size - 1);
248 code -= align;
249 code_size += align;
250 __asm__ volatile ("sync" : : : "memory");
251 for (i = 0; i < code_size; i += cl_size) {
252 __asm__ volatile ("fdc %%r0(%0)" : : "r"(code + i) : "memory");
254 __asm__ volatile ("sync");
255 #if defined(ARCH_PARISC32)
256 if (PA_SPACES) {
257 unsigned long reg;
258 __asm__ volatile("ldsid (%1), %0\n mtsp %0, %%sr0" : "=r"(reg) : "r"(code) : "memory");
260 #endif
261 for (i = 0; i < code_size; i += cl_size) {
262 #if defined(ARCH_PARISC32)
263 if (PA_SPACES) {
264 __asm__ volatile ("fic %%r0(%%sr0, %0)" : : "r"(code + i) : "memory");
265 } else {
266 __asm__ volatile ("fic %%r0(%%sr4, %0)" : : "r"(code + i) : "memory");
268 #else
269 __asm__ volatile ("fic %%r0(%0)" : : "r"(code + i) : "memory");
270 #endif
272 __asm__ volatile ("sync" : : : "memory");
273 #elif defined(ARCH_ALPHA)
274 /* imb doesn't work on SMP systems */
275 #elif defined(ARCH_SPARC64) && defined(HAVE_GCC_ASSEMBLER)
276 size_t i;
277 __asm__ volatile ("membar #StoreStore" : : : "memory");
278 for (i = 0; i < code_size; i += 8) {
279 __asm__ volatile ("flush %0" : : "r"(code + i) : "memory");
281 #elif defined(HAVE___BUILTIN___CLEAR_CACHE)
282 __builtin___clear_cache(cast_ptr(void *, code), cast_ptr(char *, code) + code_size);
283 #endif
284 #if defined(OS_HAS_MMAP) && defined(HAVE_MPROTECT)
285 if (set_exec) {
286 int prot_flags = PROT_READ | PROT_EXEC
287 #ifdef CODEGEN_USE_HEAP
288 | PROT_WRITE
289 #endif
291 int page_size = os_getpagesize();
292 int front_pad = ptr_to_num(code) & (page_size - 1);
293 uint8_t *mem_region = code - front_pad;
294 size_t mem_length = code_size + front_pad;
295 mem_length = round_up(mem_length, page_size);
296 os_mprotect(mem_region, mem_length, prot_flags, NULL);
298 #endif
301 void *os_code_map(uint8_t *code, size_t code_size, ajla_error_t attr_unused *err)
303 #ifdef CODEGEN_USE_HEAP
304 os_code_invalidate_cache(code, code_size, !amalloc_enabled);
305 return code;
306 #else
307 size_t rounded_size = round_up(code_size, os_getpagesize());
308 void *ptr = os_mmap(NULL, rounded_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, handle_none, 0, err);
309 if (unlikely(ptr == MAP_FAILED)) {
310 mem_free(code);
311 return NULL;
313 memcpy(ptr, code, code_size);
314 os_code_invalidate_cache(ptr, code_size, true);
315 mem_free(code);
316 return ptr;
317 #endif
320 void os_code_unmap(void *mapped_code, size_t attr_unused code_size)
322 #ifdef CODEGEN_USE_HEAP
323 mem_free(mapped_code);
324 #else
325 size_t rounded_size = round_up(code_size, os_getpagesize());
326 os_munmap(mapped_code, rounded_size, false);
327 #endif
331 void os_block_signals(sig_state_t attr_unused *set)
333 #ifdef USE_SIGPROCMASK
334 int er;
335 sig_state_t block;
336 sigfillset(&block);
337 sigdelset(&block, SIGFPE);
338 sigdelset(&block, SIGTRAP);
339 #ifdef USE_PTHREAD_SIGMASK
340 er = pthread_sigmask(SIG_BLOCK, &block, set);
341 if (unlikely(er))
342 fatal("pthread_sigmask failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
343 #else
344 if (unlikely(sigprocmask(SIG_BLOCK, &block, set))) {
345 er = errno;
346 fatal("sigprocmask failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
348 #endif
349 #elif defined(HAVE_SIGBLOCK) && defined(HAVE_SIGSETMASK)
350 sig_state_t s = sigblock(~(sigmask(SIGFPE) | sigmask(SIGTRAP)));
351 if (set)
352 *set = s;
353 #endif
356 void os_unblock_signals(const sig_state_t attr_unused *set)
358 #ifdef USE_SIGPROCMASK
359 int er;
360 #ifdef USE_PTHREAD_SIGMASK
361 er = pthread_sigmask(SIG_SETMASK, set, NULL);
362 if (unlikely(er))
363 fatal("pthread_sigmask failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
364 #else
365 if (unlikely(sigprocmask(SIG_SETMASK, set, NULL))) {
366 er = errno;
367 fatal("sigprocmask failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
369 #endif
370 #elif defined(HAVE_SIGBLOCK) && defined(HAVE_SIGSETMASK)
371 sigsetmask(*set);
372 #endif
375 #if !defined(OS_DOS)
376 static void os_unblock_all_signals(void)
378 sig_state_t unblock;
379 #ifdef USE_SIGPROCMASK
380 sigemptyset(&unblock);
381 #elif defined(HAVE_SIGBLOCK) && defined(HAVE_SIGSETMASK)
382 unblock = 0;
383 #endif
384 os_unblock_signals(&unblock);
386 #endif
389 void attr_cold os_stop(void)
391 #ifdef SIGSTOP
392 kill(getpid(), SIGSTOP);
393 #else
394 warning("stop not supported");
395 #endif
398 static inline void u_sleep(unsigned us)
400 struct timeval tv;
401 tv.tv_sec = us / 1000000;
402 tv.tv_usec = us % 1000000;
403 select(0, NULL, NULL, NULL, &tv);
406 void os_background(void)
408 #ifndef OS_DOS
409 int r;
410 pid_t pa, p;
411 #ifdef __linux__
412 sig_state_t set;
413 #endif
414 pa = getpid();
415 os_lock_fork(true);
416 #ifdef __linux__
417 os_block_signals(&set);
418 #endif
419 EINTR_LOOP(p, fork());
420 #ifdef __linux__
421 os_unblock_signals(&set);
422 #endif
423 if (!p) {
424 while (1) {
426 * Note that this is racy. If we send SIGCONT too
427 * quickly, the ajla process will not be put to
428 * background.
430 u_sleep(100000);
431 kill(pa, SIGCONT);
434 os_unlock_fork(true);
435 if (p == -1)
436 return;
437 kill(pa, SIGSTOP);
439 * Another race - we must not send SIGKILL too quickly
441 u_sleep(100000);
442 kill(p, SIGKILL);
443 EINTR_LOOP(r, waitpid(p, NULL, 0));
444 #endif
447 bool os_foreground(void)
449 int sigttin, sigttou;
450 signal_seq_t seq;
451 os_termios_t tc;
452 int r;
454 sigttin = os_signal_handle("SIGTTIN", &seq, NULL);
455 sigttou = os_signal_handle("SIGTTOU", &seq, NULL);
456 r = tcgetattr(0, &tc);
457 if (!r)
458 r = tcsetattr(0, TCSANOW, &tc);
459 os_signal_unhandle(sigttin);
460 os_signal_unhandle(sigttou);
461 return !r;
465 void os_set_cloexec(handle_t h)
467 int r;
468 EINTR_LOOP(r, fcntl(h, F_SETFD, FD_CLOEXEC));
469 if (unlikely(r == -1)) {
470 int er = errno;
471 fatal("fcntl(F_SETFD, FD_CLOEXEC) failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
475 static char *os_call_getcwd(ajla_error_t *err)
477 char *h, *r;
478 ajla_error_t e;
479 size_t buf_size = 32;
481 again:
482 h = mem_alloc_mayfail(char *, buf_size, err);
483 if (unlikely(!h))
484 return NULL;
485 EINTR_LOOP_VAL(r, NULL, getcwd(h, buf_size));
486 if (unlikely(!r)) {
487 if (errno == ERANGE) {
488 mem_free(h);
489 buf_size *= 2;
490 if (unlikely(!buf_size)) {
491 fatal_mayfail(error_ajla(EC_ASYNC, AJLA_ERROR_SIZE_OVERFLOW), err, "overflow when allocating directory buffer");
492 return NULL;
494 goto again;
496 e = error_from_errno(EC_SYSCALL, errno);
497 fatal_mayfail(e, err, "can't get working directory: %s", error_decode(e));
498 mem_free(h);
499 return NULL;
501 #ifdef __GLIBC__
502 if (unlikely(h[0] != '/')) {
503 e = error_from_errno(EC_SYSCALL, ENOENT);
504 fatal_mayfail(e, err, "can't get working directory: %s", error_decode(e));
505 mem_free(h);
506 return NULL;
508 #endif
510 return h;
513 static dir_handle_t os_get_cwd(ajla_error_t *err)
515 #ifndef NO_DIR_HANDLES
516 dir_handle_t h;
517 #ifdef HAVE_AT_FUNCTIONS
518 if (likely(have_O_CLOEXEC_openat)) {
519 EINTR_LOOP(h, open(".", O_RDONLY | O_CLOEXEC, 0));
520 if (unlikely(h == -1)) {
521 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
522 fatal_mayfail(e, err, "can't open the current directory: %s", error_decode(e));
523 } else {
524 obj_registry_insert(OBJ_TYPE_HANDLE, h, file_line);
526 return h;
528 #endif
529 EINTR_LOOP(h, open(".", O_RDONLY, 0));
530 if (unlikely(h == -1)) {
531 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
532 fatal_mayfail(e, err, "cam't open the current directory: %s", error_decode(e));
533 } else {
534 obj_registry_insert(OBJ_TYPE_HANDLE, h, file_line);
535 os_set_cloexec(h);
538 return h;
539 #else
540 return os_call_getcwd(err);
541 #endif
544 bool os_set_cwd(dir_handle_t h, ajla_error_t *err)
546 #ifndef NO_DIR_HANDLES
547 int r;
548 EINTR_LOOP(r, fchdir(h));
549 if (unlikely(r == -1)) {
550 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
551 fatal_mayfail(e, err, "can't set directory: %s", error_decode(e));
552 return false;
554 #else
555 int r;
556 EINTR_LOOP(r, chdir(h));
557 if (unlikely(r == -1)) {
558 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
559 fatal_mayfail(e, err, "can't set directory '%s': %s", h, error_decode(e));
560 return false;
562 #endif
563 return true;
566 void os_set_original_cwd(void)
568 int r;
569 ajla_error_t sink;
570 if (likely(os_set_cwd(os_cwd, &sink)))
571 return;
572 EINTR_LOOP(r, chdir("/"));
573 if (unlikely(r == -1)) {
574 int er = errno;
575 fatal("unable to select root directory: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
579 static handle_t os_open_internal(dir_handle_t dir, const char *path, int flags, int mode, bool want_dir, ajla_error_t *err)
581 int h;
582 bool abs_path = os_path_is_absolute(path);
584 if (unlikely(!os_test_absolute_path(dir, abs_path, err)))
585 return -1;
587 #ifdef O_DIRECTORY
588 if (want_dir)
589 flags |= O_DIRECTORY;
590 #endif
592 #ifdef HAVE_AT_FUNCTIONS
593 if (likely(have_O_CLOEXEC_openat)) {
594 if (!dir_handle_is_valid(dir)) {
595 EINTR_LOOP(h, open(path, flags | O_CLOEXEC, mode));
596 } else {
597 EINTR_LOOP(h, openat(dir, path, flags | O_CLOEXEC, mode));
599 if (h == -1) {
600 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
601 #ifdef O_PATH
602 if (errno == EACCES && want_dir) {
603 EINTR_LOOP(h, openat(dir, path, flags | O_CLOEXEC | O_PATH, mode));
604 if (h != -1)
605 goto have_it;
607 #endif
608 fatal_mayfail(e, err, "can't open file '%s': %s", path, error_decode(e));
609 } else {
610 goto have_it;
611 have_it:
612 obj_registry_insert(OBJ_TYPE_HANDLE, h, file_line);
614 goto test_dir_ret_h;
616 #endif
617 os_lock_fork(!abs_path);
619 if (!abs_path) {
620 if (unlikely(!os_set_cwd(dir, err))) {
621 h = -1;
622 goto restore_dir_ret;
626 EINTR_LOOP(h, open(path, flags, mode));
627 if (unlikely(h == -1)) {
628 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
629 fatal_mayfail(e, err, "can't open file '%s': %s", path, error_decode(e));
630 goto restore_dir_ret;
632 obj_registry_insert(OBJ_TYPE_HANDLE, h, file_line);
634 os_set_cloexec(h);
636 restore_dir_ret:
637 if (!abs_path) {
638 os_set_original_cwd();
641 os_unlock_fork(!abs_path);
643 #ifdef HAVE_AT_FUNCTIONS
644 test_dir_ret_h:
645 #endif
646 if (likely(h != -1)) {
647 os_stat_t st;
648 if (!want_dir) {
649 if (!(flags & (O_WRONLY | O_RDWR))) {
650 if (unlikely(!os_fstat(h, &st, err))) {
651 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
652 fatal_mayfail(e, err, "fstat on file '%s' failed", path);
653 os_close(h);
654 h = -1;
655 } else if (unlikely(S_ISDIR(st.st_mode))) {
656 ajla_error_t e = error_from_errno(EC_SYSCALL, EISDIR);
657 fatal_mayfail(e, err, "file '%s' is a directory", path);
658 os_close(h);
659 h = -1;
662 } else {
663 #ifndef O_DIRECTORY
664 if (unlikely(!os_fstat(h, &st, err))) {
665 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
666 fatal_mayfail(e, err, "fstat on file '%s' failed", path);
667 os_close(h);
668 h = -1;
669 } else if (unlikely(!S_ISDIR(st.st_mode))) {
670 ajla_error_t e = error_from_errno(EC_SYSCALL, ENOTDIR);
671 fatal_mayfail(e, err, "file '%s' is not a directory", path);
672 os_close(h);
673 h = -1;
675 #endif
678 return h;
681 handle_t os_open(dir_handle_t dir, const char *path, int flags, int mode, ajla_error_t *err)
683 #ifdef OS_DOS
684 flags |= O_BINARY;
685 #endif
686 return os_open_internal(dir, path, flags, mode, false, err);
689 bool os_pipe(handle_t result[2], int nonblock_flags, ajla_error_t *err)
691 int r, i;
692 #ifdef HAVE_PIPE2
693 EINTR_LOOP(r, pipe2(result, O_CLOEXEC | (nonblock_flags == 3 ? O_NONBLOCK : 0)));
694 if (likely(r != -1)) {
695 if (nonblock_flags == 3) {
696 obj_registry_insert(OBJ_TYPE_HANDLE, result[0], file_line);
697 obj_registry_insert(OBJ_TYPE_HANDLE, result[1], file_line);
698 return true;
700 goto set_nonblock;
702 if (errno != ENOSYS) {
703 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
704 fatal_mayfail(e, err, "can't create pipe: %s", error_decode(e));
705 return false;
707 #endif
709 os_lock_fork(false);
710 EINTR_LOOP(r, pipe(result));
711 if (unlikely(r == -1)) {
712 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
713 os_unlock_fork(false);
714 fatal_mayfail(e, err, "can't create pipe: %s", error_decode(e));
715 return false;
717 for (i = 0; i < 2; i++)
718 os_set_cloexec(result[i]);
719 os_unlock_fork(false);
721 #ifdef HAVE_PIPE2
722 set_nonblock:
723 #endif
724 for (i = 0; i < 2; i++) {
725 obj_registry_insert(OBJ_TYPE_HANDLE, result[i], file_line);
726 if (nonblock_flags & (1 << i)) {
727 EINTR_LOOP(r, fcntl(result[i], F_SETFL, O_NONBLOCK));
728 if (unlikely(r == -1)) {
729 int er = errno;
730 fatal("fcntl(F_SETFL, O_NONBLOCK) on a pipe failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
734 return true;
737 void os_close_handle(handle_t h)
739 int r;
740 if (unlikely(h < 0))
741 internal(file_line, "os_close: attempting to close invalid handle %d", h);
742 EINTR_LOOP(r, close(h));
743 if (unlikely(r == -1) && errno == EBADF)
744 internal(file_line, "os_close: closing invalid handle %d", h);
747 void os_close(handle_t h)
749 obj_registry_remove(OBJ_TYPE_HANDLE, h, file_line);
750 os_close_handle(h);
753 static unsigned n_std_handles;
755 unsigned os_n_std_handles(void)
757 return n_std_handles;
760 handle_t os_get_std_handle(unsigned h)
762 return (handle_t)h;
765 handle_t os_number_to_handle(uintptr_t n, bool attr_unused sckt, ajla_error_t *err)
767 if (unlikely(n != (uintptr_t)(int)n) || unlikely((int)n < 0)) {
768 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "invalid handle");
769 return handle_none;
771 obj_registry_insert(OBJ_TYPE_HANDLE, (int)n, file_line);
772 return (int)n;
776 static ssize_t os_rdwr_return(int r, const char *msg, ajla_error_t *err)
778 if (unlikely(r == -1)) {
779 ajla_error_t e;
780 if (errno == EAGAIN || errno == EWOULDBLOCK)
781 return OS_RW_WOULDBLOCK;
782 e = error_from_errno(EC_SYSCALL, errno);
783 fatal_mayfail(e, err, "error %s data: %s", msg, error_decode(e));
784 return OS_RW_ERROR;
786 return r;
789 ssize_t os_read(handle_t h, char *buffer, int size, ajla_error_t *err)
791 ssize_t r;
792 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
793 EINTR_LOOP(r, read(h, buffer, size));
794 return os_rdwr_return(r, "reading", err);
797 ssize_t os_write(handle_t h, const char *buffer, int size, ajla_error_t *err)
799 ssize_t r;
800 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
801 EINTR_LOOP(r, write(h, buffer, size));
803 * https://stackoverflow.com/questions/5656628/what-should-i-do-when-writefd-buf-count-returns-0
804 * Long, long ago, pre-POSIX, some systems returned 0 instead of EAGAIN.
806 if (unlikely(!r) && size)
807 return OS_RW_WOULDBLOCK;
808 return os_rdwr_return(r, "writing", err);
811 ssize_t os_pread(handle_t h, char *buffer, int size, os_off_t off, ajla_error_t *err)
813 ssize_t r;
814 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
815 #ifndef DO_LOCK_HANDLES
816 EINTR_LOOP(r, pread(h, buffer, size, off));
817 #else
818 address_lock(num_to_ptr(h), DEPTH_HANDLE);
819 EINTR_LOOP(off, lseek(h, off, SEEK_SET));
820 if (unlikely(off == -1)) {
821 r = -1;
822 goto ret;
824 EINTR_LOOP(r, read(h, buffer, size));
825 ret:
826 address_unlock(num_to_ptr(h), DEPTH_HANDLE);
827 #endif
828 return os_rdwr_return(r, "preading", err);
831 ssize_t os_pwrite(handle_t h, const char *buffer, int size, os_off_t off, ajla_error_t *err)
833 ssize_t r;
834 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
835 #ifndef DO_LOCK_HANDLES
836 EINTR_LOOP(r, pwrite(h, buffer, size, off));
837 #else
838 address_lock(num_to_ptr(h), DEPTH_HANDLE);
839 EINTR_LOOP(off, lseek(h, off, SEEK_SET));
840 if (unlikely(off == -1)) {
841 r = -1;
842 goto ret;
844 EINTR_LOOP(r, write(h, buffer, size));
845 ret:
846 address_unlock(num_to_ptr(h), DEPTH_HANDLE);
847 #endif
848 return os_rdwr_return(r, "pwriting", err);
851 bool os_lseek(handle_t h, unsigned mode, os_off_t off, os_off_t *result, ajla_error_t *err)
853 int whence;
854 os_off_t res;
855 #ifdef DO_LOCK_HANDLES
856 address_lock(num_to_ptr(h), DEPTH_HANDLE);
857 #endif
858 restart:
859 switch (mode) {
860 case 0:
861 whence = SEEK_SET;
862 break;
863 case 1:
864 whence = SEEK_CUR;
865 break;
866 case 2:
867 whence = SEEK_END;
868 break;
869 case 3:
870 #ifdef SEEK_DATA
871 whence = SEEK_DATA;
872 #else
873 EINTR_LOOP(res, lseek(h, 0, SEEK_END));
874 if (unlikely(res == -1))
875 goto ret_error;
876 if (unlikely(off > res))
877 off = res;
878 *result = off;
879 goto ret_true;
880 #endif
881 break;
882 case 4:
883 #ifdef SEEK_HOLE
884 whence = SEEK_HOLE;
885 #else
886 off = 0;
887 whence = SEEK_END;
888 #endif
889 break;
890 default:internal(file_line, "os_lseek: unsupported mode %u", mode);
891 goto ret_false;
893 EINTR_LOOP(res, lseek(h, off, whence));
894 if (unlikely(res == -1)) {
895 ajla_error_t e;
896 if (errno == EINVAL) {
897 if (mode == 3) {
898 *result = off;
899 goto ret_true;
901 if (mode == 4) {
902 off = 0;
903 mode = 2;
904 goto restart;
907 if (errno == ENXIO && mode >= 3) {
908 off = 0;
909 mode = 2;
910 goto restart;
912 goto ret_error;
913 ret_error:
914 e = error_from_errno(EC_SYSCALL, errno);
915 fatal_mayfail(e, err, "can't lseek: %s", error_decode(e));
916 goto ret_false;
918 *result = res;
919 ret_true:
920 #ifdef DO_LOCK_HANDLES
921 address_unlock(num_to_ptr(h), DEPTH_HANDLE);
922 #endif
923 return true;
924 ret_false:
925 #ifdef DO_LOCK_HANDLES
926 address_unlock(num_to_ptr(h), DEPTH_HANDLE);
927 #endif
928 return false;
931 bool os_ftruncate(handle_t h, os_off_t size, ajla_error_t *err)
933 int r;
934 EINTR_LOOP(r, ftruncate(h, size));
935 if (unlikely(r == -1)) {
936 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
937 fatal_mayfail(e, err, "ftruncate returned an error: %s", error_decode(e));
938 return false;
940 return true;
943 bool os_fallocate(handle_t attr_unused h, os_off_t attr_unused position, os_off_t attr_unused size, ajla_error_t attr_unused *err)
945 #ifdef HAVE_FALLOCATE
946 int r;
947 if (unlikely(!size))
948 return true;
949 /* EINTR may cause infinite loop */
950 #if 1
952 sig_state_t set;
953 os_block_signals(&set);
954 EINTR_LOOP(r, fallocate(h, FALLOC_FL_KEEP_SIZE, position, size));
955 os_unblock_signals(&set);
957 #else
958 r = fallocate(h, FALLOC_FL_KEEP_SIZE, position, size);
959 if (unlikely(r == -1) && errno == EINTR)
960 r = fallocate(h, FALLOC_FL_KEEP_SIZE, position, size);
961 #endif
962 if (unlikely(r == -1) && errno != EINTR && errno != ENOSYS && errno != EOPNOTSUPP) {
963 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
964 fatal_mayfail(e, err, "fallocate returned an error: %s", error_decode(e));
965 return false;
967 #endif
968 return true;
971 bool os_clone_range(handle_t attr_unused src_h, os_off_t attr_unused src_pos, handle_t attr_unused dst_h, os_off_t attr_unused dst_pos, os_off_t attr_unused len, ajla_error_t *err)
973 #ifdef FICLONERANGE
974 int r;
975 struct file_clone_range c;
976 c.src_fd = src_h;
977 c.src_offset = src_pos;
978 c.src_length = len;;
979 c.dest_offset = dst_pos;
980 EINTR_LOOP(r, ioctl(dst_h, FICLONERANGE, &c));
981 if (unlikely(r == -1)) {
982 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
983 fatal_mayfail(e, err, "clone range returned an error: %s", error_decode(e));
984 return false;
986 return true;
987 #endif
988 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "clone not supported");
989 return false;
991 bool os_fsync(handle_t h, unsigned mode, ajla_error_t *err)
993 int r;
994 #ifdef __APPLE__
995 if (mode == 0 || mode == 1) {
996 EINTR_LOOP(r, fcntl(h, F_FULLFSYNC));
997 if (likely(r != -1))
998 goto ret;
1000 #endif
1001 #if defined(HAVE_FDATASYNC) && !defined(__APPLE__)
1002 if (mode == 0) {
1003 EINTR_LOOP(r, fdatasync(h));
1004 goto ret;
1006 #endif
1007 if (mode == 0 || mode == 1) {
1008 EINTR_LOOP(r, fsync(h));
1009 goto ret;
1011 #ifdef HAVE_SYNCFS
1012 if (mode == 2) {
1013 EINTR_LOOP(r, syncfs(h));
1014 goto ret;
1016 #endif
1017 if (mode == 2 || mode == 3) {
1018 sync();
1019 return true;
1021 internal(file_line, "os_fsync: invalid mode %u", mode);
1022 ret:
1023 if (unlikely(r == -1)) {
1024 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
1025 fatal_mayfail(e, err, "fsync returned an error: %s", error_decode(e));
1026 return false;
1028 return true;
1031 #if !defined(OS_DOS)
1032 ssize_t os_read_console_packet(handle_t attr_unused h, struct console_read_packet attr_unused *result, ajla_error_t *err)
1034 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "console packets not supported");
1035 return OS_RW_ERROR;
1038 bool os_write_console_packet(handle_t attr_unused h, struct console_write_packet attr_unused *packet, ajla_error_t *err)
1040 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "console packets not supported");
1041 return false;
1044 dir_handle_t os_dir_root(ajla_error_t *err)
1046 const char *root = "/";
1047 #ifndef NO_DIR_HANDLES
1048 return os_dir_open(dir_none, root, 0, err);
1049 #else
1050 return str_dup(root, -1, err);
1051 #endif
1053 #endif
1055 dir_handle_t os_dir_cwd(ajla_error_t *err)
1057 return os_dir_open(os_cwd, ".", 0, err);
1060 dir_handle_t os_dir_open(dir_handle_t dir, const char *path, int attr_unused flags, ajla_error_t *err)
1062 #ifndef NO_DIR_HANDLES
1063 return os_open_internal(dir, path, O_RDONLY | flags, 0, true, err);
1064 #else
1065 dir_handle_t ret;
1067 if (unlikely(!os_test_absolute_path(dir, os_path_is_absolute(path), err)))
1068 return dir_none;
1070 os_lock_fork(true);
1072 if (dir_handle_is_valid(dir)) {
1073 if (unlikely(!os_set_cwd(dir, err))) {
1074 ret = dir_none;
1075 goto restore_ret;
1079 if (unlikely(!os_set_cwd((dir_handle_t)path, err))) {
1080 ret = dir_none;
1081 goto restore_ret;
1084 ret = os_get_cwd(err);
1086 restore_ret:
1087 os_set_original_cwd();
1089 os_unlock_fork(true);
1090 return ret;
1091 #endif
1094 void os_dir_close(dir_handle_t h)
1096 #ifndef NO_DIR_HANDLES
1097 os_close(h);
1098 #else
1099 mem_free(h);
1100 #endif
1103 char *os_dir_path(dir_handle_t h, ajla_error_t *err)
1105 #ifndef NO_DIR_HANDLES
1106 char *path;
1107 #ifdef __linux__
1108 ajla_error_t sink;
1109 char lnk[25];
1110 snprintf(lnk, sizeof(lnk), "/proc/self/fd/%u", h);
1111 path = os_readlink(dir_none, lnk, &sink);
1112 if (likely(path != NULL)) {
1113 size_t sl, dl;
1114 char *deleted = " (deleted)";
1115 if (unlikely(path[0] != '/')) {
1116 mem_free(path);
1117 goto skip_optimization;
1119 sl = strlen(path);
1120 dl = strlen(deleted);
1121 if (sl >= dl && unlikely(!memcmp(path + sl - dl, deleted, dl))) {
1122 mem_free(path);
1123 goto skip_optimization;
1125 return path;
1127 skip_optimization:
1128 #endif
1129 os_lock_fork(true);
1130 if (unlikely(!os_set_cwd(h, err))) {
1131 path = NULL;
1132 goto unlock_ret;
1134 path = os_call_getcwd(err);
1135 os_set_original_cwd();
1136 unlock_ret:
1137 os_unlock_fork(true);
1138 return path;
1139 #else
1140 return str_dup(h, -1, err);
1141 #endif
1144 static void os_close_DIR(DIR *d)
1146 int r;
1147 EINTR_LOOP(r, closedir(d));
1148 if (unlikely(r))
1149 internal(file_line, "os_close_DIR: closing invalid directory handle: %s", error_decode(error_from_errno(EC_SYSCALL, errno)));
1152 bool os_dir_read(dir_handle_t h, char ***files, size_t *n_files, ajla_error_t *err)
1154 ajla_error_t e;
1155 DIR *d;
1156 #if !defined(NO_DIR_HANDLES)
1157 int er;
1158 os_lock_fork(true);
1159 if (unlikely(!os_set_cwd(h, err))) {
1160 os_set_original_cwd();
1161 os_unlock_fork(true);
1162 return false;
1164 EINTR_LOOP_VAL(d, NULL, opendir("."));
1165 er = errno;
1166 os_set_original_cwd();
1167 os_unlock_fork(true);
1168 errno = er;
1169 #else
1170 EINTR_LOOP_VAL(d, NULL, opendir(h));
1171 #endif
1172 if (unlikely(!d)) {
1173 e = error_from_errno(EC_SYSCALL, errno);
1174 fatal_mayfail(e, err, "can't open directory: %s", error_decode(e));
1175 return false;
1177 if (unlikely(!array_init_mayfail(char *, files, n_files, err))) {
1178 os_close_DIR(d);
1179 return false;
1182 while (1) {
1183 struct dirent *de;
1184 char *fn;
1185 errno = 0;
1186 de = readdir(d);
1187 if (unlikely(!de)) {
1188 if (likely(!errno))
1189 break;
1190 e = error_from_errno(EC_SYSCALL, errno);
1191 fatal_mayfail(e, err, "error reading directory directory: %s", error_decode(e));
1192 os_dir_free(*files, *n_files);
1193 os_close_DIR(d);
1194 return false;
1196 if (unlikely(!strcmp(de->d_name, ".")) ||
1197 unlikely(!strcmp(de->d_name, "..")))
1198 continue;
1199 fn = mem_alloc_mayfail(char *, strlen(de->d_name) + 1, err);
1200 if (unlikely(!fn)) {
1201 os_dir_free(*files, *n_files);
1202 os_close_DIR(d);
1203 return false;
1205 strcpy(fn, de->d_name);
1206 array_add(char *, files, n_files, fn);
1208 os_close_DIR(d);
1209 return true;
1212 void os_dir_free(char **files, size_t n_files)
1214 size_t i;
1215 for (i = 0; i < n_files; i++)
1216 mem_free(files[i]);
1217 mem_free(files);
1221 unsigned os_dev_t_major(dev_t dev)
1223 #if defined(HAVE_SYS_SYSMACROS_H) || defined(major)
1224 return major(dev);
1225 #else
1226 return (dev >> 8) & 0xff;
1227 #endif
1230 unsigned os_dev_t_minor(dev_t dev)
1232 #if defined(HAVE_SYS_SYSMACROS_H) || defined(minor)
1233 return minor(dev);
1234 #else
1235 return dev & 0xff;
1236 #endif
1239 bool os_fstat(handle_t h, os_stat_t *st, ajla_error_t *err)
1241 int r;
1242 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
1243 EINTR_LOOP(r, fstat(h, st));
1244 if (unlikely(r == -1)) {
1245 ajla_error_t e;
1246 if (unlikely(errno == EBADF))
1247 internal(file_line, "os_fstat: invalid handle %d", h);
1248 e = error_from_errno(EC_SYSCALL, errno);
1249 fatal_mayfail(e, err, "can't stat file handle: %s", error_decode(e));
1250 return false;
1252 return true;
1255 bool os_stat(dir_handle_t dir, const char *path, bool attr_unused lnk, os_stat_t *st, ajla_error_t *err)
1257 int r;
1258 bool abs_path = os_path_is_absolute(path);
1260 if (unlikely(!os_test_absolute_path(dir, abs_path, err)))
1261 return false;
1263 #ifdef HAVE_AT_FUNCTIONS
1264 if (likely(have_O_CLOEXEC_openat) && dir_handle_is_valid(dir)) {
1265 EINTR_LOOP(r, fstatat(dir, path, st, lnk ? AT_SYMLINK_NOFOLLOW : 0));
1266 if (unlikely(r == -1)) {
1267 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
1268 fatal_mayfail(e, err, "can't open file '%s': %s", path, error_decode(e));
1270 return r != -1;
1272 #endif
1273 if (!abs_path) {
1274 os_lock_fork(true);
1275 if (unlikely(!os_set_cwd(dir, err))) {
1276 r = -1;
1277 goto unlock_ret_r;
1281 #ifdef HAVE_LSTAT
1282 EINTR_LOOP(r, (!lnk ? stat : lstat)(path, st));
1283 #else
1284 EINTR_LOOP(r, stat(path, st));
1285 #endif
1286 if (unlikely(r == -1)) {
1287 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
1288 fatal_mayfail(e, err, "can't open file '%s': %s", path, error_decode(e));
1291 unlock_ret_r:
1292 if (!abs_path) {
1293 os_set_original_cwd();
1294 os_unlock_fork(true);
1297 return r != -1;
1300 #if (defined(HAVE_FSTATFS) && !defined(HAVE_FSTATVFS)) || (defined(HAVE_STATFS) && !defined(HAVE_STATVFS))
1301 static inline void attr_unused statfs_2_statvfs(struct statfs *stfs, os_statvfs_t *st)
1303 memset(st, 0, sizeof(os_statvfs_t));
1304 #if defined(__linux__)
1305 st->f_bsize = stfs->f_bsize;
1306 st->f_frsize = stfs->f_bsize;
1307 #elif defined(__FreeBSD__) || defined(__OpenBSD__)
1308 st->f_bsize = stfs->f_iosize;
1309 st->f_frsize = stfs->f_bsize;
1310 #else
1311 st->f_bsize = stfs->f_bsize;
1312 st->f_frsize = stfs->f_bsize;
1313 #endif
1314 st->f_blocks = stfs->f_blocks;
1315 st->f_bfree = stfs->f_bfree;
1316 st->f_bavail = stfs->f_bavail;
1317 st->f_files = stfs->f_files;
1318 st->f_ffree = stfs->f_ffree;
1319 st->f_favail = stfs->f_ffree;
1320 memcpy(&st->f_fsid, &stfs->f_fsid, minimum(sizeof(st->f_fsid), sizeof(stfs->f_fsid)));
1321 #if defined(__linux__)
1322 st->f_namemax = stfs->f_namelen;
1323 #elif defined(__FreeBSD__) || defined(__OpenBSD__)
1324 st->f_namemax = stfs->f_namemax;
1325 #else
1326 st->f_namemax = 255;
1327 #endif
1329 #endif
1331 bool os_fstatvfs(handle_t h, os_statvfs_t *st, ajla_error_t *err)
1333 ajla_error_t e;
1334 int r;
1335 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
1336 #if defined(HAVE_FSTATVFS)
1337 EINTR_LOOP(r, fstatvfs(h, st));
1338 if (unlikely(r == -1))
1339 goto err;
1340 return true;
1341 #elif defined(HAVE_FSTATFS)
1343 struct statfs stfs;
1344 EINTR_LOOP(r, fstatfs(h, &stfs));
1345 if (unlikely(r == -1))
1346 goto err;
1347 statfs_2_statvfs(&stfs, st);
1348 return true;
1350 #endif
1351 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "the system doesn't support mprotect");
1352 return false;
1354 goto err;
1355 err:
1356 if (unlikely(errno == EBADF))
1357 internal(file_line, "os_fstatvfs: invalid handle %d", h);
1358 e = error_from_errno(EC_SYSCALL, errno);
1359 fatal_mayfail(e, err, "can't fstatvfs file handle: %s", error_decode(e));
1360 return false;
1363 bool os_dstatvfs(dir_handle_t dir, os_statvfs_t *st, ajla_error_t *err)
1365 ajla_error_t attr_unused e;
1366 int attr_unused r;
1367 #ifndef NO_DIR_HANDLES
1368 return os_fstatvfs(dir, st, err);
1369 #elif defined(HAVE_STATVFS)
1370 EINTR_LOOP(r, statvfs(dir, st));
1371 if (unlikely(r == -1))
1372 goto err;
1373 return true;
1374 #elif defined(HAVE_STATFS)
1376 struct statfs stfs;
1377 EINTR_LOOP(r, statfs(dir, &stfs));
1378 if (unlikely(r == -1))
1379 goto err;
1380 statfs_2_statvfs(&stfs, st);
1381 return true;
1383 #endif
1384 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "the system doesn't support mprotect");
1385 return false;
1387 goto err;
1388 err:
1389 e = error_from_errno(EC_SYSCALL, errno);
1390 fatal_mayfail(e, err, "can't statvfs directory: %s", error_decode(e));
1391 return false;
1394 char *os_readlink(dir_handle_t attr_unused dir, const char attr_unused *path, ajla_error_t *err)
1396 #ifdef HAVE_READLINK
1397 size_t buf_size = 32;
1398 ssize_t r;
1399 char *buf;
1400 bool abs_path = os_path_is_absolute(path);
1402 if (unlikely(!os_test_absolute_path(dir, abs_path, err)))
1403 return NULL;
1405 alloc_larger:
1406 buf = mem_alloc_mayfail(char *, buf_size, err);
1407 if (unlikely(!buf))
1408 return NULL;
1410 #ifdef HAVE_AT_FUNCTIONS
1411 if (likely(have_O_CLOEXEC_openat)) {
1412 EINTR_LOOP(r, readlinkat(dir, path, buf, buf_size));
1413 goto proc_r;
1415 #endif
1416 if (!abs_path) {
1417 os_lock_fork(true);
1418 if (unlikely(!os_set_cwd(dir, err))) {
1419 os_unlock_fork(true);
1420 mem_free(buf);
1421 return NULL;
1425 EINTR_LOOP(r, readlink(path, buf, buf_size));
1427 if (!abs_path) {
1428 int e = errno;
1429 os_set_original_cwd();
1430 os_unlock_fork(true);
1431 errno = e;
1434 #ifdef HAVE_AT_FUNCTIONS
1435 proc_r:
1436 #endif
1437 if (unlikely(r == -1)) {
1438 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
1439 fatal_mayfail(e, err, "can't read link '%s': %s", path, error_decode(e));
1440 mem_free(buf);
1441 return NULL;
1443 if (unlikely((size_t)r == buf_size)) {
1444 mem_free(buf);
1445 buf_size *= 2;
1446 if (unlikely((buf_size * 2) == 0)) {
1447 fatal_mayfail(error_ajla(EC_ASYNC, AJLA_ERROR_SIZE_OVERFLOW), err, "overflow when allocating readlink buffer");
1448 return NULL;
1450 goto alloc_larger;
1453 buf[r] = 0;
1455 return buf;
1456 #else
1457 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "readlink not supported");
1458 return NULL;
1459 #endif
1462 bool os_dir_action(dir_handle_t dir, const char *path, int action, int mode, ajla_time_t attr_unused dev_major, ajla_time_t attr_unused dev_minor, const char *syml, ajla_error_t *err)
1464 int r;
1465 bool abs_path = os_path_is_absolute(path);
1467 if (unlikely((mode & ~07777) != 0)) {
1468 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "invalid mode: %d", mode);
1469 return false;
1472 if (unlikely(!os_test_absolute_path(dir, abs_path, err)))
1473 return false;
1475 #ifdef HAVE_AT_FUNCTIONS
1476 if (likely(have_O_CLOEXEC_openat)) {
1477 if (!dir_handle_is_valid(dir))
1478 dir = AT_FDCWD;
1479 switch (action) {
1480 case IO_Action_Rm:
1481 EINTR_LOOP(r, unlinkat(dir, path, 0));
1482 break;
1483 case IO_Action_Rm_Dir:
1484 EINTR_LOOP(r, unlinkat(dir, path, AT_REMOVEDIR));
1485 break;
1486 case IO_Action_Mk_Dir:
1487 EINTR_LOOP(r, mkdirat(dir, path, mode));
1488 break;
1489 case IO_Action_Mk_Pipe:
1490 EINTR_LOOP(r, mknodat(dir, path, mode | S_IFIFO, 0));
1491 break;
1492 case IO_Action_Mk_Socket:
1493 EINTR_LOOP(r, mknodat(dir, path, mode | S_IFSOCK, 0));
1494 break;
1495 case IO_Action_Mk_CharDev:
1496 #if defined(HAVE_SYS_SYSMACROS_H) || defined(makedev)
1497 EINTR_LOOP(r, mknodat(dir, path, mode | S_IFCHR, makedev(dev_major, dev_minor)));
1498 #else
1499 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "mkchardev not supported");
1500 return false;
1501 #endif
1502 break;
1503 case IO_Action_Mk_BlockDev:
1504 #if defined(HAVE_SYS_SYSMACROS_H) || defined(makedev)
1505 EINTR_LOOP(r, mknodat(dir, path, mode | S_IFBLK, makedev(dev_major, dev_minor)));
1506 #else
1507 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "mkblockdev not supported");
1508 return false;
1509 #endif
1510 break;
1511 case IO_Action_Mk_SymLink:
1512 EINTR_LOOP(r, symlinkat(syml, dir, path));
1513 break;
1514 case IO_Action_ChMod:
1515 EINTR_LOOP(r, fchmodat(dir, path, mode, 0));
1516 break;
1517 case IO_Action_ChOwn:
1518 EINTR_LOOP(r, fchownat(dir, path, dev_major, dev_minor, 0));
1519 break;
1520 case IO_Action_LChOwn:
1521 EINTR_LOOP(r, fchownat(dir, path, dev_major, dev_minor, AT_SYMLINK_NOFOLLOW));
1522 break;
1523 case IO_Action_UTime:
1524 case IO_Action_LUTime: {
1525 struct timespec ts[2];
1526 ts[0].tv_sec = dev_minor / 1000000;
1527 ts[0].tv_nsec = dev_minor % 1000000 * 1000;
1528 ts[1].tv_sec = dev_major / 1000000;
1529 ts[1].tv_nsec = dev_major % 1000000 * 1000;
1530 EINTR_LOOP(r, utimensat(dir, path, ts, action == IO_Action_UTime ? 0 : AT_SYMLINK_NOFOLLOW));
1531 break;
1533 default:
1534 internal(file_line, "os_dir_action: invalid action %d", action);
1536 goto proc_r;
1538 #endif
1540 if (!abs_path) {
1541 os_lock_fork(true);
1542 if (unlikely(!os_set_cwd(dir, err))) {
1543 os_unlock_fork(true);
1544 return false;
1548 switch (action) {
1549 case IO_Action_Rm:
1550 EINTR_LOOP(r, unlink(path));
1551 break;
1552 case IO_Action_Rm_Dir:
1553 EINTR_LOOP(r, rmdir(path));
1554 break;
1555 case IO_Action_Mk_Dir:
1556 EINTR_LOOP(r, mkdir(path, mode));
1557 #ifdef __minix__
1559 * Minix 3 returns EACCES when attempting to make the
1560 * home directory. So we test if the directory exists
1561 * and return EEXIST if it does.
1563 if (r == -1 && errno == EACCES) {
1564 struct stat st;
1565 int rr;
1566 EINTR_LOOP(rr, stat(path, &st));
1567 if (!rr)
1568 errno = EEXIST;
1569 else
1570 errno = EACCES;
1572 #endif
1573 break;
1574 case IO_Action_Mk_Pipe:
1575 #ifdef HAVE_MKNOD
1576 EINTR_LOOP(r, mknod(path, mode | S_IFIFO, 0));
1577 #else
1578 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "mkpipe not supported");
1579 goto ret_false;
1580 #endif
1581 break;
1582 case IO_Action_Mk_Socket:
1583 #if defined(HAVE_MKNOD) && defined(S_IFSOCK)
1584 EINTR_LOOP(r, mknod(path, mode | S_IFSOCK, 0));
1585 #else
1586 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "mksocket not supported");
1587 goto ret_false;
1588 #endif
1589 break;
1590 case IO_Action_Mk_CharDev:
1591 #if defined(HAVE_MKNOD) && (defined(HAVE_SYS_SYSMACROS_H) || defined(makedev))
1592 EINTR_LOOP(r, mknod(path, mode | S_IFCHR, makedev(dev_major, dev_minor)));
1593 #else
1594 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "mkchardev not supported");
1595 goto ret_false;
1596 #endif
1597 break;
1598 case IO_Action_Mk_BlockDev:
1599 #if defined(HAVE_MKNOD) && (defined(HAVE_SYS_SYSMACROS_H) || defined(makedev))
1600 EINTR_LOOP(r, mknod(path, mode | S_IFBLK, makedev(dev_major, dev_minor)));
1601 #else
1602 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "mkblockdev not supported");
1603 goto ret_false;
1604 #endif
1605 break;
1606 case IO_Action_Mk_SymLink:
1607 #ifdef HAVE_SYMLINK
1608 EINTR_LOOP(r, symlink(syml, path));
1609 #else
1610 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "symlink not supported");
1611 goto ret_false;
1612 #endif
1613 break;
1614 case IO_Action_ChMod:
1615 EINTR_LOOP(r, chmod(path, mode));
1616 break;
1617 case IO_Action_LChOwn: {
1618 #ifdef HAVE_LCHOWN
1619 EINTR_LOOP(r, lchown(path, dev_major, dev_minor));
1620 break;
1621 #else
1622 struct stat st;
1623 EINTR_LOOP(r, lstat(path, &st));
1624 if (unlikely(r))
1625 break;
1626 if (S_ISLNK(st.st_mode))
1627 break;
1628 #endif
1630 /*-fallthrough*/
1631 case IO_Action_ChOwn:
1632 #ifdef HAVE_CHOWN
1633 EINTR_LOOP(r, chown(path, dev_major, dev_minor));
1634 #else
1635 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "chown not supported");
1636 goto ret_false;
1637 #endif
1638 break;
1639 case IO_Action_LUTime: {
1640 struct stat st;
1641 EINTR_LOOP(r, lstat(path, &st));
1642 if (unlikely(r))
1643 break;
1644 if (S_ISLNK(st.st_mode)) {
1645 r = -1;
1646 errno = -ELOOP;
1647 break;
1650 /*-fallthrough*/
1651 case IO_Action_UTime: {
1652 #if defined(HAVE_UTIMES)
1653 struct timeval ts[2];
1654 ts[0].tv_sec = dev_minor / 1000000;
1655 ts[0].tv_usec = dev_minor % 1000000;
1656 ts[1].tv_sec = dev_major / 1000000;
1657 ts[1].tv_usec = dev_major % 1000000;
1658 EINTR_LOOP(r, utimes(cast_ptr(char *, path), ts));
1659 break;
1660 #elif defined(HAVE_UTIME)
1661 struct utimbuf tm;
1662 tm.actime = dev_minor / 1000000;
1663 tm.modtime = dev_major / 1000000;
1664 EINTR_LOOP(r, times(path, &tm));
1665 break;
1666 #endif
1667 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "utime not supported");
1668 goto ret_false;
1670 default:
1671 internal(file_line, "os_dir_action: invalid action %d", action);
1674 if (!abs_path) {
1675 int e = errno;
1676 os_set_original_cwd();
1677 os_unlock_fork(true);
1678 errno = e;
1681 #ifdef HAVE_AT_FUNCTIONS
1682 proc_r:
1683 #endif
1684 if (unlikely(r == -1)) {
1685 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
1686 fatal_mayfail(e, err, "can't perform action %d on '%s': %s", action, path, error_decode(e));
1687 return false;
1689 return true;
1691 ret_false:
1692 if (!abs_path) {
1693 int e = errno;
1694 os_set_original_cwd();
1695 os_unlock_fork(true);
1696 errno = e;
1698 return false;
1701 bool os_dir2_action(dir_handle_t dest_dir, const char *dest_path, int action, dir_handle_t src_dir, const char *src_path, ajla_error_t *err)
1703 bool ret;
1704 int r;
1705 char *dest_final_path = NULL;
1706 char *src_final_path = NULL;
1707 bool abs_dest_path = os_path_is_absolute(dest_path);
1708 bool abs_src_path = os_path_is_absolute(src_path);
1710 if (unlikely(!os_test_absolute_path(dest_dir, abs_dest_path, err)))
1711 return false;
1712 if (unlikely(!os_test_absolute_path(src_dir, abs_src_path, err)))
1713 return false;
1715 #ifdef HAVE_AT_FUNCTIONS
1716 if (likely(have_O_CLOEXEC_openat)) {
1717 if (!dir_handle_is_valid(dest_dir))
1718 dest_dir = AT_FDCWD;
1719 if (!dir_handle_is_valid(src_dir))
1720 src_dir = AT_FDCWD;
1721 switch (action) {
1722 case IO_Action_Mk_Link:
1723 EINTR_LOOP(r, linkat(src_dir, src_path, dest_dir, dest_path, 0));
1724 break;
1725 case IO_Action_Rename:
1726 EINTR_LOOP(r, renameat(src_dir, src_path, dest_dir, dest_path));
1727 break;
1728 default:
1729 internal(file_line, "os_dir2_action: invalid action %d", action);
1732 goto proc_r;
1734 #endif
1735 if (abs_dest_path) {
1736 dest_final_path = str_dup(dest_path, -1, err);
1737 if (unlikely(!dest_final_path)) {
1738 ret = false;
1739 goto free_ret;
1741 } else {
1742 char *dest_dir_path = os_dir_path(dest_dir, err);
1743 if (unlikely(!dest_dir_path)) {
1744 ret = false;
1745 goto free_ret;
1747 dest_final_path = os_join_paths(dest_dir_path, dest_path, true, err);
1748 if (unlikely(!dest_final_path)) {
1749 mem_free(dest_dir_path);
1750 ret = false;
1751 goto free_ret;
1753 mem_free(dest_dir_path);
1754 dest_dir_path = NULL;
1756 if (abs_src_path) {
1757 src_final_path = str_dup(src_path, -1, err);
1758 if (unlikely(!src_final_path)) {
1759 ret = false;
1760 goto free_ret;
1762 } else {
1763 char *src_dir_path = os_dir_path(src_dir, err);
1764 if (unlikely(!src_dir_path)) {
1765 ret = false;
1766 goto free_ret;
1768 src_final_path = os_join_paths(src_dir_path, src_path, true, err);
1769 if (unlikely(!src_final_path)) {
1770 mem_free(src_dir_path);
1771 ret = false;
1772 goto free_ret;
1774 mem_free(src_dir_path);
1775 src_dir_path = NULL;
1778 switch (action) {
1779 case IO_Action_Mk_Link:
1780 #ifdef HAVE_LINK
1781 EINTR_LOOP(r, link(src_final_path, dest_final_path));
1782 #else
1783 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "link not supported");
1784 ret = false;
1785 goto free_ret;
1786 #endif
1787 break;
1788 case IO_Action_Rename:
1789 EINTR_LOOP(r, rename(src_final_path, dest_final_path));
1790 break;
1791 default:
1792 internal(file_line, "os_dir2_action: invalid action %d", action);
1796 #ifdef HAVE_AT_FUNCTIONS
1797 proc_r:
1798 #endif
1799 if (unlikely(r == -1)) {
1800 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
1801 fatal_mayfail(e, err, "can't perform action %d on '%s' and '%s': %s", action, src_path, dest_path, error_decode(e));
1802 ret = false;
1803 goto free_ret;
1805 ret = true;
1807 free_ret:
1808 if (dest_final_path)
1809 mem_free(dest_final_path);
1810 if (src_final_path)
1811 mem_free(src_final_path);
1812 return ret;
1815 #if !defined(OS_DOS)
1817 bool os_drives(char **drives, size_t *drives_l, ajla_error_t *err)
1819 #if defined(OS_CYGWIN)
1820 uint32_t mask = GetLogicalDrives();
1821 return os_drives_bitmap(mask, drives, drives_l, err);
1822 #elif defined(HAVE_GETFSSTAT) || defined(HAVE_GETVFSFSSTAT)
1823 int r, i;
1824 int n_entries;
1825 #if defined(HAVE_GETVFSSTAT)
1826 struct statvfs *buf;
1827 #else
1828 struct statfs *buf;
1829 #endif
1831 n_entries = 2;
1832 again:
1833 #if defined(HAVE_GETVFSSTAT)
1834 buf = mem_alloc_array_mayfail(mem_alloc_mayfail, struct statvfs *, 0, 0, n_entries, sizeof(struct statvfs), err);
1835 #else
1836 buf = mem_alloc_array_mayfail(mem_alloc_mayfail, struct statfs *, 0, 0, n_entries, sizeof(struct statfs), err);
1837 #endif
1838 if (unlikely(!buf))
1839 return false;
1840 #if defined(HAVE_GETVFSSTAT)
1841 EINTR_LOOP(r, getvfsstat(buf, sizeof(struct statvfs) * n_entries, ST_NOWAIT));
1842 #else
1843 EINTR_LOOP(r, getfsstat(buf, sizeof(struct statfs) * n_entries, MNT_NOWAIT));
1844 #endif
1845 if (unlikely(r == -1)) {
1846 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
1847 fatal_mayfail(e, err, "getfsstat failed: %s", error_decode(e));
1848 mem_free(buf);
1849 return false;
1851 if (r >= n_entries) {
1852 mem_free(buf);
1853 n_entries *= 2U;
1854 if (unlikely(n_entries < 0)) {
1855 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_SIZE_OVERFLOW), err, "getfsstat buffer overflow");
1856 return false;
1858 goto again;
1861 if (unlikely(!array_init_mayfail(char, drives, drives_l, err))) {
1862 mem_free(buf);
1863 return false;
1866 for (i = 0; i < r; i++) {
1867 char *str;
1868 size_t str_l;
1869 if (buf[i].f_blocks <= 2)
1870 continue;
1871 str = buf[i].f_mntonname;
1872 str_l = strlen(str) + 1;
1873 if (unlikely(!array_add_multiple_mayfail(char, drives, drives_l, str, str_l, NULL, err))) {
1874 mem_free(buf);
1875 return false;
1879 mem_free(buf);
1880 return true;
1881 #else
1882 if (unlikely(!array_init_mayfail(char, drives, drives_l, err)))
1883 return false;
1884 return true;
1885 #endif
1888 #endif
1891 bool os_tcgetattr(handle_t h, os_termios_t *t, ajla_error_t *err)
1893 int r;
1894 EINTR_LOOP(r, tcgetattr(h, t));
1895 if (unlikely(r == -1)) {
1896 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
1897 fatal_mayfail(e, err, "tcgetattr failed: %s", error_decode(e));
1898 return false;
1900 return true;
1903 bool os_tcsetattr(handle_t h, const os_termios_t *t, ajla_error_t *err)
1905 int r;
1906 EINTR_LOOP(r, tcsetattr(h, TCSANOW, cast_ptr(os_termios_t *, t)));
1907 if (unlikely(r == -1)) {
1908 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
1909 fatal_mayfail(e, err, "tcsetattr failed: %s", error_decode(e));
1910 return false;
1912 return true;
1915 void os_tcflags(os_termios_t *t, int flags)
1917 if (flags & IO_Stty_Flag_Raw) {
1918 #ifdef HAVE_CFMAKERAW
1919 cfmakeraw(t);
1920 t->c_cc[VMIN] = 1;
1921 #else
1922 t->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
1923 t->c_oflag &= ~OPOST;
1924 t->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
1925 t->c_cflag &= ~(CSIZE|PARENB);
1926 t->c_cflag |= CS8;
1927 t->c_cc[VMIN] = 1;
1928 t->c_cc[VTIME] = 0;
1929 #endif
1931 if (flags & IO_Stty_Flag_Noecho)
1932 t->c_lflag &= ~ECHO;
1933 else
1934 t->c_lflag |= ECHO;
1935 if (flags & IO_Stty_Flag_Nosignal)
1936 t->c_lflag &= ~ISIG;
1937 else
1938 t->c_lflag |= ISIG;
1939 if (flags & IO_Stty_Flag_NoCRLF)
1940 t->c_oflag &= ~OPOST;
1941 else
1942 t->c_oflag |= OPOST;
1945 bool os_tty_size(handle_t h, int *nx, int *ny, int *ox, int *oy, ajla_error_t *err)
1947 int r;
1948 struct winsize ws;
1949 signal_seq_t attr_unused seq;
1951 EINTR_LOOP(r, ioctl(h, TIOCGWINSZ, &ws));
1952 if (unlikely(r == -1)) {
1953 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
1954 fatal_mayfail(e, err, "ioctl(TIOCGWINSZ) failed: %s", error_decode(e));
1955 return false;
1958 *nx = ws.ws_col;
1959 *ny = ws.ws_row;
1960 *ox = 0;
1961 *oy = 0;
1963 return true;
1967 static char *os_path_to_exe;
1969 static void os_init_path_to_exe(void)
1971 size_t i, sep;
1972 char *path, *component, *test_path;
1973 ajla_error_t sink;
1974 os_stat_t st;
1975 dir_handle_t dh;
1976 #ifdef __linux__
1977 char *ptexe = os_readlink(dir_none, "/proc/self/exe", &sink);
1978 if (likely(ptexe != NULL)) {
1979 if (likely(ptexe[0] == '/')) {
1980 sep = 0;
1981 for (i = 0; ptexe[i]; i++)
1982 if (unlikely(os_is_path_separator(ptexe[i])))
1983 sep = i + !i;
1984 ptexe[sep] = 0;
1985 os_path_to_exe = ptexe;
1986 return;
1988 mem_free(ptexe);
1990 #endif
1991 sep = 0;
1992 for (i = 0; arg0[i]; i++)
1993 if (unlikely(os_is_path_separator(arg0[i])))
1994 sep = i + 1;
1995 if (sep) {
1996 component = str_dup(arg0, sep, NULL);
1997 goto get_abs_path;
2000 path = getenv("PATH");
2001 if (!path) {
2002 component = str_dup(".", -1, NULL);
2003 goto get_abs_path;
2005 next_component:
2006 i = 0;
2007 while (path[i] && !os_is_env_separator(path[i]))
2008 i++;
2009 component = str_dup(path, i, NULL);
2010 test_path = os_join_paths(component, arg0, true, NULL);
2011 if (os_stat(os_cwd, test_path, false, &st, &sink)) {
2012 mem_free(test_path);
2013 goto get_abs_path;
2015 mem_free(test_path);
2016 mem_free(component);
2017 if (path[i]) {
2018 path += i + 1;
2019 goto next_component;
2021 warning("could not find executable in path");
2022 component = str_dup(".", -1, NULL);
2023 get_abs_path:
2024 if (os_path_is_absolute(component)) {
2025 os_path_to_exe = component;
2026 return;
2028 dh = os_dir_open(os_cwd, component, 0, NULL);
2029 os_path_to_exe = os_dir_path(dh, NULL);
2030 os_dir_close(dh);
2031 mem_free(component);
2034 const char *os_get_path_to_exe(void)
2036 return os_path_to_exe;
2040 ajla_time_t os_time_t_to_ajla_time(time_t sec)
2042 return (ajla_time_t)sec * 1000000;
2045 static ajla_time_t os_timeval_to_ajla_time(const struct timeval *tv)
2047 return os_time_t_to_ajla_time(tv->tv_sec) + tv->tv_usec;
2050 #ifdef HAVE_STRUCT_TIMESPEC
2051 ajla_time_t os_timespec_to_ajla_time(const struct timespec *ts)
2053 return os_time_t_to_ajla_time(ts->tv_sec) + ts->tv_nsec / 1000;
2055 #endif
2057 ajla_time_t os_time_real(void)
2059 int r;
2060 struct timeval tv;
2061 EINTR_LOOP(r, gettimeofday(&tv, NULL));
2062 if (unlikely(r == -1)) {
2063 int e = errno;
2064 fatal("gettimeofday failed: %d, %s", e, error_decode(error_from_errno(EC_SYSCALL, e)));
2066 return os_timeval_to_ajla_time(&tv);
2069 ajla_time_t os_time_monotonic(void)
2071 #if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_MONOTONIC)
2072 int r;
2073 struct timespec ts;
2074 EINTR_LOOP(r, clock_gettime(CLOCK_MONOTONIC, &ts));
2075 if (unlikely(r == -1)) {
2076 int e = errno;
2077 fatal("clock_gettime(%d) failed: %d, %s", (int)CLOCK_MONOTONIC, e, error_decode(error_from_errno(EC_SYSCALL, e)));
2079 return os_timespec_to_ajla_time(&ts);
2080 #else
2081 return os_time_real();
2082 #endif
2086 #if !defined(OS_DOS)
2088 static bool spawn_process_handles(unsigned n_handles, handle_t *src, int *target)
2090 int r;
2091 unsigned i;
2092 handle_t max_handle = 3;
2093 for (i = 0; i < n_handles; i++) {
2094 if (unlikely(src[i] >= signed_maximum(int) / 2) ||
2095 unlikely(target[i] >= signed_maximum(int) / 2))
2096 return false;
2097 if (src[i] >= max_handle) max_handle = src[i] + 1;
2098 if (target[i] >= max_handle) max_handle = target[i] + 1;
2100 for (i = 0; i < n_handles; i++) {
2101 EINTR_LOOP(r, dup2(src[i], max_handle + i));
2102 if (unlikely(r == -1))
2103 return false;
2104 /*os_close_handle(src[i]);*/
2106 for (i = 0; i < n_handles; i++) {
2107 EINTR_LOOP(r, close(src[i]));
2109 EINTR_LOOP(r, close(0));
2110 EINTR_LOOP(r, close(1));
2111 EINTR_LOOP(r, close(2));
2112 for (i = 0; i < n_handles; i++) {
2113 EINTR_LOOP(r, dup2(max_handle + i, target[i]));
2114 if (unlikely(r == -1))
2115 return false;
2116 os_close_handle(max_handle + i);
2118 for (i = 0; i < n_handles; i++) {
2119 EINTR_LOOP(r, fcntl(target[i], F_GETFL));
2120 if (likely(r >= 0) && r & O_NONBLOCK) {
2121 int ir;
2122 r &= ~O_NONBLOCK;
2123 EINTR_LOOP(ir, fcntl(target[i], F_SETFL, r));
2126 return true;
2129 static bool os_fork(dir_handle_t wd, const char *path, unsigned n_handles, handle_t *src, int *target, char * const args[], char * const env[], pid_t *pid, ajla_error_t *err)
2131 #ifdef __linux__
2132 sig_state_t set;
2133 #endif
2134 pid_t p;
2135 int r;
2136 os_lock_fork(true);
2137 #ifdef __linux__
2138 os_block_signals(&set);
2139 #endif
2140 EINTR_LOOP(p, fork());
2141 #ifdef __linux__
2142 os_unblock_signals(&set);
2143 #endif
2144 if (!p) {
2145 ajla_error_t sink;
2146 if (unlikely(!os_set_cwd(wd, &sink)))
2147 _exit(127);
2148 if (unlikely(!spawn_process_handles(n_handles, src, target)))
2149 _exit(127);
2150 os_unblock_all_signals();
2151 EINTR_LOOP(r, execve(path, args, env));
2152 _exit(127);
2154 os_unlock_fork(true);
2155 if (p == -1) {
2156 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
2157 fatal_mayfail(e, err, "can't spawn process '%s': %s", path, error_decode(e));
2158 return false;
2160 *pid = p;
2161 return true;
2164 struct proc_handle {
2165 struct tree_entry entry;
2166 pid_t pid;
2167 bool fired;
2168 bool detached;
2169 int status;
2170 int sigchld;
2171 struct list wait_list;
2174 static struct tree proc_tree;
2175 static mutex_t proc_tree_mutex;
2177 static inline void proc_lock(void)
2179 mutex_lock(&proc_tree_mutex);
2182 static inline void proc_unlock(void)
2184 mutex_unlock(&proc_tree_mutex);
2187 static void proc_handle_free(struct proc_handle *ph)
2189 os_signal_unhandle(ph->sigchld);
2190 mem_free(ph);
2193 static int proc_handle_compare(const struct tree_entry *e, uintptr_t pid)
2195 const struct proc_handle *ph = get_struct(e, struct proc_handle, entry);
2196 if (unlikely(ph->pid == (pid_t)pid)) return 0;
2197 if (ph->pid > (pid_t)pid) return 1;
2198 return -1;
2201 struct proc_handle *os_proc_spawn(dir_handle_t wd, const char *path, size_t n_handles, handle_t *src, int *target, char * const args[], char *envc, ajla_error_t *err)
2203 struct proc_handle *ph;
2204 signal_seq_t seq;
2205 struct tree_insert_position ins;
2206 struct tree_entry *e;
2208 char **env;
2209 size_t env_l;
2211 if (unlikely(!array_init_mayfail(char *, &env, &env_l, err)))
2212 return NULL;
2213 while (*envc) {
2214 if (unlikely(!array_add_mayfail(char *, &env, &env_l, envc, NULL, err)))
2215 return NULL;
2216 envc = strchr(envc, 0) + 1;
2218 if (unlikely(!array_add_mayfail(char *, &env, &env_l, NULL, NULL, err)))
2219 return NULL;
2221 ph = mem_alloc_mayfail(struct proc_handle *, sizeof(struct proc_handle), err);
2222 if (unlikely(!ph)) {
2223 mem_free(env);
2224 return NULL;
2226 ph->fired = false;
2227 ph->detached = false;
2228 list_init(&ph->wait_list);
2229 ph->sigchld = os_signal_handle("SIGCHLD", &seq, err);
2230 if (unlikely(ph->sigchld < 0)) {
2231 mem_free(env);
2232 mem_free(ph);
2233 return NULL;
2235 if (unlikely(!ph->sigchld))
2236 iomux_enable_poll();
2238 proc_lock();
2240 if (unlikely(!os_fork(wd, path, n_handles, src, target, args, env, &ph->pid, err))) {
2241 proc_unlock();
2242 mem_free(env);
2243 proc_handle_free(ph);
2244 return NULL;
2247 e = tree_find_for_insert(&proc_tree, proc_handle_compare, ph->pid, &ins);
2248 if (unlikely(e != NULL)) {
2249 fatal("pid %ld is already present in the tree", (long)ph->pid);
2252 tree_insert_after_find(&ph->entry, &ins);
2254 proc_unlock();
2256 mem_free(env);
2258 return ph;
2261 void os_proc_free_handle(struct proc_handle *ph)
2263 proc_lock();
2264 ajla_assert_lo(list_is_empty(&ph->wait_list), (file_line, "os_proc_free_handle: freeing handle when there are processes waiting for it"));
2265 if (ph->fired) {
2266 proc_unlock();
2267 proc_handle_free(ph);
2268 } else {
2269 ph->detached = true;
2270 proc_unlock();
2274 bool os_proc_register_wait(struct proc_handle *ph, mutex_t **mutex_to_lock, struct list *list_entry, int *status)
2276 proc_lock();
2277 if (ph->fired) {
2278 *status = ph->status;
2279 proc_unlock();
2280 return true;
2281 } else {
2282 *mutex_to_lock = &proc_tree_mutex;
2283 list_add(&ph->wait_list, list_entry);
2284 proc_unlock();
2285 return false;
2289 static void process_pid_and_status(pid_t pid, int status)
2291 struct tree_entry *e;
2292 struct proc_handle *ph;
2294 proc_lock();
2296 e = tree_find(&proc_tree, proc_handle_compare, pid);
2297 if (!e) {
2298 proc_unlock();
2299 return;
2302 ph = get_struct(e, struct proc_handle, entry);
2303 ph->fired = true;
2305 if (WIFEXITED(status)) {
2306 ph->status = WEXITSTATUS(status);
2307 } else if (WIFSIGNALED(status)) {
2308 ph->status = -WTERMSIG(status);
2309 } else {
2310 proc_unlock();
2311 return;
2314 tree_delete(&ph->entry);
2316 if (!ph->detached) {
2317 call(wake_up_wait_list)(&ph->wait_list, &proc_tree_mutex, true);
2318 } else {
2319 proc_handle_free(ph);
2320 proc_unlock();
2324 static attr_noinline void proc_check_owned(void)
2326 struct tree_entry *e;
2327 struct proc_handle *ph;
2328 pid_t pid = 0;
2329 pid_t r;
2330 int status;
2332 next:
2333 proc_lock();
2334 e = tree_find_next(&proc_tree, proc_handle_compare, pid);
2335 if (likely(!e)) {
2336 proc_unlock();
2337 return;
2339 ph = get_struct(e, struct proc_handle, entry);
2340 pid = ph->pid;
2341 proc_unlock();
2343 EINTR_LOOP(r, waitpid(pid, &status, WNOHANG));
2344 if (r <= 0)
2345 goto next;
2347 process_pid_and_status(pid, status);
2348 goto next;
2351 void os_proc_check_all(void)
2353 pid_t pid;
2354 int status;
2356 proc_lock();
2357 if (likely(tree_is_empty(&proc_tree))) {
2358 proc_unlock();
2359 return;
2361 proc_unlock();
2363 if (!dll) {
2364 test_another:
2365 EINTR_LOOP(pid, waitpid(-1, &status, WNOHANG));
2366 if (unlikely(pid > 0)) {
2367 process_pid_and_status(pid, status);
2368 goto test_another;
2370 } else {
2371 proc_check_owned();
2375 #endif
2378 #ifdef OS_HAS_SIGNALS
2380 #if defined(SIGRTMAX)
2381 #define N_SIGNALS (int)(SIGRTMAX + 1)
2382 #elif defined(NSIG)
2383 #define N_SIGNALS (int)NSIG
2384 #else
2385 #define N_SIGNALS 32
2386 #endif
2388 struct signal_state {
2389 thread_volatile signal_seq_t sig_sequence;
2390 signal_seq_t last_sig_sequence;
2391 bool trapped;
2392 uintptr_t refcount;
2393 struct list wait_list;
2394 struct sigaction prev_sa;
2397 static struct signal_state *signal_states;
2398 static mutex_t signal_state_mutex;
2400 static void signal_handler(int sig)
2402 signal_states[sig].sig_sequence += 1UL;
2403 os_notify();
2406 static int os_signal_number(const char *str)
2408 #ifdef SIGABRT
2409 if (!strcmp(str, "SIGABRT")) return SIGABRT;
2410 #endif
2411 #ifdef SIGALRM
2412 if (!strcmp(str, "SIGALRM")) return SIGALRM;
2413 #endif
2414 #ifdef SIGBUS
2415 if (!strcmp(str, "SIGBUS")) return SIGBUS;
2416 #endif
2417 #ifdef SIGCHLD
2418 if (!strcmp(str, "SIGCHLD")) return SIGCHLD;
2419 #endif
2420 #ifdef SIGCLD
2421 if (!strcmp(str, "SIGCLD")) return SIGCLD;
2422 #endif
2423 #ifdef SIGCONT
2424 if (!strcmp(str, "SIGCONT")) return SIGCONT;
2425 #endif
2426 #ifdef SIGEMT
2427 if (!strcmp(str, "SIGEMT")) return SIGEMT;
2428 #endif
2429 #ifdef SIGFPE
2430 if (!strcmp(str, "SIGFPE")) return SIGFPE;
2431 #endif
2432 #ifdef SIGHUP
2433 if (!strcmp(str, "SIGHUP")) return SIGHUP;
2434 #endif
2435 #ifdef SIGILL
2436 if (!strcmp(str, "SIGILL")) return SIGILL;
2437 #endif
2438 #ifdef SIGINFO
2439 if (!strcmp(str, "SIGINFO")) return SIGINFO;
2440 #endif
2441 #ifdef SIGINT
2442 if (!strcmp(str, "SIGINT")) return SIGINT;
2443 #endif
2444 #ifdef SIGIO
2445 if (!strcmp(str, "SIGIO")) return SIGIO;
2446 #endif
2447 #ifdef SIGIOT
2448 if (!strcmp(str, "SIGIOT")) return SIGIOT;
2449 #endif
2450 #ifdef SIGKILL
2451 if (!strcmp(str, "SIGKILL")) return SIGKILL;
2452 #endif
2453 #ifdef SIGLOST
2454 if (!strcmp(str, "SIGLOST")) return SIGLOST;
2455 #endif
2456 #ifdef SIGPIPE
2457 if (!strcmp(str, "SIGPIPE")) return SIGPIPE;
2458 #endif
2459 #ifdef SIGPOLL
2460 if (!strcmp(str, "SIGPOLL")) return SIGPOLL;
2461 #endif
2462 #ifdef SIGPROF
2463 if (!strcmp(str, "SIGPROF")) return SIGPROF;
2464 #endif
2465 #ifdef SIGPWR
2466 if (!strcmp(str, "SIGPWR")) return SIGPWR;
2467 #endif
2468 #ifdef SIGQUIT
2469 if (!strcmp(str, "SIGQUIT")) return SIGQUIT;
2470 #endif
2471 #ifdef SIGSEGV
2472 if (!strcmp(str, "SIGSEGV")) return SIGSEGV;
2473 #endif
2474 #ifdef SIGSTKFLT
2475 if (!strcmp(str, "SIGSTKFLT")) return SIGSTKFLT;
2476 #endif
2477 #ifdef SIGSTOP
2478 if (!strcmp(str, "SIGSTOP")) return SIGSTOP;
2479 #endif
2480 #ifdef SIGTSTP
2481 if (!strcmp(str, "SIGTSTP")) return SIGTSTP;
2482 #endif
2483 #ifdef SIGSYS
2484 if (!strcmp(str, "SIGSYS")) return SIGSYS;
2485 #endif
2486 #ifdef SIGTERM
2487 if (!strcmp(str, "SIGTERM")) return SIGTERM;
2488 #endif
2489 #ifdef SIGTRAP
2490 if (!strcmp(str, "SIGTRAP")) return SIGTRAP;
2491 #endif
2492 #ifdef SIGTTIN
2493 if (!strcmp(str, "SIGTTIN")) return SIGTTIN;
2494 #endif
2495 #ifdef SIGTTOU
2496 if (!strcmp(str, "SIGTTOU")) return SIGTTOU;
2497 #endif
2498 #ifdef SIGUNUSED
2499 if (!strcmp(str, "SIGUNUSED")) return SIGUNUSED;
2500 #endif
2501 #ifdef SIGURG
2502 if (!strcmp(str, "SIGURG")) return SIGURG;
2503 #endif
2504 #ifdef SIGUSR1
2505 if (!strcmp(str, "SIGUSR1")) return SIGUSR1;
2506 #endif
2507 #ifdef SIGUSR2
2508 if (!strcmp(str, "SIGUSR2")) return SIGUSR2;
2509 #endif
2510 #ifdef SIGVTALRM
2511 if (!strcmp(str, "SIGVTALRM")) return SIGVTALRM;
2512 #endif
2513 #ifdef SIGWINCH
2514 if (!strcmp(str, "SIGWINCH")) return SIGWINCH;
2515 #endif
2516 #ifdef SIGXCPU
2517 if (!strcmp(str, "SIGXCPU")) return SIGXCPU;
2518 #endif
2519 #ifdef SIGXFSZ
2520 if (!strcmp(str, "SIGXFSZ")) return SIGXFSZ;
2521 #endif
2522 #if defined(SIGRTMIN) && defined(SIGRTMAX)
2523 if (!strncmp(str, "SIGRT", 5) && str[5]) {
2524 char *endptr;
2525 unsigned long num = strtoul(str + 5, &endptr, 10);
2526 if (unlikely(*endptr))
2527 return 0;
2528 num += SIGRTMIN;
2529 if (unlikely(num < (unsigned long)SIGRTMIN) || unlikely(num > (unsigned long)SIGRTMAX))
2530 return 0;
2531 return num;
2533 #endif
2534 return 0;
2537 int os_signal_handle(const char *str, signal_seq_t *seq, ajla_error_t *err)
2539 struct signal_state *s;
2540 int sig = os_signal_number(str);
2541 if (unlikely(!sig)) {
2542 *seq = 0;
2543 return 0;
2545 mutex_lock(&signal_state_mutex);
2546 s = &signal_states[sig];
2547 if (unlikely(s->trapped)) {
2548 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "signal %s already handled", str);
2549 goto unlock_err;
2551 if (likely(!s->refcount)) {
2552 struct sigaction sa;
2553 int r;
2555 s->sig_sequence = 0;
2556 s->last_sig_sequence = 0;
2558 (void)memset(&sa, 0, sizeof sa);
2559 sa.sa_handler = signal_handler;
2560 sigemptyset(&sa.sa_mask);
2561 #ifdef SA_RESTART
2562 if (sig != SIGTTIN && sig != SIGTTOU)
2563 sa.sa_flags |= SA_RESTART;
2564 #endif
2565 EINTR_LOOP(r, sigaction(sig, &sa, &s->prev_sa));
2566 if (unlikely(r == -1)) {
2567 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
2568 fatal_mayfail(e, err, "sigaction(%d) failed: %s", sig, error_decode(e));
2569 goto unlock_err;
2572 s->refcount++;
2573 *seq = s->last_sig_sequence;
2574 mutex_unlock(&signal_state_mutex);
2575 return sig;
2577 unlock_err:
2578 mutex_unlock(&signal_state_mutex);
2579 return -1;
2582 static void os_signal_restore(struct signal_state *s, int sig)
2584 int r;
2585 EINTR_LOOP(r, sigaction(sig, &s->prev_sa, NULL));
2586 if (unlikely(r == -1)) {
2587 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
2588 fatal("sigaction(%d) failed: %s", sig, error_decode(e));
2592 void os_signal_unhandle(int sig)
2594 struct signal_state *s;
2595 if (unlikely(!sig))
2596 return;
2597 mutex_lock(&signal_state_mutex);
2598 s = &signal_states[sig];
2599 if (!s->refcount)
2600 internal(file_line, "os_signal_unhandle: refcount underflow");
2601 s->refcount--;
2602 if (!s->refcount)
2603 os_signal_restore(s, sig);
2604 mutex_unlock(&signal_state_mutex);
2607 signal_seq_t os_signal_seq(int sig)
2609 struct signal_state *s;
2610 signal_seq_t seq;
2611 if (unlikely(!sig))
2612 return 0;
2613 mutex_lock(&signal_state_mutex);
2614 s = &signal_states[sig];
2615 if (unlikely(!s))
2616 internal(file_line, "os_signal_unhandle: unhandled signal");
2617 seq = s->last_sig_sequence;
2618 mutex_unlock(&signal_state_mutex);
2619 return seq;
2622 bool os_signal_wait(int sig, signal_seq_t seq, mutex_t **mutex_to_lock, struct list *list_entry)
2624 struct signal_state *s;
2626 if (unlikely(!sig)) {
2627 iomux_never(mutex_to_lock, list_entry);
2628 return true;
2631 mutex_lock(&signal_state_mutex);
2632 s = &signal_states[sig];
2633 if (unlikely(seq != s->last_sig_sequence)) {
2634 mutex_unlock(&signal_state_mutex);
2635 return false;
2637 *mutex_to_lock = &signal_state_mutex;
2638 list_add(&s->wait_list, list_entry);
2639 mutex_unlock(&signal_state_mutex);
2641 return true;
2644 void os_signal_check_all(void)
2646 int sig = 0;
2647 again:
2648 mutex_lock(&signal_state_mutex);
2649 for (; sig < N_SIGNALS; sig++) {
2650 struct signal_state *s = &signal_states[sig];
2651 signal_seq_t seq = s->sig_sequence;
2652 if (unlikely(seq != s->last_sig_sequence)) {
2653 s->last_sig_sequence = seq;
2654 call(wake_up_wait_list)(&s->wait_list, &signal_state_mutex, true);
2655 sig++;
2656 goto again;
2659 mutex_unlock(&signal_state_mutex);
2662 #ifdef HAVE_CODEGEN_TRAPS
2664 void *u_data_trap_lookup(void *ptr);
2665 void *c_data_trap_lookup(void *ptr);
2667 static void sigfpe_handler(int attr_unused sig, siginfo_t *siginfo, void *ucontext)
2669 ucontext_t *uc = ucontext;
2670 #if defined(ARCH_ALPHA)
2671 if (unlikely(siginfo->si_code != FPE_FLTINV))
2672 fatal("unexpected SIGFPE received: %d", siginfo->si_code);
2673 uc->uc_mcontext.sc_pc = ptr_to_num(call(data_trap_lookup)(num_to_ptr(uc->uc_mcontext.sc_pc)));
2674 #endif
2675 #if defined(ARCH_MIPS)
2676 if (unlikely(siginfo->si_code != FPE_INTOVF))
2677 fatal("unexpected SIGFPE received: %d", siginfo->si_code);
2678 uc->uc_mcontext.pc = ptr_to_num(call(data_trap_lookup)(num_to_ptr(uc->uc_mcontext.pc)));
2679 #endif
2682 #endif
2684 #ifdef SA_SIGINFO
2685 void os_signal_trap(int sig, void (*handler)(int, siginfo_t *, void *))
2687 if (OS_SUPPORTS_TRAPS) {
2688 struct signal_state *s = &signal_states[sig];
2689 struct sigaction sa;
2690 int r;
2692 s->trapped = true;
2694 (void)memset(&sa, 0, sizeof sa);
2695 sa.sa_sigaction = handler;
2696 sigemptyset(&sa.sa_mask);
2697 sa.sa_flags |= SA_SIGINFO;
2698 EINTR_LOOP(r, sigaction(sig, &sa, &s->prev_sa));
2699 if (unlikely(r == -1)) {
2700 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
2701 fatal("sigaction(%d) failed: %s", sig, error_decode(e));
2705 void os_signal_untrap(int sig)
2707 if (OS_SUPPORTS_TRAPS) {
2708 struct signal_state *s = &signal_states[sig];
2709 ajla_assert_lo(s->trapped, (file_line, "os_signal_untrap: signal %d not trapped", sig));
2710 os_signal_restore(s, sig);
2711 s->trapped = false;
2714 #endif
2716 #else
2718 int os_signal_handle(const char attr_unused *str, signal_seq_t attr_unused *seq, ajla_error_t attr_unused *err)
2720 *seq = NULL;
2721 return 0;
2724 void os_signal_unhandle(int attr_unused sig)
2728 signal_seq_t os_signal_seq(int attr_unused sig)
2730 return 0;
2733 bool os_signal_wait(int attr_unused sig, signal_seq_t attr_unused seq, mutex_t **mutex_to_lock, struct list *list_entry)
2735 iomux_never(mutex_to_lock, list_entry);
2736 return true;
2739 void os_signal_check_all(void)
2743 #endif
2746 #ifdef HAVE_NETWORK
2748 handle_t os_socket(int domain, int type, int protocol, ajla_error_t *err)
2750 int r, h;
2751 domain = os_socket_pf(domain, err);
2752 if (unlikely(domain == -1))
2753 return -1;
2754 type = os_socket_type(type, err);
2755 if (unlikely(type == -1))
2756 return -1;
2757 #if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC)
2758 EINTR_LOOP(h, socket(domain, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol));
2759 if (likely(h != -1)) {
2760 obj_registry_insert(OBJ_TYPE_HANDLE, h, file_line);
2761 return h;
2763 if (errno != EINVAL) {
2764 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
2765 fatal_mayfail(e, err, "can't create socket (%d, %d, %d): %s", domain, type, protocol, error_decode(e));
2766 return -1;
2768 #endif
2769 os_lock_fork(false);
2770 EINTR_LOOP(h, socket(domain, type, protocol));
2771 if (unlikely(h == -1)) {
2772 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
2773 os_unlock_fork(false);
2774 fatal_mayfail(e, err, "can't create socket (%d, %d, %d): %s", domain, type, protocol, error_decode(e));
2775 return -1;
2777 os_set_cloexec(h);
2778 os_unlock_fork(false);
2779 obj_registry_insert(OBJ_TYPE_HANDLE, h, file_line);
2780 EINTR_LOOP(r, fcntl(h, F_SETFL, O_NONBLOCK));
2781 if (unlikely(r == -1)) {
2782 int er = errno;
2783 fatal("fcntl(F_SETFL, O_NONBLOCK) on a socket failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
2785 return h;
2788 bool os_bind_connect(bool bnd, handle_t h, unsigned char *addr, size_t addr_len, ajla_error_t *err)
2790 int r;
2791 struct sockaddr *sa;
2792 ajla_error_t e;
2793 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
2794 sa = os_get_sock_addr(addr, &addr_len, err);
2795 if (unlikely(!sa))
2796 return false;
2797 if (likely(!bnd))
2798 EINTR_LOOP(r, connect(h, sa, addr_len));
2799 else
2800 EINTR_LOOP(r, bind(h, sa, addr_len));
2801 mem_free_aligned(sa);
2802 if (unlikely(!r))
2803 return true;
2804 if (likely(!bnd) && likely(errno == EINPROGRESS))
2805 return true;
2806 e = error_from_errno(EC_SYSCALL, errno);
2807 fatal_mayfail(e, err, "can't %s socket: %s", !bnd ? "connect" : "bind", error_decode(e));
2808 return false;
2811 bool os_connect_completed(handle_t h, ajla_error_t *err)
2813 ajla_error_t e;
2814 int r;
2815 int er;
2816 socklen_t er_l;
2817 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
2818 er_l = sizeof er;
2819 EINTR_LOOP(r, getsockopt(h, SOL_SOCKET, SO_ERROR, &er, &er_l));
2820 if (unlikely(r == -1)) {
2821 e = error_from_errno(EC_SYSCALL, errno);
2822 fatal_mayfail(e, err, "getsockopt returned an error: %s", error_decode(e));
2823 return false;
2825 if (unlikely(er)) {
2826 e = error_from_errno(EC_SYSCALL, er);
2827 fatal_mayfail(e, err, "can't connect socket: %s", error_decode(e));
2828 return false;
2830 return true;
2833 bool os_listen(handle_t h, ajla_error_t *err)
2835 int r;
2836 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
2837 EINTR_LOOP(r, listen(h, signed_maximum(int)));
2838 if (unlikely(r == -1)) {
2839 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
2840 fatal_mayfail(e, err, "listen returned an error: %s", error_decode(e));
2841 return false;
2843 return true;
2846 int os_accept(handle_t h, handle_t *result, ajla_error_t *err)
2848 int r;
2849 ajla_error_t e;
2850 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
2851 #ifdef HAVE_ACCEPT4
2852 EINTR_LOOP(r, accept4(h, NULL, 0, SOCK_NONBLOCK | SOCK_CLOEXEC));
2853 if (likely(r != -1)) {
2854 *result = r;
2855 obj_registry_insert(OBJ_TYPE_HANDLE, r, file_line);
2856 return 0;
2858 if (errno == EAGAIN || errno == EWOULDBLOCK)
2859 return OS_RW_WOULDBLOCK;
2860 if (errno != ENOSYS) {
2861 e = error_from_errno(EC_SYSCALL, errno);
2862 fatal_mayfail(e, err, "accept returned an error: %s", error_decode(e));
2863 return OS_RW_ERROR;
2865 #endif
2866 os_lock_fork(false);
2867 EINTR_LOOP(r, accept(h, NULL, 0));
2868 if (unlikely(r == -1)) {
2869 os_unlock_fork(false);
2870 if (errno == EAGAIN || errno == EWOULDBLOCK)
2871 return OS_RW_WOULDBLOCK;
2872 e = error_from_errno(EC_SYSCALL, errno);
2873 fatal_mayfail(e, err, "accept returned an error: %s", error_decode(e));
2874 return OS_RW_ERROR;
2876 os_set_cloexec(r);
2877 os_unlock_fork(false);
2878 *result = r;
2879 obj_registry_insert(OBJ_TYPE_HANDLE, r, file_line);
2880 EINTR_LOOP(r, fcntl(r, F_SETFL, O_NONBLOCK));
2881 if (unlikely(r == -1)) {
2882 int er = errno;
2883 fatal("fcntl(F_SETFL, O_NONBLOCK) on a socket failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
2885 return 0;
2888 bool os_getsockpeername(bool peer, handle_t h, unsigned char **addr, size_t *addr_len, ajla_error_t *err)
2890 int r;
2891 struct sockaddr *sa;
2892 socklen_t addrlen;
2894 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
2896 sa = mem_align_mayfail(struct sockaddr *, SOCKADDR_MAX_LEN, SOCKADDR_ALIGN, err);
2897 if (unlikely(!sa))
2898 return false;
2899 addrlen = SOCKADDR_MAX_LEN;
2901 if (!peer) {
2902 EINTR_LOOP(r, getsockname(h, sa, &addrlen));
2903 } else {
2904 #ifdef HAVE_GETPEERNAME
2905 EINTR_LOOP(r, getpeername(h, sa, &addrlen));
2906 #else
2907 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "getpeername not supported");
2908 goto free_ret_false;
2909 #endif
2911 if (unlikely(r == -1)) {
2912 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
2913 fatal_mayfail(e, err, "%s returned an error: %s", !peer ? "getsockname" : "getpeername", error_decode(e));
2914 goto free_ret_false;
2917 *addr = os_get_ajla_addr(sa, &addrlen, err);
2918 if (unlikely(!*addr))
2919 goto free_ret_false;
2921 *addr_len = addrlen;
2923 mem_free_aligned(sa);
2924 return true;
2926 free_ret_false:
2927 mem_free_aligned(sa);
2928 return false;
2931 ssize_t os_recvfrom(handle_t h, char *buffer, size_t len, int flags, unsigned char **addr, size_t *addr_len, ajla_error_t *err)
2933 struct sockaddr *sa;
2934 socklen_t addrlen;
2935 ssize_t r;
2936 int f;
2938 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
2940 f = translate_flags(os_socket_msg, flags, err);
2941 if (unlikely(f < 0))
2942 return OS_RW_ERROR;
2944 sa = mem_align_mayfail(struct sockaddr *, SOCKADDR_MAX_LEN, SOCKADDR_ALIGN, err);
2945 if (unlikely(!sa))
2946 return OS_RW_ERROR;
2947 addrlen = SOCKADDR_MAX_LEN;
2949 EINTR_LOOP(r, recvfrom(h, buffer, len, f, sa, &addrlen));
2951 if (r >= 0) {
2952 if (unlikely(addrlen > SOCKADDR_MAX_LEN)) {
2953 fatal_mayfail(error_ajla(EC_SYSCALL, AJLA_ERROR_SIZE_OVERFLOW), err, "the system returned too long address");
2954 mem_free_aligned(sa);
2955 return OS_RW_ERROR;
2957 if (!addrlen) {
2958 if (unlikely(!array_init_mayfail(unsigned char, addr, addr_len, err))) {
2959 mem_free_aligned(sa);
2960 return OS_RW_ERROR;
2962 } else {
2963 *addr = os_get_ajla_addr(sa, &addrlen, err);
2964 if (unlikely(!*addr)) {
2965 mem_free_aligned(sa);
2966 return OS_RW_ERROR;
2968 *addr_len = addrlen;
2971 mem_free_aligned(sa);
2972 return os_rdwr_return(r, "receiving", err);
2975 ssize_t os_sendto(handle_t h, const char *buffer, size_t len, int flags, unsigned char *addr, size_t addr_len, ajla_error_t *err)
2977 struct sockaddr *sa;
2978 ssize_t r;
2979 int f;
2981 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
2983 f = translate_flags(os_socket_msg, flags, err);
2984 if (unlikely(f < 0))
2985 return OS_RW_ERROR;
2987 if (addr_len != 0) {
2988 sa = os_get_sock_addr(addr, &addr_len, err);
2989 if (unlikely(!sa))
2990 return OS_RW_ERROR;
2991 EINTR_LOOP(r, sendto(h, buffer, len, f, sa, addr_len));
2992 mem_free_aligned(sa);
2993 } else {
2994 EINTR_LOOP(r, send(h, buffer, len, f));
2997 return os_rdwr_return(r, "sending", err);
3000 bool os_getsockopt(handle_t h, int level, int option, char **buffer, size_t *buffer_len, ajla_error_t *err)
3002 int r;
3003 socklen_t opt_len;
3005 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
3007 level = os_socket_level(level, err);
3008 if (unlikely(level < 0))
3009 return false;
3011 option = os_socket_option(option, err);
3012 if (unlikely(level < 0))
3013 return false;
3015 opt_len = 4096;
3016 *buffer = mem_alloc_mayfail(char *, opt_len, err);
3017 if (unlikely(!*buffer))
3018 return false;
3020 EINTR_LOOP(r, getsockopt(h, level, option, *buffer, &opt_len));
3022 if (unlikely(r == -1)) {
3023 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
3024 fatal_mayfail(e, err, "getsockopt returned an error: %s", error_decode(e));
3025 mem_free(*buffer);
3026 return false;
3029 *buffer_len = opt_len;
3030 return true;
3033 bool os_setsockopt(handle_t h, int level, int option, const char *buffer, size_t buffer_len, ajla_error_t *err)
3035 int r;
3037 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
3039 level = os_socket_level(level, err);
3040 if (unlikely(level < 0))
3041 return false;
3043 option = os_socket_option(option, err);
3044 if (unlikely(level < 0))
3045 return false;
3047 EINTR_LOOP(r, setsockopt(h, level, option, buffer, buffer_len));
3049 if (unlikely(r == -1)) {
3050 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
3051 fatal_mayfail(e, err, "setsockopt returned an error: %s", error_decode(e));
3052 return false;
3055 return true;
3058 #ifdef HAVE_GETADDRINFO
3059 bool os_getaddrinfo(const char *host, int port, struct address **result, size_t *result_l, ajla_error_t *err)
3061 char port_str[6];
3062 int r;
3063 size_t i;
3064 struct addrinfo *res = NULL, *rs;
3066 if (unlikely(!array_init_mayfail(struct address, result, result_l, err)))
3067 return false;
3069 snprintf(port_str, sizeof port_str, "%d", port);
3070 r = getaddrinfo(host, port_str, NULL, &res);
3071 if (unlikely(r)) {
3072 if (unlikely(r == EAI_SYSTEM))
3073 fatal_mayfail(error_from_errno(EC_SYSCALL, errno), err, "host not found");
3074 else
3075 fatal_mayfail(error_ajla_aux(EC_SYSCALL, AJLA_ERROR_GAI, abs((int)r)), err, "host not found");
3076 goto fail;
3079 for (rs = res; rs; rs = rs->ai_next) {
3080 void *xresult;
3081 struct address addr;
3082 ajla_error_t e;
3083 socklen_t addrlen = rs->ai_addrlen;
3085 memset(&addr.entry, 0, sizeof addr.entry); /* avoid warning */
3087 addr.address = os_get_ajla_addr(rs->ai_addr, &addrlen, &e);
3088 if (unlikely(!addr.address))
3089 continue;
3090 addr.address_length = addrlen;
3092 if (unlikely(!array_add_mayfail(struct address, result, result_l, addr, &xresult, err))) {
3093 *result = xresult;
3094 goto fail;
3098 if (unlikely(!*result_l)) {
3099 fatal_mayfail(error_ajla_aux(EC_SYSCALL, AJLA_ERROR_GAI, abs(EAI_NONAME)), err, "host not found");
3100 goto fail;
3103 freeaddrinfo(res);
3104 return true;
3106 fail:
3107 if (res)
3108 freeaddrinfo(res);
3109 for (i = 0; i < *result_l; i++)
3110 mem_free((*result)[i].address);
3111 mem_free(*result);
3112 return false;
3114 #else
3115 #ifndef NO_DATA
3116 #define NO_DATA 1
3117 #endif
3118 #ifndef HAVE_H_ERRNO
3119 #define h_errno 1
3120 #endif
3121 bool os_getaddrinfo(const char *host, int port, struct address **result, size_t *result_l, ajla_error_t *err)
3123 struct hostent *he;
3124 size_t i;
3125 void *xresult;
3126 char *a;
3128 if (unlikely(!array_init_mayfail(struct address, result, result_l, err)))
3129 return false;
3131 he = gethostbyname(host);
3133 if (unlikely(!he)) {
3134 fatal_mayfail(error_ajla_aux(EC_SYSCALL, AJLA_ERROR_H_ERRNO, h_errno), err, "host not found");
3135 goto fail;
3138 if (he->h_addrtype != AF_INET || he->h_length != 4 || !he->h_addr) {
3139 fatal_mayfail(error_ajla_aux(EC_SYSCALL, AJLA_ERROR_H_ERRNO, NO_DATA), err, "host not found");
3140 goto fail;
3143 #ifdef h_addr
3144 for (i = 0; (a = he->h_addr_list[i]); i++)
3145 #else
3146 a = he->h_addr;
3147 #endif
3149 struct sockaddr_in sin;
3150 struct sockaddr sa;
3151 struct address addr;
3152 ajla_error_t e;
3153 socklen_t addrlen = sizeof sin;
3155 sin.sin_family = AF_INET;
3156 sin.sin_port = htons(port);
3157 memcpy(&sin.sin_addr, a, 4);
3159 memcpy(&sa, &sin, sizeof sin);
3161 addr.address = os_get_ajla_addr(&sa, &addrlen, &e);
3162 if (unlikely(!addr.address))
3163 continue;
3164 addr.address_length = addrlen;
3166 if (unlikely(!array_add_mayfail(struct address, result, result_l, addr, &xresult, err))) {
3167 *result = xresult;
3168 goto fail;
3172 if (unlikely(!*result_l)) {
3173 fatal_mayfail(error_ajla_aux(EC_SYSCALL, AJLA_ERROR_H_ERRNO, NO_DATA), err, "host not found");
3174 goto fail;
3177 return true;
3179 fail:
3180 for (i = 0; i < *result_l; i++)
3181 mem_free((*result)[i].address);
3182 mem_free(*result);
3183 return false;
3185 #endif
3187 #ifdef HAVE_GETNAMEINFO
3188 char *os_getnameinfo(unsigned char *addr, size_t addr_len, ajla_error_t *err)
3190 struct sockaddr *sa;
3191 int r;
3192 char *h;
3193 size_t h_len;
3194 sa = os_get_sock_addr(addr, &addr_len, err);
3195 if (unlikely(!sa))
3196 return NULL;
3197 #ifdef EAI_OVERFLOW
3198 h_len = 64;
3199 alloc_buffer_again:
3200 #else
3201 h_len = NI_MAXHOST;
3202 #endif
3203 h = mem_alloc_mayfail(char *, h_len, err);
3204 if (unlikely(!h)) {
3205 mem_free_aligned(sa);
3206 return NULL;
3208 r = getnameinfo(sa, addr_len, h, h_len, NULL, 0, 0);
3209 if (unlikely(r)) {
3210 #ifdef EAI_OVERFLOW
3211 if (unlikely(r == EAI_OVERFLOW)) {
3212 mem_free(h);
3213 h_len *= 2;
3214 if (unlikely(!h_len)) {
3215 fatal_mayfail(error_ajla(EC_SYSCALL, AJLA_ERROR_SIZE_OVERFLOW), err, "name buffer overflow");
3216 mem_free_aligned(sa);
3217 return NULL;
3219 goto alloc_buffer_again;
3221 #endif
3222 if (unlikely(r == EAI_SYSTEM)) {
3223 fatal_mayfail(error_from_errno(EC_SYSCALL, errno), err, "host not found");
3224 } else {
3225 fatal_mayfail(error_ajla_aux(EC_SYSCALL, AJLA_ERROR_GAI, abs((int)r)), err, "host not found");
3227 mem_free(h);
3228 mem_free_aligned(sa);
3229 return NULL;
3231 mem_free_aligned(sa);
3232 return h;
3234 #elif defined(HAVE_GETHOSTBYADDR)
3235 char *os_getnameinfo(unsigned char *addr, size_t addr_len, ajla_error_t *err)
3237 struct sockaddr *sa;
3238 struct hostent *he;
3239 char *name;
3240 size_t le;
3241 sa = os_get_sock_addr(addr, &addr_len, err);
3242 if (unlikely(!sa))
3243 return NULL;
3244 switch (sa->sa_family) {
3245 case AF_INET: {
3246 struct sockaddr_in *sin;
3247 if (unlikely(addr_len < offsetof(struct sockaddr_in, sin_addr) + 4)) {
3248 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "too short address");
3249 mem_free_aligned(sa);
3250 return NULL;
3252 sin = cast_ptr(struct sockaddr_in *, sa);
3253 he = gethostbyaddr(cast_ptr(void *, &sin->sin_addr.s_addr), 4, sa->sa_family);
3254 break;
3256 #ifdef AF_INET6
3257 case AF_INET6: {
3258 struct sockaddr_in6 *sin6;
3259 if (unlikely(addr_len < offsetof(struct sockaddr_in6, sin6_addr) + 16)) {
3260 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "too short address");
3261 mem_free_aligned(sa);
3262 return NULL;
3264 sin6 = cast_ptr(struct sockaddr_in6 *, sa);
3265 he = gethostbyaddr(&sin6->sin6_addr.s6_addr, 16, sa->sa_family);
3266 break;
3268 #endif
3269 default: {
3270 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "address family %d not supported", sa->sa_family);
3271 mem_free_aligned(sa);
3272 return NULL;
3275 mem_free_aligned(sa);
3276 if (unlikely(!he) || !he->h_name) {
3277 fatal_mayfail(error_ajla_aux(EC_SYSCALL, AJLA_ERROR_H_ERRNO, h_errno), err, "host not found");
3278 return NULL;
3280 le = strlen(he->h_name);
3281 name = mem_alloc_mayfail(char *, le + 1, err);
3282 if (unlikely(!name))
3283 return NULL;
3284 return memcpy(name, he->h_name, le + 1);
3286 #else
3287 char *os_getnameinfo(unsigned char attr_unused *addr, size_t attr_unused addr_len, ajla_error_t *err)
3289 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "getnameinfo not supported");
3290 return NULL;
3292 #endif
3294 #else
3296 handle_t os_socket(int attr_unused domain, int attr_unused type, int attr_unused protocol, ajla_error_t *err)
3298 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "sockets not supported");
3299 return -1;
3302 bool os_bind_connect(bool attr_unused bnd, handle_t attr_unused h, unsigned char attr_unused *addr, size_t attr_unused addr_len, ajla_error_t *err)
3304 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "sockets not supported");
3305 return false;
3308 bool os_connect_completed(handle_t attr_unused h, ajla_error_t *err)
3310 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "sockets not supported");
3311 return false;
3314 bool os_listen(handle_t attr_unused h, ajla_error_t *err)
3316 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "sockets not supported");
3317 return false;
3320 int os_accept(handle_t attr_unused h, handle_t attr_unused *result, ajla_error_t *err)
3322 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "sockets not supported");
3323 return OS_RW_ERROR;
3326 bool os_getsockpeername(bool attr_unused peer, handle_t attr_unused h, unsigned char attr_unused **addr, size_t attr_unused *addr_len, ajla_error_t *err)
3328 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "sockets not supported");
3329 return false;
3332 ssize_t os_recvfrom(handle_t attr_unused h, char attr_unused *buffer, size_t attr_unused len, int attr_unused flags, unsigned char attr_unused **addr, size_t attr_unused *addr_len, ajla_error_t *err)
3334 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "sockets not supported");
3335 return OS_RW_ERROR;
3338 ssize_t os_sendto(handle_t attr_unused h, const char attr_unused *buffer, size_t attr_unused len, int attr_unused flags, unsigned char attr_unused *addr, size_t attr_unused addr_len, ajla_error_t *err)
3340 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "sockets not supported");
3341 return false;
3344 bool os_getsockopt(handle_t attr_unused h, int attr_unused level, int attr_unused option, char attr_unused **buffer, size_t attr_unused *buffer_len, ajla_error_t *err)
3346 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "sockets not supported");
3347 return false;
3350 bool os_setsockopt(handle_t attr_unused h, int attr_unused level, int attr_unused option, const char attr_unused *buffer, size_t attr_unused buffer_len, ajla_error_t *err)
3352 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "sockets not supported");
3353 return false;
3356 bool os_getaddrinfo(const char attr_unused *host, int attr_unused port, struct address attr_unused **result, size_t attr_unused *result_l, ajla_error_t *err)
3358 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "sockets not supported");
3359 return false;
3362 char *os_getnameinfo(unsigned char attr_unused *addr, size_t attr_unused addr_len, ajla_error_t *err)
3364 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "sockets not supported");
3365 return NULL;
3368 #endif
3371 const char *os_decode_error(ajla_error_t error, char attr_unused *(*tls_buffer)(void))
3373 switch (error.error_type) {
3374 #if defined(HAVE_NETWORK) && defined(HAVE_GETADDRINFO)
3375 case AJLA_ERROR_GAI: {
3376 return gai_strerror(error.error_aux * (EAI_NONAME < 0 ? -1 : 1));
3378 #else
3379 case AJLA_ERROR_H_ERRNO: {
3380 #if defined(HAVE_NETWORK) && defined(HAVE_HSTRERROR)
3381 return hstrerror(error.error_aux);
3382 #else
3383 return "Unknown host";
3384 #endif
3386 #endif
3388 return NULL;
3392 #ifdef OS_HAS_DLOPEN
3393 struct dl_handle_t *os_dlopen(const char *filename, ajla_error_t *err, char **err_msg)
3395 struct dl_handle_t *dlh;
3396 dlh = dlopen(filename, RTLD_LAZY);
3397 if (unlikely(!dlh)) {
3398 ajla_error_t e;
3399 *err_msg = dlerror();
3400 e = error_ajla(EC_SYNC, AJLA_ERROR_LIBRARY_NOT_FOUND);
3401 fatal_mayfail(e, err, "can't open dynamic library '%s': %s", filename, *err_msg);
3402 return NULL;
3404 return dlh;
3407 void os_dlclose(struct dl_handle_t *dlh)
3409 int r = dlclose(dlh);
3410 #if defined(OS_CYGWIN)
3411 /* dlclose fails if we attempt to unload non-cygwin dll */
3412 if (unlikely(r == -1) && errno == ENOENT)
3413 return;
3414 #endif
3415 if (unlikely(r))
3416 internal(file_line, "dlclose failed: %s", dlerror());
3419 bool os_dlsym(struct dl_handle_t *dlh, const char *symbol, void **result)
3421 void *r;
3422 r = dlsym(dlh, symbol);
3423 if (unlikely(!r))
3424 return false;
3425 *result = r;
3426 return true;
3428 #endif
3431 #ifdef OS_HAVE_NOTIFY_PIPE
3432 handle_t os_notify_pipe[2];
3434 void os_notify(void)
3436 int r;
3437 char c = 0;
3438 EINTR_LOOP(r, write(os_notify_pipe[1], &c, 1));
3439 if (unlikely(r == -1)) {
3440 int er = errno;
3441 if (unlikely(er != EAGAIN) && unlikely(er != EWOULDBLOCK) && unlikely(er != EBADF)) {
3442 fatal("error writing to the notify pipe: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
3447 bool os_drain_notify_pipe(void)
3449 static char buffer[1024];
3450 int r;
3451 EINTR_LOOP(r, read(os_notify_pipe[0], buffer, sizeof(buffer)));
3452 if (likely(r == -1)) {
3453 int er = errno;
3454 if (unlikely(er != EAGAIN) && unlikely(er != EWOULDBLOCK)) {
3455 fatal("error reading the notify pipe: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
3458 return !r;
3461 void os_shutdown_notify_pipe(void)
3463 int r;
3464 EINTR_LOOP(r, dup2(os_notify_pipe[0], os_notify_pipe[1]));
3465 if (unlikely(r == -1)) {
3466 int er = errno;
3467 fatal("error shutting down the notify pipe: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
3469 #ifdef DEBUG
3470 os_notify();
3471 #endif
3473 #endif
3476 #if defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME)
3478 const char *os_get_flavor(void)
3480 #if defined(OS_DOS)
3481 return "DOS";
3482 #elif defined(OS_CYGWIN)
3483 return "Cygwin";
3484 #else
3485 return "Unix";
3486 #endif
3489 void os_get_uname(os_utsname_t *un)
3491 int r;
3492 EINTR_LOOP(r, uname(un));
3493 if (unlikely(r == -1)) {
3494 int er = errno;
3495 fatal("uname returned error: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
3497 if (sizeof un->sysname >= 10 && likely(!strcmp(un->sysname, "Linux")))
3498 strcpy(un->sysname, "GNU/Linux"); /* make RMS happy */
3501 bool os_kernel_version(const char *sys, const char *vers)
3503 static os_utsname_t un;
3504 static bool have_un = false;
3505 const char *last_comp, *ptr_sys, *ptr_wanted;
3506 if (!have_un) {
3507 os_get_uname(&un);
3508 have_un = true;
3510 if (unlikely(strcmp(sys, un.sysname)))
3511 return false;
3512 last_comp = strrchr(vers, '.');
3513 if (last_comp)
3514 last_comp++;
3515 else
3516 last_comp = vers;
3517 if (strncmp(un.release, vers, last_comp - vers))
3518 return false;
3519 ptr_sys = un.release + (last_comp - vers);
3520 ptr_wanted = vers + (last_comp - vers);
3521 if (!*ptr_wanted)
3522 return true;
3523 if (likely(*ptr_sys >= '0') && likely(*ptr_sys <= '9')) {
3524 if (atoi(ptr_sys) >= atoi(ptr_wanted)) {
3525 return true;
3528 return false;
3531 #else
3533 void os_get_uname(os_utsname_t *un)
3535 memset(un, 0, sizeof(os_utsname_t));
3536 strcpy(un->sysname, "Posix");
3537 #ifdef ARCH_NAME
3538 strcpy(un->machine, ARCH_NAME);
3539 #endif
3542 bool os_kernel_version(const char attr_unused *sys, const char attr_unused *vers)
3544 return false;
3547 #endif
3549 char *os_get_host_name(ajla_error_t *err)
3551 #ifdef HAVE_GETHOSTNAME
3552 size_t s = 128;
3553 char *hn;
3554 int r;
3556 try_more:
3557 s *= 2;
3558 if (unlikely(!s)) {
3559 fatal_mayfail(error_ajla(EC_ASYNC, AJLA_ERROR_SIZE_OVERFLOW), err, "overflow when allocating host name");
3560 return NULL;
3562 hn = mem_alloc_mayfail(char *, s, err);
3563 if (unlikely(!hn))
3564 return NULL;
3566 EINTR_LOOP(r, gethostname(hn, s));
3568 if (unlikely(r == -1)) {
3569 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
3570 mem_free(hn);
3571 if (errno == EINVAL || errno == ENAMETOOLONG)
3572 goto try_more;
3573 fatal_mayfail(e, err, "can't get hostname: %s", error_decode(e));
3574 return NULL;
3577 if (unlikely(strnlen(hn, s) >= s - 1)) {
3578 mem_free(hn);
3579 goto try_more;
3582 return hn;
3583 #else
3584 char *e = getenv("HOSTNAME");
3585 if (!e)
3586 e = "";
3587 return str_dup(e, -1, err);
3588 #endif
3591 void os_init(void)
3593 ajla_error_t sink;
3594 int r, i;
3596 #if defined(OS_DOS)
3597 EINTR_LOOP(r, close(3));
3598 EINTR_LOOP(r, close(4));
3599 #endif
3601 n_std_handles = 0;
3602 while (1) {
3603 struct stat st;
3604 EINTR_LOOP(r, fstat(n_std_handles, &st));
3605 if (r)
3606 break;
3607 n_std_handles++;
3609 if (unlikely(n_std_handles < 3))
3610 exit(127);
3612 #ifdef HAVE_AT_FUNCTIONS
3613 if (os_kernel_version("GNU/Linux", "3") ||
3614 os_kernel_version("GNU/Linux", "2.6.23")) {
3615 have_O_CLOEXEC_openat = true;
3616 } else {
3617 int h, r;
3618 int flags;
3619 os_stat_t st;
3620 EINTR_LOOP(r, fstatat(AT_FDCWD, "/", &st, AT_SYMLINK_NOFOLLOW));
3621 if (unlikely(r == -1))
3622 goto skip_test;
3624 EINTR_LOOP(h, openat(AT_FDCWD, "/dev/null", O_RDONLY | O_CLOEXEC));
3625 if (unlikely(h == -1))
3626 goto skip_test;
3628 EINTR_LOOP(flags, fcntl(h, F_GETFD));
3629 if (likely(flags >= 0) && likely(flags & FD_CLOEXEC))
3630 have_O_CLOEXEC_openat = true;
3631 os_close_handle(h);
3632 skip_test:;
3634 #endif
3636 os_cwd = os_get_cwd(&sink);
3637 if (unlikely(dir_handle_is_valid(os_cwd))) {
3638 os_set_original_cwd();
3639 } else {
3640 os_cwd = os_get_cwd(NULL);
3643 os_init_path_to_exe();
3645 #ifdef OS_HAVE_NOTIFY_PIPE
3646 os_pipe(os_notify_pipe, 3, NULL);
3647 #endif
3649 #ifdef OS_HAS_SIGNALS
3650 signal_states = mem_alloc_array_mayfail(mem_calloc_mayfail, struct signal_state *, 0, 0, N_SIGNALS, sizeof(struct signal_state), NULL);
3651 for (i = 0; i < N_SIGNALS; i++)
3652 list_init(&signal_states[i].wait_list);
3653 if (!dll) {
3654 #ifdef HAVE_CODEGEN_TRAPS
3655 os_signal_trap(SIGFPE, sigfpe_handler);
3656 #if defined(ARCH_MIPS)
3657 os_signal_trap(SIGTRAP, sigfpe_handler);
3658 #endif
3659 #endif
3661 #endif
3664 void os_done(void)
3666 #ifdef OS_HAS_SIGNALS
3667 int sig;
3668 if (!dll) {
3669 #ifdef HAVE_CODEGEN_TRAPS
3670 os_signal_untrap(SIGFPE);
3671 #if defined(ARCH_MIPS)
3672 os_signal_untrap(SIGTRAP);
3673 #endif
3674 #endif
3676 for (sig = 0; sig < N_SIGNALS; sig++) {
3677 if (unlikely(signal_states[sig].trapped) || unlikely(signal_states[sig].refcount != 0))
3678 internal(file_line, "signal %d leaked", sig);
3680 mem_free(signal_states);
3681 signal_states = NULL;
3682 #endif
3684 #ifdef OS_HAVE_NOTIFY_PIPE
3685 os_close(os_notify_pipe[0]);
3686 os_close(os_notify_pipe[1]);
3687 #endif
3689 os_dir_close(os_cwd);
3691 mem_free(os_path_to_exe);
3694 void os_init_multithreaded(void)
3696 unsigned u;
3698 os_init_calendar_lock();
3700 rwmutex_init(&fork_lock);
3701 os_threads_initialized = true;
3703 #if !defined(OS_DOS)
3704 tree_init(&proc_tree);
3705 mutex_init(&proc_tree_mutex);
3706 #endif
3708 #ifdef OS_HAS_SIGNALS
3709 mutex_init(&signal_state_mutex);
3710 #endif
3712 for (u = 0; u < n_std_handles; u++)
3713 obj_registry_insert(OBJ_TYPE_HANDLE, u, file_line);
3714 #ifdef OS_DOS
3715 dos_init();
3716 #endif
3719 void os_done_multithreaded(void)
3721 unsigned u;
3722 #ifdef OS_DOS
3723 dos_done();
3724 #endif
3726 for (u = 0; u < n_std_handles; u++)
3727 obj_registry_remove(OBJ_TYPE_HANDLE, u, file_line);
3729 #ifdef OS_HAS_SIGNALS
3730 mutex_done(&signal_state_mutex);
3731 #endif
3733 #if !defined(OS_DOS)
3734 if (unlikely(!tree_is_empty(&proc_tree))) {
3735 struct proc_handle *ph = get_struct(tree_any(&proc_tree), struct proc_handle, entry);
3736 tree_delete(&ph->entry);
3737 proc_handle_free(ph);
3739 mutex_done(&proc_tree_mutex);
3740 #endif
3742 os_threads_initialized = false;
3743 rwmutex_done(&fork_lock);
3745 os_done_calendar_lock();
3748 #endif