x86-64: hack the ABI of cg_upcall_ipret_copy_variable_to_pointer
[ajla.git] / os_posix.c
blob1ffbb75c42706ac18199dd530d3f31c2c0c4e290
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 <unistd.h>
37 #include <dirent.h>
38 #ifdef HAVE_SYS_SYSMACROS_H
39 #include <sys/sysmacros.h>
40 #endif
41 #ifdef HAVE_LINUX_FALLOC_H
42 #include <linux/falloc.h>
43 #endif
44 #include <time.h>
45 #include <sys/time.h>
46 #include <sys/wait.h>
47 #ifdef HAVE_SYS_SELECT_H
48 #include <sys/select.h>
49 #endif
50 #ifdef HAVE_NETWORK
51 #include <sys/socket.h>
52 #include <netinet/in.h>
53 #include <netdb.h>
54 #endif
55 #ifdef OS_HAS_DLOPEN
56 #include <dlfcn.h>
57 #endif
58 #include <signal.h>
59 #include <sys/ioctl.h>
60 #if defined(HAVE_SYS_PARAM_H)
61 #include <sys/param.h>
62 #endif
63 #if defined(HAVE_SYS_UCRED_H)
64 #include <sys/ucred.h>
65 #endif
66 #if defined(HAVE_SYS_MOUNT_H)
67 #include <sys/mount.h>
68 #endif
69 #if defined(HAVE_LINUX_FS_H) && !defined(__TINYC__)
70 #include <linux/fs.h>
71 #endif
73 #define SOCKADDR_MAX_LEN 65535
74 #define SOCKADDR_ALIGN 16
76 #ifndef wake_up_wait_list
77 void u_name(wake_up_wait_list)(struct list *wait_list, mutex_t *mutex_to_lock, bool can_allocate_memory);
78 void c_name(wake_up_wait_list)(struct list *wait_list, mutex_t *mutex_to_lock, bool can_allocate_memory);
79 #endif
81 #if !defined(THREAD_NONE) && defined(USE_SIGPROCMASK) && defined(HAVE_PTHREAD) && defined(HAVE_PTHREAD_SIGMASK)
82 #include <pthread.h>
83 #define USE_PTHREAD_SIGMASK
84 #endif
86 #ifdef OS_USE_LARGEFILE64_SOURCE
87 #define fstat fstat64
88 #define fstatvfs fstatvfs64
89 #define ftruncate ftruncate64
90 #define lseek lseek64
91 #define lstat lstat64
92 #define mmap mmap64
93 #define open open64
94 #define pread pread64
95 #define pwrite pwrite64
96 #define stat stat64
97 #define statvfs statvfs64
98 #define truncate truncate64
99 #endif
101 static rwmutex_t fork_lock;
102 static bool os_threads_initialized = false;
104 #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)
105 static bool have_O_CLOEXEC_openat = false;
106 #define HAVE_AT_FUNCTIONS
107 #endif
109 dir_handle_t os_cwd;
112 #include "os_com.inc"
115 static void os_lock_fork(bool for_write)
117 if (os_threads_initialized) {
118 if (!for_write)
119 rwmutex_lock_read(&fork_lock);
120 else
121 rwmutex_lock_write(&fork_lock);
125 static void os_unlock_fork(bool for_write)
127 if (os_threads_initialized) {
128 if (!for_write)
129 rwmutex_unlock_read(&fork_lock);
130 else
131 rwmutex_unlock_write(&fork_lock);
135 uint32_t os_get_last_error(void)
137 return 0;
140 uint32_t os_get_last_socket_error(void)
142 return 0;
145 #ifdef OS_HAS_MMAP
147 int os_getpagesize(void)
149 #if defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
151 long ps;
152 EINTR_LOOP(ps, sysconf(_SC_PAGESIZE));
153 if (unlikely(ps == -1)) {
154 int er = errno;
155 warning("sysconf(_SC_PAGESIZE) returned error: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
156 } else {
157 return ps;
160 #elif defined(HAVE_GETPAGESIZE)
162 int ps;
163 EINTR_LOOP(ps, getpagesize());
164 if (unlikely(ps == -1)) {
165 int er = errno;
166 warning("getpagesize() returned error: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
167 } else {
168 return ps;
171 #endif
172 return 512;
175 void *os_mmap(void *ptr, size_t size, int prot, int flags, int h, os_off_t off, ajla_error_t *err)
177 void *p;
178 #ifdef PROT_MPROTECT
179 prot |= PROT_MPROTECT(PROT_EXEC);
180 #endif
181 #ifndef HAVE_MPROTECT
182 prot |= PROT_EXEC;
183 #endif
184 EINTR_LOOP_VAL(p, MAP_FAILED, mmap(ptr, size, prot, flags, h, off));
185 if (unlikely(p == MAP_FAILED)) {
186 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
187 fatal_mayfail(e, err, "can't map memory: %s", error_decode(e));
188 return MAP_FAILED;
190 return p;
193 void os_munmap(void *ptr, size_t size, bool attr_unused file)
195 int r;
196 EINTR_LOOP(r, munmap(ptr, size));
197 if (unlikely(r == -1)) {
198 int er = errno;
199 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)));
203 bool os_mprotect(void attr_unused *ptr, size_t attr_unused size, int attr_unused prot, ajla_error_t *err)
205 #ifdef HAVE_MPROTECT
206 int r;
207 EINTR_LOOP(r, mprotect(ptr, size, prot));
208 if (unlikely(r == -1)) {
209 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
210 fatal_mayfail(e, err, "can't protect memory: %s", error_decode(e));
211 return false;
213 return true;
214 #else
215 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "the system doesn't support mprotect");
216 return false;
217 #endif
220 #ifdef OS_HAS_MREMAP
221 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)
223 void *p;
224 #ifdef MREMAP_FIXED
225 EINTR_LOOP_VAL(p, MAP_FAILED, mremap(old_ptr, old_size, new_size, flags, new_ptr));
226 #else
227 EINTR_LOOP_VAL(p, MAP_FAILED, mremap(old_ptr, old_size, new_size, flags));
228 #endif
229 if (unlikely(p == MAP_FAILED)) {
230 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
231 fatal_mayfail(e, err, "can't remap memory: %s", error_decode(e));
232 return MAP_FAILED;
234 return p;
236 #endif
238 #endif
241 void os_code_invalidate_cache(uint8_t attr_unused *code, size_t attr_unused code_size, bool attr_unused set_exec)
243 #if defined(ARCH_PARISC) && defined(HAVE_GCC_ASSEMBLER)
244 size_t i;
245 size_t cl_size = cpu_test_feature(CPU_FEATURE_pa20) ? 64 : 16;
246 size_t align = ptr_to_num(code) & (cl_size - 1);
247 code -= align;
248 code_size += align;
249 __asm__ volatile ("sync" : : : "memory");
250 for (i = 0; i < code_size; i += cl_size) {
251 __asm__ volatile ("fdc %%r0(%0)" : : "r"(code + i) : "memory");
253 __asm__ volatile ("sync");
254 #if defined(ARCH_PARISC32)
255 if (PA_SPACES) {
256 unsigned long reg;
257 __asm__ volatile("ldsid (%1), %0\n mtsp %0, %%sr0" : "=r"(reg) : "r"(code) : "memory");
259 #endif
260 for (i = 0; i < code_size; i += cl_size) {
261 #if defined(ARCH_PARISC32)
262 if (PA_SPACES) {
263 __asm__ volatile ("fic %%r0(%%sr0, %0)" : : "r"(code + i) : "memory");
264 } else {
265 __asm__ volatile ("fic %%r0(%%sr4, %0)" : : "r"(code + i) : "memory");
267 #else
268 __asm__ volatile ("fic %%r0(%0)" : : "r"(code + i) : "memory");
269 #endif
271 __asm__ volatile ("sync" : : : "memory");
272 #elif defined(ARCH_ALPHA)
273 /* imb doesn't work on SMP systems */
274 #elif defined(ARCH_SPARC64) && defined(HAVE_GCC_ASSEMBLER)
275 size_t i;
276 __asm__ volatile ("membar #StoreStore" : : : "memory");
277 for (i = 0; i < code_size; i += 8) {
278 __asm__ volatile ("flush %0" : : "r"(code + i) : "memory");
280 #elif defined(HAVE___BUILTIN___CLEAR_CACHE)
281 __builtin___clear_cache(cast_ptr(void *, code), cast_ptr(char *, code) + code_size);
282 #endif
283 #if defined(OS_HAS_MMAP) && defined(HAVE_MPROTECT)
284 if (set_exec) {
285 int prot_flags = PROT_READ | PROT_EXEC
286 #ifdef CODEGEN_USE_HEAP
287 | PROT_WRITE
288 #endif
290 int page_size = os_getpagesize();
291 int front_pad = ptr_to_num(code) & (page_size - 1);
292 uint8_t *mem_region = code - front_pad;
293 size_t mem_length = code_size + front_pad;
294 mem_length = round_up(mem_length, page_size);
295 os_mprotect(mem_region, mem_length, prot_flags, NULL);
297 #endif
300 void *os_code_map(uint8_t *code, size_t code_size, ajla_error_t attr_unused *err)
302 #ifdef CODEGEN_USE_HEAP
303 os_code_invalidate_cache(code, code_size, !amalloc_enabled);
304 return code;
305 #else
306 size_t rounded_size = round_up(code_size, os_getpagesize());
307 void *ptr = os_mmap(NULL, rounded_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, handle_none, 0, err);
308 if (unlikely(ptr == MAP_FAILED)) {
309 mem_free(code);
310 return NULL;
312 memcpy(ptr, code, code_size);
313 os_code_invalidate_cache(ptr, code_size, true);
314 mem_free(code);
315 return ptr;
316 #endif
319 void os_code_unmap(void *mapped_code, size_t attr_unused code_size)
321 #ifdef CODEGEN_USE_HEAP
322 mem_free(mapped_code);
323 #else
324 size_t rounded_size = round_up(code_size, os_getpagesize());
325 os_munmap(mapped_code, rounded_size, false);
326 #endif
330 void os_block_signals(sig_state_t attr_unused *set)
332 #ifdef USE_SIGPROCMASK
333 int er;
334 sig_state_t block;
335 sigfillset(&block);
336 sigdelset(&block, SIGFPE);
337 sigdelset(&block, SIGTRAP);
338 #ifdef USE_PTHREAD_SIGMASK
339 er = pthread_sigmask(SIG_BLOCK, &block, set);
340 if (unlikely(er))
341 fatal("pthread_sigmask failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
342 #else
343 if (unlikely(sigprocmask(SIG_BLOCK, &block, set))) {
344 er = errno;
345 fatal("sigprocmask failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
347 #endif
348 #elif defined(HAVE_SIGBLOCK) && defined(HAVE_SIGSETMASK)
349 sig_state_t s = sigblock(~(sigmask(SIGFPE) | sigmask(SIGTRAP)));
350 if (set)
351 *set = s;
352 #endif
355 void os_unblock_signals(const sig_state_t attr_unused *set)
357 #ifdef USE_SIGPROCMASK
358 int er;
359 #ifdef USE_PTHREAD_SIGMASK
360 er = pthread_sigmask(SIG_SETMASK, set, NULL);
361 if (unlikely(er))
362 fatal("pthread_sigmask failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
363 #else
364 if (unlikely(sigprocmask(SIG_SETMASK, set, NULL))) {
365 er = errno;
366 fatal("sigprocmask failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
368 #endif
369 #elif defined(HAVE_SIGBLOCK) && defined(HAVE_SIGSETMASK)
370 sigsetmask(*set);
371 #endif
374 #if !defined(OS_DOS)
375 static void os_unblock_all_signals(void)
377 sig_state_t unblock;
378 #ifdef USE_SIGPROCMASK
379 sigemptyset(&unblock);
380 #elif defined(HAVE_SIGBLOCK) && defined(HAVE_SIGSETMASK)
381 unblock = 0;
382 #endif
383 os_unblock_signals(&unblock);
385 #endif
388 void attr_cold os_stop(void)
390 #ifdef SIGSTOP
391 kill(getpid(), SIGSTOP);
392 #else
393 warning("stop not supported");
394 #endif
397 static inline void u_sleep(unsigned us)
399 struct timeval tv;
400 tv.tv_sec = us / 1000000;
401 tv.tv_usec = us % 1000000;
402 select(0, NULL, NULL, NULL, &tv);
405 void os_background(void)
407 #ifndef OS_DOS
408 int r;
409 pid_t pa, p;
410 #ifdef __linux__
411 sig_state_t set;
412 #endif
413 pa = getpid();
414 os_lock_fork(true);
415 #ifdef __linux__
416 os_block_signals(&set);
417 #endif
418 EINTR_LOOP(p, fork());
419 #ifdef __linux__
420 os_unblock_signals(&set);
421 #endif
422 if (!p) {
423 while (1) {
425 * Note that this is racy. If we send SIGCONT too
426 * quickly, the ajla process will not be put to
427 * background.
429 u_sleep(100000);
430 kill(pa, SIGCONT);
433 os_unlock_fork(true);
434 if (p == -1)
435 return;
436 kill(pa, SIGSTOP);
438 * Another race - we must not send SIGKILL too quickly
440 u_sleep(100000);
441 kill(p, SIGKILL);
442 EINTR_LOOP(r, waitpid(p, NULL, 0));
443 #endif
446 bool os_foreground(void)
448 int sigttin, sigttou;
449 signal_seq_t seq;
450 os_termios_t tc;
451 int r;
453 sigttin = os_signal_handle("SIGTTIN", &seq, NULL);
454 sigttou = os_signal_handle("SIGTTOU", &seq, NULL);
455 r = tcgetattr(0, &tc);
456 if (!r)
457 r = tcsetattr(0, TCSANOW, &tc);
458 os_signal_unhandle(sigttin);
459 os_signal_unhandle(sigttou);
460 return !r;
464 void os_set_cloexec(handle_t h)
466 int r;
467 EINTR_LOOP(r, fcntl(h, F_SETFD, FD_CLOEXEC));
468 if (unlikely(r == -1)) {
469 int er = errno;
470 fatal("fcntl(F_SETFD, FD_CLOEXEC) failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
474 static char *os_call_getcwd(ajla_error_t *err)
476 char *h, *r;
477 ajla_error_t e;
478 size_t buf_size = 32;
480 again:
481 h = mem_alloc_mayfail(char *, buf_size, err);
482 if (unlikely(!h))
483 return NULL;
484 EINTR_LOOP_VAL(r, NULL, getcwd(h, buf_size));
485 if (unlikely(!r)) {
486 if (errno == ERANGE) {
487 mem_free(h);
488 buf_size *= 2;
489 if (unlikely(!buf_size)) {
490 fatal_mayfail(error_ajla(EC_ASYNC, AJLA_ERROR_SIZE_OVERFLOW), err, "overflow when allocating directory buffer");
491 return NULL;
493 goto again;
495 e = error_from_errno(EC_SYSCALL, errno);
496 fatal_mayfail(e, err, "can't get working directory: %s", error_decode(e));
497 mem_free(h);
498 return NULL;
500 #ifdef __GLIBC__
501 if (unlikely(h[0] != '/')) {
502 e = error_from_errno(EC_SYSCALL, ENOENT);
503 fatal_mayfail(e, err, "can't get working directory: %s", error_decode(e));
504 mem_free(h);
505 return NULL;
507 #endif
509 return h;
512 static dir_handle_t os_get_cwd(ajla_error_t *err)
514 #ifndef NO_DIR_HANDLES
515 dir_handle_t h;
516 #ifdef HAVE_AT_FUNCTIONS
517 if (likely(have_O_CLOEXEC_openat)) {
518 EINTR_LOOP(h, open(".", O_RDONLY | O_CLOEXEC, 0));
519 if (unlikely(h == -1)) {
520 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
521 fatal_mayfail(e, err, "can't open the current directory: %s", error_decode(e));
522 } else {
523 obj_registry_insert(OBJ_TYPE_HANDLE, h, file_line);
525 return h;
527 #endif
528 EINTR_LOOP(h, open(".", O_RDONLY, 0));
529 if (unlikely(h == -1)) {
530 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
531 fatal_mayfail(e, err, "cam't open the current directory: %s", error_decode(e));
532 } else {
533 obj_registry_insert(OBJ_TYPE_HANDLE, h, file_line);
534 os_set_cloexec(h);
537 return h;
538 #else
539 return os_call_getcwd(err);
540 #endif
543 bool os_set_cwd(dir_handle_t h, ajla_error_t *err)
545 #ifndef NO_DIR_HANDLES
546 int r;
547 EINTR_LOOP(r, fchdir(h));
548 if (unlikely(r == -1)) {
549 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
550 fatal_mayfail(e, err, "can't set directory: %s", error_decode(e));
551 return false;
553 #else
554 int r;
555 EINTR_LOOP(r, chdir(h));
556 if (unlikely(r == -1)) {
557 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
558 fatal_mayfail(e, err, "can't set directory '%s': %s", h, error_decode(e));
559 return false;
561 #endif
562 return true;
565 void os_set_original_cwd(void)
567 int r;
568 ajla_error_t sink;
569 if (likely(os_set_cwd(os_cwd, &sink)))
570 return;
571 EINTR_LOOP(r, chdir("/"));
572 if (unlikely(r == -1)) {
573 int er = errno;
574 fatal("unable to select root directory: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
578 static handle_t os_open_internal(dir_handle_t dir, const char *path, int flags, int mode, bool want_dir, ajla_error_t *err)
580 int h;
581 bool abs_path = os_path_is_absolute(path);
583 if (unlikely(!os_test_absolute_path(dir, abs_path, err)))
584 return -1;
586 #ifdef O_DIRECTORY
587 if (want_dir)
588 flags |= O_DIRECTORY;
589 #endif
591 #ifdef HAVE_AT_FUNCTIONS
592 if (likely(have_O_CLOEXEC_openat)) {
593 if (!dir_handle_is_valid(dir)) {
594 EINTR_LOOP(h, open(path, flags | O_CLOEXEC, mode));
595 } else {
596 EINTR_LOOP(h, openat(dir, path, flags | O_CLOEXEC, mode));
598 if (h == -1) {
599 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
600 #ifdef O_PATH
601 if (errno == EACCES && want_dir) {
602 EINTR_LOOP(h, openat(dir, path, flags | O_CLOEXEC | O_PATH, mode));
603 if (h != -1)
604 goto have_it;
606 #endif
607 fatal_mayfail(e, err, "can't open file '%s': %s", path, error_decode(e));
608 } else {
609 goto have_it;
610 have_it:
611 obj_registry_insert(OBJ_TYPE_HANDLE, h, file_line);
613 goto test_dir_ret_h;
615 #endif
616 os_lock_fork(!abs_path);
618 if (!abs_path) {
619 if (unlikely(!os_set_cwd(dir, err))) {
620 h = -1;
621 goto restore_dir_ret;
625 EINTR_LOOP(h, open(path, flags, mode));
626 if (unlikely(h == -1)) {
627 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
628 fatal_mayfail(e, err, "can't open file '%s': %s", path, error_decode(e));
629 goto restore_dir_ret;
631 obj_registry_insert(OBJ_TYPE_HANDLE, h, file_line);
633 os_set_cloexec(h);
635 restore_dir_ret:
636 if (!abs_path) {
637 os_set_original_cwd();
640 os_unlock_fork(!abs_path);
642 #ifdef HAVE_AT_FUNCTIONS
643 test_dir_ret_h:
644 #endif
645 if (likely(h != -1)) {
646 os_stat_t st;
647 if (!want_dir) {
648 if (!(flags & (O_WRONLY | O_RDWR))) {
649 if (unlikely(!os_fstat(h, &st, err))) {
650 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
651 fatal_mayfail(e, err, "fstat on file '%s' failed", path);
652 os_close(h);
653 h = -1;
654 } else if (unlikely(S_ISDIR(st.st_mode))) {
655 ajla_error_t e = error_from_errno(EC_SYSCALL, EISDIR);
656 fatal_mayfail(e, err, "file '%s' is a directory", path);
657 os_close(h);
658 h = -1;
661 } else {
662 #ifndef O_DIRECTORY
663 if (unlikely(!os_fstat(h, &st, err))) {
664 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
665 fatal_mayfail(e, err, "fstat on file '%s' failed", path);
666 os_close(h);
667 h = -1;
668 } else if (unlikely(!S_ISDIR(st.st_mode))) {
669 ajla_error_t e = error_from_errno(EC_SYSCALL, ENOTDIR);
670 fatal_mayfail(e, err, "file '%s' is not a directory", path);
671 os_close(h);
672 h = -1;
674 #endif
677 return h;
680 handle_t os_open(dir_handle_t dir, const char *path, int flags, int mode, ajla_error_t *err)
682 #ifdef OS_DOS
683 flags |= O_BINARY;
684 #endif
685 return os_open_internal(dir, path, flags, mode, false, err);
688 bool os_pipe(handle_t result[2], int nonblock_flags, ajla_error_t *err)
690 int r, i;
691 #ifdef HAVE_PIPE2
692 EINTR_LOOP(r, pipe2(result, O_CLOEXEC | (nonblock_flags == 3 ? O_NONBLOCK : 0)));
693 if (likely(r != -1)) {
694 if (nonblock_flags == 3) {
695 obj_registry_insert(OBJ_TYPE_HANDLE, result[0], file_line);
696 obj_registry_insert(OBJ_TYPE_HANDLE, result[1], file_line);
697 return true;
699 goto set_nonblock;
701 if (errno != ENOSYS) {
702 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
703 fatal_mayfail(e, err, "can't create pipe: %s", error_decode(e));
704 return false;
706 #endif
708 os_lock_fork(false);
709 EINTR_LOOP(r, pipe(result));
710 if (unlikely(r == -1)) {
711 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
712 os_unlock_fork(false);
713 fatal_mayfail(e, err, "can't create pipe: %s", error_decode(e));
714 return false;
716 for (i = 0; i < 2; i++)
717 os_set_cloexec(result[i]);
718 os_unlock_fork(false);
720 #ifdef HAVE_PIPE2
721 set_nonblock:
722 #endif
723 for (i = 0; i < 2; i++) {
724 obj_registry_insert(OBJ_TYPE_HANDLE, result[i], file_line);
725 if (nonblock_flags & (1 << i)) {
726 EINTR_LOOP(r, fcntl(result[i], F_SETFL, O_NONBLOCK));
727 if (unlikely(r == -1)) {
728 int er = errno;
729 fatal("fcntl(F_SETFL, O_NONBLOCK) on a pipe failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
733 return true;
736 void os_close_handle(handle_t h)
738 int r;
739 if (unlikely(h < 0))
740 internal(file_line, "os_close: attempting to close invalid handle %d", h);
741 EINTR_LOOP(r, close(h));
742 if (unlikely(r == -1) && errno == EBADF)
743 internal(file_line, "os_close: closing invalid handle %d", h);
746 void os_close(handle_t h)
748 obj_registry_remove(OBJ_TYPE_HANDLE, h, file_line);
749 os_close_handle(h);
752 static unsigned n_std_handles;
754 unsigned os_n_std_handles(void)
756 return n_std_handles;
759 handle_t os_get_std_handle(unsigned h)
761 return (handle_t)h;
764 handle_t os_number_to_handle(uintptr_t n, bool attr_unused sckt, ajla_error_t *err)
766 if (unlikely(n != (uintptr_t)(int)n) || unlikely((int)n < 0)) {
767 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "invalid handle");
768 return handle_none;
770 obj_registry_insert(OBJ_TYPE_HANDLE, (int)n, file_line);
771 return (int)n;
775 static ssize_t os_rdwr_return(int r, const char *msg, ajla_error_t *err)
777 if (unlikely(r == -1)) {
778 ajla_error_t e;
779 if (errno == EAGAIN || errno == EWOULDBLOCK)
780 return OS_RW_WOULDBLOCK;
781 e = error_from_errno(EC_SYSCALL, errno);
782 fatal_mayfail(e, err, "error %s data: %s", msg, error_decode(e));
783 return OS_RW_ERROR;
785 return r;
788 ssize_t os_read(handle_t h, char *buffer, int size, ajla_error_t *err)
790 ssize_t r;
791 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
792 EINTR_LOOP(r, read(h, buffer, size));
793 return os_rdwr_return(r, "reading", err);
796 ssize_t os_write(handle_t h, const char *buffer, int size, ajla_error_t *err)
798 ssize_t r;
799 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
800 EINTR_LOOP(r, write(h, buffer, size));
802 * https://stackoverflow.com/questions/5656628/what-should-i-do-when-writefd-buf-count-returns-0
803 * Long, long ago, pre-POSIX, some systems returned 0 instead of EAGAIN.
805 if (unlikely(!r) && size)
806 return OS_RW_WOULDBLOCK;
807 return os_rdwr_return(r, "writing", err);
810 ssize_t os_pread(handle_t h, char *buffer, int size, os_off_t off, ajla_error_t *err)
812 ssize_t r;
813 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
814 #ifndef DO_LOCK_HANDLES
815 EINTR_LOOP(r, pread(h, buffer, size, off));
816 #else
817 address_lock(num_to_ptr(h), DEPTH_HANDLE);
818 EINTR_LOOP(off, lseek(h, off, SEEK_SET));
819 if (unlikely(off == -1)) {
820 r = -1;
821 goto ret;
823 EINTR_LOOP(r, read(h, buffer, size));
824 ret:
825 address_unlock(num_to_ptr(h), DEPTH_HANDLE);
826 #endif
827 return os_rdwr_return(r, "preading", err);
830 ssize_t os_pwrite(handle_t h, const char *buffer, int size, os_off_t off, ajla_error_t *err)
832 ssize_t r;
833 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
834 #ifndef DO_LOCK_HANDLES
835 EINTR_LOOP(r, pwrite(h, buffer, size, off));
836 #else
837 address_lock(num_to_ptr(h), DEPTH_HANDLE);
838 EINTR_LOOP(off, lseek(h, off, SEEK_SET));
839 if (unlikely(off == -1)) {
840 r = -1;
841 goto ret;
843 EINTR_LOOP(r, write(h, buffer, size));
844 ret:
845 address_unlock(num_to_ptr(h), DEPTH_HANDLE);
846 #endif
847 return os_rdwr_return(r, "pwriting", err);
850 bool os_lseek(handle_t h, unsigned mode, os_off_t off, os_off_t *result, ajla_error_t *err)
852 int whence;
853 os_off_t res;
854 #ifdef DO_LOCK_HANDLES
855 address_lock(num_to_ptr(h), DEPTH_HANDLE);
856 #endif
857 restart:
858 switch (mode) {
859 case 0:
860 whence = SEEK_SET;
861 break;
862 case 1:
863 whence = SEEK_CUR;
864 break;
865 case 2:
866 whence = SEEK_END;
867 break;
868 case 3:
869 #ifdef SEEK_DATA
870 whence = SEEK_DATA;
871 #else
872 EINTR_LOOP(res, lseek(h, 0, SEEK_END));
873 if (unlikely(res == -1))
874 goto ret_error;
875 if (unlikely(off > res))
876 off = res;
877 *result = off;
878 goto ret_true;
879 #endif
880 break;
881 case 4:
882 #ifdef SEEK_HOLE
883 whence = SEEK_HOLE;
884 #else
885 off = 0;
886 whence = SEEK_END;
887 #endif
888 break;
889 default:internal(file_line, "os_lseek: unsupported mode %u", mode);
890 goto ret_false;
892 EINTR_LOOP(res, lseek(h, off, whence));
893 if (unlikely(res == -1)) {
894 ajla_error_t e;
895 if (errno == EINVAL) {
896 if (mode == 3) {
897 *result = off;
898 goto ret_true;
900 if (mode == 4) {
901 off = 0;
902 mode = 2;
903 goto restart;
906 if (errno == ENXIO && mode >= 3) {
907 off = 0;
908 mode = 2;
909 goto restart;
911 goto ret_error;
912 ret_error:
913 e = error_from_errno(EC_SYSCALL, errno);
914 fatal_mayfail(e, err, "can't lseek: %s", error_decode(e));
915 goto ret_false;
917 *result = res;
918 ret_true:
919 #ifdef DO_LOCK_HANDLES
920 address_unlock(num_to_ptr(h), DEPTH_HANDLE);
921 #endif
922 return true;
923 ret_false:
924 #ifdef DO_LOCK_HANDLES
925 address_unlock(num_to_ptr(h), DEPTH_HANDLE);
926 #endif
927 return false;
930 bool os_ftruncate(handle_t h, os_off_t size, ajla_error_t *err)
932 int r;
933 EINTR_LOOP(r, ftruncate(h, size));
934 if (unlikely(r == -1)) {
935 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
936 fatal_mayfail(e, err, "ftruncate returned an error: %s", error_decode(e));
937 return false;
939 return true;
942 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)
944 #ifdef HAVE_FALLOCATE
945 int r;
946 if (unlikely(!size))
947 return true;
948 /* EINTR may cause infinite loop */
949 #if 1
951 sig_state_t set;
952 os_block_signals(&set);
953 EINTR_LOOP(r, fallocate(h, FALLOC_FL_KEEP_SIZE, position, size));
954 os_unblock_signals(&set);
956 #else
957 r = fallocate(h, FALLOC_FL_KEEP_SIZE, position, size);
958 if (unlikely(r == -1) && errno == EINTR)
959 r = fallocate(h, FALLOC_FL_KEEP_SIZE, position, size);
960 #endif
961 if (unlikely(r == -1) && errno != EINTR && errno != ENOSYS && errno != EOPNOTSUPP) {
962 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
963 fatal_mayfail(e, err, "fallocate returned an error: %s", error_decode(e));
964 return false;
966 #endif
967 return true;
970 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)
972 #ifdef FICLONERANGE
973 int r;
974 struct file_clone_range c;
975 c.src_fd = src_h;
976 c.src_offset = src_pos;
977 c.src_length = len;;
978 c.dest_offset = dst_pos;
979 EINTR_LOOP(r, ioctl(dst_h, FICLONERANGE, &c));
980 if (unlikely(r == -1)) {
981 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
982 fatal_mayfail(e, err, "clone range returned an error: %s", error_decode(e));
983 return false;
985 return true;
986 #endif
987 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "clone not supported");
988 return false;
990 bool os_fsync(handle_t h, unsigned mode, ajla_error_t *err)
992 int r;
993 #ifdef __APPLE__
994 if (mode == 0 || mode == 1) {
995 EINTR_LOOP(r, fcntl(h, F_FULLFSYNC));
996 if (likely(r != -1))
997 goto ret;
999 #endif
1000 #if defined(HAVE_FDATASYNC) && !defined(__APPLE__)
1001 if (mode == 0) {
1002 EINTR_LOOP(r, fdatasync(h));
1003 goto ret;
1005 #endif
1006 if (mode == 0 || mode == 1) {
1007 EINTR_LOOP(r, fsync(h));
1008 goto ret;
1010 #ifdef HAVE_SYNCFS
1011 if (mode == 2) {
1012 EINTR_LOOP(r, syncfs(h));
1013 goto ret;
1015 #endif
1016 if (mode == 2 || mode == 3) {
1017 sync();
1018 return true;
1020 internal(file_line, "os_fsync: invalid mode %u", mode);
1021 ret:
1022 if (unlikely(r == -1)) {
1023 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
1024 fatal_mayfail(e, err, "fsync returned an error: %s", error_decode(e));
1025 return false;
1027 return true;
1030 #if !defined(OS_DOS)
1031 ssize_t os_read_console_packet(handle_t attr_unused h, struct console_read_packet attr_unused *result, ajla_error_t *err)
1033 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "console packets not supported");
1034 return OS_RW_ERROR;
1037 bool os_write_console_packet(handle_t attr_unused h, struct console_write_packet attr_unused *packet, ajla_error_t *err)
1039 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "console packets not supported");
1040 return false;
1043 dir_handle_t os_dir_root(ajla_error_t *err)
1045 const char *root = "/";
1046 #ifndef NO_DIR_HANDLES
1047 return os_dir_open(dir_none, root, 0, err);
1048 #else
1049 return str_dup(root, -1, err);
1050 #endif
1052 #endif
1054 dir_handle_t os_dir_cwd(ajla_error_t *err)
1056 return os_dir_open(os_cwd, ".", 0, err);
1059 dir_handle_t os_dir_open(dir_handle_t dir, const char *path, int attr_unused flags, ajla_error_t *err)
1061 #ifndef NO_DIR_HANDLES
1062 return os_open_internal(dir, path, O_RDONLY | flags, 0, true, err);
1063 #else
1064 dir_handle_t ret;
1066 if (unlikely(!os_test_absolute_path(dir, os_path_is_absolute(path), err)))
1067 return dir_none;
1069 os_lock_fork(true);
1071 if (dir_handle_is_valid(dir)) {
1072 if (unlikely(!os_set_cwd(dir, err))) {
1073 ret = dir_none;
1074 goto restore_ret;
1078 if (unlikely(!os_set_cwd((dir_handle_t)path, err))) {
1079 ret = dir_none;
1080 goto restore_ret;
1083 ret = os_get_cwd(err);
1085 restore_ret:
1086 os_set_original_cwd();
1088 os_unlock_fork(true);
1089 return ret;
1090 #endif
1093 void os_dir_close(dir_handle_t h)
1095 #ifndef NO_DIR_HANDLES
1096 os_close(h);
1097 #else
1098 mem_free(h);
1099 #endif
1102 char *os_dir_path(dir_handle_t h, ajla_error_t *err)
1104 #ifndef NO_DIR_HANDLES
1105 char *path;
1106 #ifdef __linux__
1107 ajla_error_t sink;
1108 char lnk[25];
1109 snprintf(lnk, sizeof(lnk), "/proc/self/fd/%u", h);
1110 path = os_readlink(dir_none, lnk, &sink);
1111 if (likely(path != NULL)) {
1112 size_t sl, dl;
1113 char *deleted = " (deleted)";
1114 if (unlikely(path[0] != '/')) {
1115 mem_free(path);
1116 goto skip_optimization;
1118 sl = strlen(path);
1119 dl = strlen(deleted);
1120 if (sl >= dl && unlikely(!memcmp(path + sl - dl, deleted, dl))) {
1121 mem_free(path);
1122 goto skip_optimization;
1124 return path;
1126 skip_optimization:
1127 #endif
1128 os_lock_fork(true);
1129 if (unlikely(!os_set_cwd(h, err))) {
1130 path = NULL;
1131 goto unlock_ret;
1133 path = os_call_getcwd(err);
1134 os_set_original_cwd();
1135 unlock_ret:
1136 os_unlock_fork(true);
1137 return path;
1138 #else
1139 return str_dup(h, -1, err);
1140 #endif
1143 static void os_close_DIR(DIR *d)
1145 int r;
1146 EINTR_LOOP(r, closedir(d));
1147 if (unlikely(r))
1148 internal(file_line, "os_close_DIR: closing invalid directory handle: %s", error_decode(error_from_errno(EC_SYSCALL, errno)));
1151 bool os_dir_read(dir_handle_t h, char ***files, size_t *n_files, ajla_error_t *err)
1153 ajla_error_t e;
1154 DIR *d;
1155 #if !defined(NO_DIR_HANDLES)
1156 int er;
1157 os_lock_fork(true);
1158 if (unlikely(!os_set_cwd(h, err))) {
1159 os_set_original_cwd();
1160 os_unlock_fork(true);
1161 return false;
1163 EINTR_LOOP_VAL(d, NULL, opendir("."));
1164 er = errno;
1165 os_set_original_cwd();
1166 os_unlock_fork(true);
1167 errno = er;
1168 #else
1169 EINTR_LOOP_VAL(d, NULL, opendir(h));
1170 #endif
1171 if (unlikely(!d)) {
1172 e = error_from_errno(EC_SYSCALL, errno);
1173 fatal_mayfail(e, err, "can't open directory: %s", error_decode(e));
1174 return false;
1176 if (unlikely(!array_init_mayfail(char *, files, n_files, err))) {
1177 os_close_DIR(d);
1178 return false;
1181 while (1) {
1182 struct dirent *de;
1183 char *fn;
1184 errno = 0;
1185 de = readdir(d);
1186 if (unlikely(!de)) {
1187 if (likely(!errno))
1188 break;
1189 e = error_from_errno(EC_SYSCALL, errno);
1190 fatal_mayfail(e, err, "error reading directory directory: %s", error_decode(e));
1191 os_dir_free(*files, *n_files);
1192 os_close_DIR(d);
1193 return false;
1195 if (unlikely(!strcmp(de->d_name, ".")) ||
1196 unlikely(!strcmp(de->d_name, "..")))
1197 continue;
1198 fn = mem_alloc_mayfail(char *, strlen(de->d_name) + 1, err);
1199 if (unlikely(!fn)) {
1200 os_dir_free(*files, *n_files);
1201 os_close_DIR(d);
1202 return false;
1204 strcpy(fn, de->d_name);
1205 array_add(char *, files, n_files, fn);
1207 os_close_DIR(d);
1208 return true;
1211 void os_dir_free(char **files, size_t n_files)
1213 size_t i;
1214 for (i = 0; i < n_files; i++)
1215 mem_free(files[i]);
1216 mem_free(files);
1220 unsigned os_dev_t_major(dev_t dev)
1222 #if defined(HAVE_SYS_SYSMACROS_H) || defined(major)
1223 return major(dev);
1224 #else
1225 return (dev >> 8) & 0xff;
1226 #endif
1229 unsigned os_dev_t_minor(dev_t dev)
1231 #if defined(HAVE_SYS_SYSMACROS_H) || defined(minor)
1232 return minor(dev);
1233 #else
1234 return dev & 0xff;
1235 #endif
1238 bool os_fstat(handle_t h, os_stat_t *st, ajla_error_t *err)
1240 int r;
1241 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
1242 EINTR_LOOP(r, fstat(h, st));
1243 if (unlikely(r == -1)) {
1244 ajla_error_t e;
1245 if (unlikely(errno == EBADF))
1246 internal(file_line, "os_fstat: invalid handle %d", h);
1247 e = error_from_errno(EC_SYSCALL, errno);
1248 fatal_mayfail(e, err, "can't stat file handle: %s", error_decode(e));
1249 return false;
1251 return true;
1254 bool os_stat(dir_handle_t dir, const char *path, bool attr_unused lnk, os_stat_t *st, ajla_error_t *err)
1256 int r;
1257 bool abs_path = os_path_is_absolute(path);
1259 if (unlikely(!os_test_absolute_path(dir, abs_path, err)))
1260 return false;
1262 #ifdef HAVE_AT_FUNCTIONS
1263 if (likely(have_O_CLOEXEC_openat) && dir_handle_is_valid(dir)) {
1264 EINTR_LOOP(r, fstatat(dir, path, st, lnk ? AT_SYMLINK_NOFOLLOW : 0));
1265 if (unlikely(r == -1)) {
1266 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
1267 fatal_mayfail(e, err, "can't open file '%s': %s", path, error_decode(e));
1269 return r != -1;
1271 #endif
1272 if (!abs_path) {
1273 os_lock_fork(true);
1274 if (unlikely(!os_set_cwd(dir, err))) {
1275 r = -1;
1276 goto unlock_ret_r;
1280 #ifdef HAVE_LSTAT
1281 EINTR_LOOP(r, (!lnk ? stat : lstat)(path, st));
1282 #else
1283 EINTR_LOOP(r, stat(path, st));
1284 #endif
1285 if (unlikely(r == -1)) {
1286 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
1287 fatal_mayfail(e, err, "can't open file '%s': %s", path, error_decode(e));
1290 unlock_ret_r:
1291 if (!abs_path) {
1292 os_set_original_cwd();
1293 os_unlock_fork(true);
1296 return r != -1;
1299 #if (defined(HAVE_FSTATFS) && !defined(HAVE_FSTATVFS)) || (defined(HAVE_STATFS) && !defined(HAVE_STATVFS))
1300 static inline void attr_unused statfs_2_statvfs(struct statfs *stfs, os_statvfs_t *st)
1302 memset(st, 0, sizeof(os_statvfs_t));
1303 #if defined(__linux__)
1304 st->f_bsize = stfs->f_bsize;
1305 st->f_frsize = stfs->f_bsize;
1306 #elif defined(__FreeBSD__) || defined(__OpenBSD__)
1307 st->f_bsize = stfs->f_iosize;
1308 st->f_frsize = stfs->f_bsize;
1309 #else
1310 st->f_bsize = stfs->f_bsize;
1311 st->f_frsize = stfs->f_bsize;
1312 #endif
1313 st->f_blocks = stfs->f_blocks;
1314 st->f_bfree = stfs->f_bfree;
1315 st->f_bavail = stfs->f_bavail;
1316 st->f_files = stfs->f_files;
1317 st->f_ffree = stfs->f_ffree;
1318 st->f_favail = stfs->f_ffree;
1319 memcpy(&st->f_fsid, &stfs->f_fsid, minimum(sizeof(st->f_fsid), sizeof(stfs->f_fsid)));
1320 #if defined(__linux__)
1321 st->f_namemax = stfs->f_namelen;
1322 #elif defined(__FreeBSD__) || defined(__OpenBSD__)
1323 st->f_namemax = stfs->f_namemax;
1324 #else
1325 st->f_namemax = 255;
1326 #endif
1328 #endif
1330 bool os_fstatvfs(handle_t h, os_statvfs_t *st, ajla_error_t *err)
1332 ajla_error_t e;
1333 int r;
1334 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
1335 #if defined(HAVE_FSTATVFS)
1336 EINTR_LOOP(r, fstatvfs(h, st));
1337 if (unlikely(r == -1))
1338 goto err;
1339 return true;
1340 #elif defined(HAVE_FSTATFS)
1342 struct statfs stfs;
1343 EINTR_LOOP(r, fstatfs(h, &stfs));
1344 if (unlikely(r == -1))
1345 goto err;
1346 statfs_2_statvfs(&stfs, st);
1347 return true;
1349 #endif
1350 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "the system doesn't support mprotect");
1351 return false;
1353 goto err;
1354 err:
1355 if (unlikely(errno == EBADF))
1356 internal(file_line, "os_fstatvfs: invalid handle %d", h);
1357 e = error_from_errno(EC_SYSCALL, errno);
1358 fatal_mayfail(e, err, "can't fstatvfs file handle: %s", error_decode(e));
1359 return false;
1362 bool os_dstatvfs(dir_handle_t dir, os_statvfs_t *st, ajla_error_t *err)
1364 ajla_error_t attr_unused e;
1365 int attr_unused r;
1366 #ifndef NO_DIR_HANDLES
1367 return os_fstatvfs(dir, st, err);
1368 #elif defined(HAVE_STATVFS)
1369 EINTR_LOOP(r, statvfs(dir, st));
1370 if (unlikely(r == -1))
1371 goto err;
1372 return true;
1373 #elif defined(HAVE_STATFS)
1375 struct statfs stfs;
1376 EINTR_LOOP(r, statfs(dir, &stfs));
1377 if (unlikely(r == -1))
1378 goto err;
1379 statfs_2_statvfs(&stfs, st);
1380 return true;
1382 #endif
1383 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "the system doesn't support mprotect");
1384 return false;
1386 goto err;
1387 err:
1388 e = error_from_errno(EC_SYSCALL, errno);
1389 fatal_mayfail(e, err, "can't statvfs directory: %s", error_decode(e));
1390 return false;
1393 char *os_readlink(dir_handle_t attr_unused dir, const char attr_unused *path, ajla_error_t *err)
1395 #ifdef HAVE_READLINK
1396 size_t buf_size = 32;
1397 ssize_t r;
1398 char *buf;
1399 bool abs_path = os_path_is_absolute(path);
1401 if (unlikely(!os_test_absolute_path(dir, abs_path, err)))
1402 return NULL;
1404 alloc_larger:
1405 buf = mem_alloc_mayfail(char *, buf_size, err);
1406 if (unlikely(!buf))
1407 return NULL;
1409 #ifdef HAVE_AT_FUNCTIONS
1410 if (likely(have_O_CLOEXEC_openat)) {
1411 EINTR_LOOP(r, readlinkat(dir, path, buf, buf_size));
1412 goto proc_r;
1414 #endif
1415 if (!abs_path) {
1416 os_lock_fork(true);
1417 if (unlikely(!os_set_cwd(dir, err))) {
1418 os_unlock_fork(true);
1419 mem_free(buf);
1420 return NULL;
1424 EINTR_LOOP(r, readlink(path, buf, buf_size));
1426 if (!abs_path) {
1427 int e = errno;
1428 os_set_original_cwd();
1429 os_unlock_fork(true);
1430 errno = e;
1433 #ifdef HAVE_AT_FUNCTIONS
1434 proc_r:
1435 #endif
1436 if (unlikely(r == -1)) {
1437 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
1438 fatal_mayfail(e, err, "can't read link '%s': %s", path, error_decode(e));
1439 mem_free(buf);
1440 return NULL;
1442 if (unlikely((size_t)r == buf_size)) {
1443 mem_free(buf);
1444 buf_size *= 2;
1445 if (unlikely((buf_size * 2) == 0)) {
1446 fatal_mayfail(error_ajla(EC_ASYNC, AJLA_ERROR_SIZE_OVERFLOW), err, "overflow when allocating readlink buffer");
1447 return NULL;
1449 goto alloc_larger;
1452 buf[r] = 0;
1454 return buf;
1455 #else
1456 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "readlink not supported");
1457 return NULL;
1458 #endif
1461 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)
1463 int r;
1464 bool abs_path = os_path_is_absolute(path);
1466 if (unlikely((mode & ~07777) != 0)) {
1467 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "invalid mode: %d", mode);
1468 return false;
1471 if (unlikely(!os_test_absolute_path(dir, abs_path, err)))
1472 return false;
1474 #ifdef HAVE_AT_FUNCTIONS
1475 if (likely(have_O_CLOEXEC_openat)) {
1476 if (!dir_handle_is_valid(dir))
1477 dir = AT_FDCWD;
1478 switch (action) {
1479 case IO_Action_Rm:
1480 EINTR_LOOP(r, unlinkat(dir, path, 0));
1481 break;
1482 case IO_Action_Rm_Dir:
1483 EINTR_LOOP(r, unlinkat(dir, path, AT_REMOVEDIR));
1484 break;
1485 case IO_Action_Mk_Dir:
1486 EINTR_LOOP(r, mkdirat(dir, path, mode));
1487 break;
1488 case IO_Action_Mk_Pipe:
1489 EINTR_LOOP(r, mknodat(dir, path, mode | S_IFIFO, 0));
1490 break;
1491 case IO_Action_Mk_Socket:
1492 EINTR_LOOP(r, mknodat(dir, path, mode | S_IFSOCK, 0));
1493 break;
1494 case IO_Action_Mk_CharDev:
1495 #if defined(HAVE_SYS_SYSMACROS_H) || defined(makedev)
1496 EINTR_LOOP(r, mknodat(dir, path, mode | S_IFCHR, makedev(dev_major, dev_minor)));
1497 #else
1498 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "mkchardev not supported");
1499 return false;
1500 #endif
1501 break;
1502 case IO_Action_Mk_BlockDev:
1503 #if defined(HAVE_SYS_SYSMACROS_H) || defined(makedev)
1504 EINTR_LOOP(r, mknodat(dir, path, mode | S_IFBLK, makedev(dev_major, dev_minor)));
1505 #else
1506 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "mkblockdev not supported");
1507 return false;
1508 #endif
1509 break;
1510 case IO_Action_Mk_SymLink:
1511 EINTR_LOOP(r, symlinkat(syml, dir, path));
1512 break;
1513 case IO_Action_ChMod:
1514 EINTR_LOOP(r, fchmodat(dir, path, mode, 0));
1515 break;
1516 case IO_Action_ChOwn:
1517 EINTR_LOOP(r, fchownat(dir, path, dev_major, dev_minor, 0));
1518 break;
1519 case IO_Action_LChOwn:
1520 EINTR_LOOP(r, fchownat(dir, path, dev_major, dev_minor, AT_SYMLINK_NOFOLLOW));
1521 break;
1522 case IO_Action_UTime:
1523 case IO_Action_LUTime: {
1524 struct timespec ts[2];
1525 ts[0].tv_sec = dev_minor / 1000000;
1526 ts[0].tv_nsec = dev_minor % 1000000 * 1000;
1527 ts[1].tv_sec = dev_major / 1000000;
1528 ts[1].tv_nsec = dev_major % 1000000 * 1000;
1529 EINTR_LOOP(r, utimensat(dir, path, ts, action == IO_Action_UTime ? 0 : AT_SYMLINK_NOFOLLOW));
1530 break;
1532 default:
1533 internal(file_line, "os_dir_action: invalid action %d", action);
1535 goto proc_r;
1537 #endif
1539 if (!abs_path) {
1540 os_lock_fork(true);
1541 if (unlikely(!os_set_cwd(dir, err))) {
1542 os_unlock_fork(true);
1543 return false;
1547 switch (action) {
1548 case IO_Action_Rm:
1549 EINTR_LOOP(r, unlink(path));
1550 break;
1551 case IO_Action_Rm_Dir:
1552 EINTR_LOOP(r, rmdir(path));
1553 break;
1554 case IO_Action_Mk_Dir:
1555 EINTR_LOOP(r, mkdir(path, mode));
1556 #ifdef __minix__
1558 * Minix 3 returns EACCES when attempting to make the
1559 * home directory. So we test if the directory exists
1560 * and return EEXIST if it does.
1562 if (r == -1 && errno == EACCES) {
1563 struct stat st;
1564 int rr;
1565 EINTR_LOOP(rr, stat(path, &st));
1566 if (!rr)
1567 errno = EEXIST;
1568 else
1569 errno = EACCES;
1571 #endif
1572 break;
1573 case IO_Action_Mk_Pipe:
1574 #ifdef HAVE_MKNOD
1575 EINTR_LOOP(r, mknod(path, mode | S_IFIFO, 0));
1576 #else
1577 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "mkpipe not supported");
1578 goto ret_false;
1579 #endif
1580 break;
1581 case IO_Action_Mk_Socket:
1582 #if defined(HAVE_MKNOD) && defined(S_IFSOCK)
1583 EINTR_LOOP(r, mknod(path, mode | S_IFSOCK, 0));
1584 #else
1585 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "mksocket not supported");
1586 goto ret_false;
1587 #endif
1588 break;
1589 case IO_Action_Mk_CharDev:
1590 #if defined(HAVE_MKNOD) && (defined(HAVE_SYS_SYSMACROS_H) || defined(makedev))
1591 EINTR_LOOP(r, mknod(path, mode | S_IFCHR, makedev(dev_major, dev_minor)));
1592 #else
1593 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "mkchardev not supported");
1594 goto ret_false;
1595 #endif
1596 break;
1597 case IO_Action_Mk_BlockDev:
1598 #if defined(HAVE_MKNOD) && (defined(HAVE_SYS_SYSMACROS_H) || defined(makedev))
1599 EINTR_LOOP(r, mknod(path, mode | S_IFBLK, makedev(dev_major, dev_minor)));
1600 #else
1601 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "mkblockdev not supported");
1602 goto ret_false;
1603 #endif
1604 break;
1605 case IO_Action_Mk_SymLink:
1606 #ifdef HAVE_SYMLINK
1607 EINTR_LOOP(r, symlink(syml, path));
1608 #else
1609 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "symlink not supported");
1610 goto ret_false;
1611 #endif
1612 break;
1613 case IO_Action_ChMod:
1614 EINTR_LOOP(r, chmod(path, mode));
1615 break;
1616 case IO_Action_LChOwn: {
1617 #ifdef HAVE_LCHOWN
1618 EINTR_LOOP(r, lchown(path, dev_major, dev_minor));
1619 break;
1620 #else
1621 struct stat st;
1622 EINTR_LOOP(r, lstat(path, &st));
1623 if (unlikely(r))
1624 break;
1625 if (S_ISLNK(st.st_mode))
1626 break;
1627 #endif
1629 /*-fallthrough*/
1630 case IO_Action_ChOwn:
1631 #ifdef HAVE_CHOWN
1632 EINTR_LOOP(r, chown(path, dev_major, dev_minor));
1633 #else
1634 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "chown not supported");
1635 goto ret_false;
1636 #endif
1637 break;
1638 case IO_Action_LUTime: {
1639 struct stat st;
1640 EINTR_LOOP(r, lstat(path, &st));
1641 if (unlikely(r))
1642 break;
1643 if (S_ISLNK(st.st_mode)) {
1644 r = -1;
1645 errno = -ELOOP;
1646 break;
1649 /*-fallthrough*/
1650 case IO_Action_UTime: {
1651 #if defined(HAVE_UTIMES)
1652 struct timeval ts[2];
1653 ts[0].tv_sec = dev_minor / 1000000;
1654 ts[0].tv_usec = dev_minor % 1000000;
1655 ts[1].tv_sec = dev_major / 1000000;
1656 ts[1].tv_usec = dev_major % 1000000;
1657 EINTR_LOOP(r, utimes(cast_ptr(char *, path), ts));
1658 break;
1659 #elif defined(HAVE_UTIME)
1660 struct utimbuf tm;
1661 tm.actime = dev_minor / 1000000;
1662 tm.modtime = dev_major / 1000000;
1663 EINTR_LOOP(r, times(path, &tm));
1664 break;
1665 #endif
1666 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "utime not supported");
1667 goto ret_false;
1669 default:
1670 internal(file_line, "os_dir_action: invalid action %d", action);
1673 if (!abs_path) {
1674 int e = errno;
1675 os_set_original_cwd();
1676 os_unlock_fork(true);
1677 errno = e;
1680 #ifdef HAVE_AT_FUNCTIONS
1681 proc_r:
1682 #endif
1683 if (unlikely(r == -1)) {
1684 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
1685 fatal_mayfail(e, err, "can't perform action %d on '%s': %s", action, path, error_decode(e));
1686 return false;
1688 return true;
1690 ret_false:
1691 if (!abs_path) {
1692 int e = errno;
1693 os_set_original_cwd();
1694 os_unlock_fork(true);
1695 errno = e;
1697 return false;
1700 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)
1702 bool ret;
1703 int r;
1704 char *dest_final_path = NULL;
1705 char *src_final_path = NULL;
1706 bool abs_dest_path = os_path_is_absolute(dest_path);
1707 bool abs_src_path = os_path_is_absolute(src_path);
1709 if (unlikely(!os_test_absolute_path(dest_dir, abs_dest_path, err)))
1710 return false;
1711 if (unlikely(!os_test_absolute_path(src_dir, abs_src_path, err)))
1712 return false;
1714 #ifdef HAVE_AT_FUNCTIONS
1715 if (likely(have_O_CLOEXEC_openat)) {
1716 if (!dir_handle_is_valid(dest_dir))
1717 dest_dir = AT_FDCWD;
1718 if (!dir_handle_is_valid(src_dir))
1719 src_dir = AT_FDCWD;
1720 switch (action) {
1721 case IO_Action_Mk_Link:
1722 EINTR_LOOP(r, linkat(src_dir, src_path, dest_dir, dest_path, 0));
1723 break;
1724 case IO_Action_Rename:
1725 EINTR_LOOP(r, renameat(src_dir, src_path, dest_dir, dest_path));
1726 break;
1727 default:
1728 internal(file_line, "os_dir2_action: invalid action %d", action);
1731 goto proc_r;
1733 #endif
1734 if (abs_dest_path) {
1735 dest_final_path = str_dup(dest_path, -1, err);
1736 if (unlikely(!dest_final_path)) {
1737 ret = false;
1738 goto free_ret;
1740 } else {
1741 char *dest_dir_path = os_dir_path(dest_dir, err);
1742 if (unlikely(!dest_dir_path)) {
1743 ret = false;
1744 goto free_ret;
1746 dest_final_path = os_join_paths(dest_dir_path, dest_path, true, err);
1747 if (unlikely(!dest_final_path)) {
1748 mem_free(dest_dir_path);
1749 ret = false;
1750 goto free_ret;
1752 mem_free(dest_dir_path);
1753 dest_dir_path = NULL;
1755 if (abs_src_path) {
1756 src_final_path = str_dup(src_path, -1, err);
1757 if (unlikely(!src_final_path)) {
1758 ret = false;
1759 goto free_ret;
1761 } else {
1762 char *src_dir_path = os_dir_path(src_dir, err);
1763 if (unlikely(!src_dir_path)) {
1764 ret = false;
1765 goto free_ret;
1767 src_final_path = os_join_paths(src_dir_path, src_path, true, err);
1768 if (unlikely(!src_final_path)) {
1769 mem_free(src_dir_path);
1770 ret = false;
1771 goto free_ret;
1773 mem_free(src_dir_path);
1774 src_dir_path = NULL;
1777 switch (action) {
1778 case IO_Action_Mk_Link:
1779 #ifdef HAVE_LINK
1780 EINTR_LOOP(r, link(src_final_path, dest_final_path));
1781 #else
1782 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "link not supported");
1783 ret = false;
1784 goto free_ret;
1785 #endif
1786 break;
1787 case IO_Action_Rename:
1788 EINTR_LOOP(r, rename(src_final_path, dest_final_path));
1789 break;
1790 default:
1791 internal(file_line, "os_dir2_action: invalid action %d", action);
1795 #ifdef HAVE_AT_FUNCTIONS
1796 proc_r:
1797 #endif
1798 if (unlikely(r == -1)) {
1799 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
1800 fatal_mayfail(e, err, "can't perform action %d on '%s' and '%s': %s", action, src_path, dest_path, error_decode(e));
1801 ret = false;
1802 goto free_ret;
1804 ret = true;
1806 free_ret:
1807 if (dest_final_path)
1808 mem_free(dest_final_path);
1809 if (src_final_path)
1810 mem_free(src_final_path);
1811 return ret;
1814 #if !defined(OS_DOS)
1816 bool os_drives(char **drives, size_t *drives_l, ajla_error_t *err)
1818 #if defined(OS_CYGWIN)
1819 uint32_t mask = GetLogicalDrives();
1820 return os_drives_bitmap(mask, drives, drives_l, err);
1821 #elif defined(HAVE_GETFSSTAT) || defined(HAVE_GETVFSFSSTAT)
1822 int r, i;
1823 int n_entries;
1824 #if defined(HAVE_GETVFSSTAT)
1825 struct statvfs *buf;
1826 #else
1827 struct statfs *buf;
1828 #endif
1830 n_entries = 2;
1831 again:
1832 #if defined(HAVE_GETVFSSTAT)
1833 buf = mem_alloc_array_mayfail(mem_alloc_mayfail, struct statvfs *, 0, 0, n_entries, sizeof(struct statvfs), err);
1834 #else
1835 buf = mem_alloc_array_mayfail(mem_alloc_mayfail, struct statfs *, 0, 0, n_entries, sizeof(struct statfs), err);
1836 #endif
1837 if (unlikely(!buf))
1838 return false;
1839 #if defined(HAVE_GETVFSSTAT)
1840 EINTR_LOOP(r, getvfsstat(buf, sizeof(struct statvfs) * n_entries, ST_NOWAIT));
1841 #else
1842 EINTR_LOOP(r, getfsstat(buf, sizeof(struct statfs) * n_entries, MNT_NOWAIT));
1843 #endif
1844 if (unlikely(r == -1)) {
1845 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
1846 fatal_mayfail(e, err, "getfsstat failed: %s", error_decode(e));
1847 mem_free(buf);
1848 return false;
1850 if (r >= n_entries) {
1851 mem_free(buf);
1852 n_entries *= 2U;
1853 if (unlikely(n_entries < 0)) {
1854 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_SIZE_OVERFLOW), err, "getfsstat buffer overflow");
1855 return false;
1857 goto again;
1860 if (unlikely(!array_init_mayfail(char, drives, drives_l, err))) {
1861 mem_free(buf);
1862 return false;
1865 for (i = 0; i < r; i++) {
1866 char *str;
1867 size_t str_l;
1868 if (buf[i].f_blocks <= 2)
1869 continue;
1870 str = buf[i].f_mntonname;
1871 str_l = strlen(str) + 1;
1872 if (unlikely(!array_add_multiple_mayfail(char, drives, drives_l, str, str_l, NULL, err))) {
1873 mem_free(buf);
1874 return false;
1878 mem_free(buf);
1879 return true;
1880 #else
1881 if (unlikely(!array_init_mayfail(char, drives, drives_l, err)))
1882 return false;
1883 return true;
1884 #endif
1887 #endif
1890 bool os_tcgetattr(handle_t h, os_termios_t *t, ajla_error_t *err)
1892 int r;
1893 EINTR_LOOP(r, tcgetattr(h, t));
1894 if (unlikely(r == -1)) {
1895 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
1896 fatal_mayfail(e, err, "tcgetattr failed: %s", error_decode(e));
1897 return false;
1899 return true;
1902 bool os_tcsetattr(handle_t h, const os_termios_t *t, ajla_error_t *err)
1904 int r;
1905 EINTR_LOOP(r, tcsetattr(h, TCSANOW, cast_ptr(os_termios_t *, t)));
1906 if (unlikely(r == -1)) {
1907 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
1908 fatal_mayfail(e, err, "tcsetattr failed: %s", error_decode(e));
1909 return false;
1911 return true;
1914 void os_tcflags(os_termios_t *t, int flags)
1916 if (flags & IO_Stty_Flag_Raw) {
1917 #ifdef HAVE_CFMAKERAW
1918 cfmakeraw(t);
1919 t->c_cc[VMIN] = 1;
1920 #else
1921 t->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
1922 t->c_oflag &= ~OPOST;
1923 t->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
1924 t->c_cflag &= ~(CSIZE|PARENB);
1925 t->c_cflag |= CS8;
1926 t->c_cc[VMIN] = 1;
1927 t->c_cc[VTIME] = 0;
1928 #endif
1930 if (flags & IO_Stty_Flag_Noecho)
1931 t->c_lflag &= ~ECHO;
1932 else
1933 t->c_lflag |= ECHO;
1934 if (flags & IO_Stty_Flag_Nosignal)
1935 t->c_lflag &= ~ISIG;
1936 else
1937 t->c_lflag |= ISIG;
1938 if (flags & IO_Stty_Flag_NoCRLF)
1939 t->c_oflag &= ~OPOST;
1940 else
1941 t->c_oflag |= OPOST;
1944 bool os_tty_size(handle_t h, int *nx, int *ny, int *ox, int *oy, ajla_error_t *err)
1946 int r;
1947 struct winsize ws;
1948 signal_seq_t attr_unused seq;
1950 EINTR_LOOP(r, ioctl(h, TIOCGWINSZ, &ws));
1951 if (unlikely(r == -1)) {
1952 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
1953 fatal_mayfail(e, err, "ioctl(TIOCGWINSZ) failed: %s", error_decode(e));
1954 return false;
1957 *nx = ws.ws_col;
1958 *ny = ws.ws_row;
1959 *ox = 0;
1960 *oy = 0;
1962 return true;
1966 static char *os_path_to_exe;
1968 static void os_init_path_to_exe(void)
1970 size_t i, sep;
1971 char *path, *component, *test_path;
1972 ajla_error_t sink;
1973 os_stat_t st;
1974 dir_handle_t dh;
1975 #ifdef __linux__
1976 char *ptexe = os_readlink(dir_none, "/proc/self/exe", &sink);
1977 if (likely(ptexe != NULL)) {
1978 if (likely(ptexe[0] == '/')) {
1979 sep = 0;
1980 for (i = 0; ptexe[i]; i++)
1981 if (unlikely(os_is_path_separator(ptexe[i])))
1982 sep = i + !i;
1983 ptexe[sep] = 0;
1984 os_path_to_exe = ptexe;
1985 return;
1987 mem_free(ptexe);
1989 #endif
1990 sep = 0;
1991 for (i = 0; arg0[i]; i++)
1992 if (unlikely(os_is_path_separator(arg0[i])))
1993 sep = i + 1;
1994 if (sep) {
1995 component = str_dup(arg0, sep, NULL);
1996 goto get_abs_path;
1999 path = getenv("PATH");
2000 if (!path) {
2001 component = str_dup(".", -1, NULL);
2002 goto get_abs_path;
2004 next_component:
2005 i = 0;
2006 while (path[i] && !os_is_env_separator(path[i]))
2007 i++;
2008 component = str_dup(path, i, NULL);
2009 test_path = os_join_paths(component, arg0, true, NULL);
2010 if (os_stat(os_cwd, test_path, false, &st, &sink)) {
2011 mem_free(test_path);
2012 goto get_abs_path;
2014 mem_free(test_path);
2015 mem_free(component);
2016 if (path[i]) {
2017 path += i + 1;
2018 goto next_component;
2020 warning("could not find executable in path");
2021 component = str_dup(".", -1, NULL);
2022 get_abs_path:
2023 if (os_path_is_absolute(component)) {
2024 os_path_to_exe = component;
2025 return;
2027 dh = os_dir_open(os_cwd, component, 0, NULL);
2028 os_path_to_exe = os_dir_path(dh, NULL);
2029 os_dir_close(dh);
2030 mem_free(component);
2033 const char *os_get_path_to_exe(void)
2035 return os_path_to_exe;
2039 ajla_time_t os_time_t_to_ajla_time(time_t sec)
2041 return (ajla_time_t)sec * 1000000;
2044 static ajla_time_t os_timeval_to_ajla_time(const struct timeval *tv)
2046 return os_time_t_to_ajla_time(tv->tv_sec) + tv->tv_usec;
2049 #ifdef HAVE_STRUCT_TIMESPEC
2050 ajla_time_t os_timespec_to_ajla_time(const struct timespec *ts)
2052 return os_time_t_to_ajla_time(ts->tv_sec) + ts->tv_nsec / 1000;
2054 #endif
2056 ajla_time_t os_time_real(void)
2058 int r;
2059 struct timeval tv;
2060 EINTR_LOOP(r, gettimeofday(&tv, NULL));
2061 if (unlikely(r == -1)) {
2062 int e = errno;
2063 fatal("gettimeofday failed: %d, %s", e, error_decode(error_from_errno(EC_SYSCALL, e)));
2065 return os_timeval_to_ajla_time(&tv);
2068 ajla_time_t os_time_monotonic(void)
2070 #if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_MONOTONIC)
2071 int r;
2072 struct timespec ts;
2073 EINTR_LOOP(r, clock_gettime(CLOCK_MONOTONIC, &ts));
2074 if (unlikely(r == -1)) {
2075 int e = errno;
2076 fatal("clock_gettime(%d) failed: %d, %s", (int)CLOCK_MONOTONIC, e, error_decode(error_from_errno(EC_SYSCALL, e)));
2078 return os_timespec_to_ajla_time(&ts);
2079 #else
2080 return os_time_real();
2081 #endif
2085 #if !defined(OS_DOS)
2087 static bool spawn_process_handles(unsigned n_handles, handle_t *src, int *target)
2089 int r;
2090 unsigned i;
2091 handle_t max_handle = 3;
2092 for (i = 0; i < n_handles; i++) {
2093 if (unlikely(src[i] >= signed_maximum(int) / 2) ||
2094 unlikely(target[i] >= signed_maximum(int) / 2))
2095 return false;
2096 if (src[i] >= max_handle) max_handle = src[i] + 1;
2097 if (target[i] >= max_handle) max_handle = target[i] + 1;
2099 for (i = 0; i < n_handles; i++) {
2100 EINTR_LOOP(r, dup2(src[i], max_handle + i));
2101 if (unlikely(r == -1))
2102 return false;
2103 /*os_close_handle(src[i]);*/
2105 for (i = 0; i < n_handles; i++) {
2106 EINTR_LOOP(r, close(src[i]));
2108 EINTR_LOOP(r, close(0));
2109 EINTR_LOOP(r, close(1));
2110 EINTR_LOOP(r, close(2));
2111 for (i = 0; i < n_handles; i++) {
2112 EINTR_LOOP(r, dup2(max_handle + i, target[i]));
2113 if (unlikely(r == -1))
2114 return false;
2115 os_close_handle(max_handle + i);
2117 for (i = 0; i < n_handles; i++) {
2118 EINTR_LOOP(r, fcntl(target[i], F_GETFL));
2119 if (likely(r >= 0) && r & O_NONBLOCK) {
2120 int ir;
2121 r &= ~O_NONBLOCK;
2122 EINTR_LOOP(ir, fcntl(target[i], F_SETFL, r));
2125 return true;
2128 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)
2130 #ifdef __linux__
2131 sig_state_t set;
2132 #endif
2133 pid_t p;
2134 int r;
2135 os_lock_fork(true);
2136 #ifdef __linux__
2137 os_block_signals(&set);
2138 #endif
2139 EINTR_LOOP(p, fork());
2140 #ifdef __linux__
2141 os_unblock_signals(&set);
2142 #endif
2143 if (!p) {
2144 ajla_error_t sink;
2145 if (unlikely(!os_set_cwd(wd, &sink)))
2146 _exit(127);
2147 if (unlikely(!spawn_process_handles(n_handles, src, target)))
2148 _exit(127);
2149 os_unblock_all_signals();
2150 EINTR_LOOP(r, execve(path, args, env));
2151 _exit(127);
2153 os_unlock_fork(true);
2154 if (p == -1) {
2155 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
2156 fatal_mayfail(e, err, "can't spawn process '%s': %s", path, error_decode(e));
2157 return false;
2159 *pid = p;
2160 return true;
2163 struct proc_handle {
2164 struct tree_entry entry;
2165 pid_t pid;
2166 bool fired;
2167 bool detached;
2168 int status;
2169 int sigchld;
2170 struct list wait_list;
2173 static struct tree proc_tree;
2174 static mutex_t proc_tree_mutex;
2176 static inline void proc_lock(void)
2178 mutex_lock(&proc_tree_mutex);
2181 static inline void proc_unlock(void)
2183 mutex_unlock(&proc_tree_mutex);
2186 static void proc_handle_free(struct proc_handle *ph)
2188 os_signal_unhandle(ph->sigchld);
2189 mem_free(ph);
2192 static int proc_handle_compare(const struct tree_entry *e, uintptr_t pid)
2194 const struct proc_handle *ph = get_struct(e, struct proc_handle, entry);
2195 if (unlikely(ph->pid == (pid_t)pid)) return 0;
2196 if (ph->pid > (pid_t)pid) return 1;
2197 return -1;
2200 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)
2202 struct proc_handle *ph;
2203 signal_seq_t seq;
2204 struct tree_insert_position ins;
2205 struct tree_entry *e;
2207 char **env;
2208 size_t env_l;
2210 if (unlikely(!array_init_mayfail(char *, &env, &env_l, err)))
2211 return NULL;
2212 while (*envc) {
2213 if (unlikely(!array_add_mayfail(char *, &env, &env_l, envc, NULL, err)))
2214 return NULL;
2215 envc = strchr(envc, 0) + 1;
2217 if (unlikely(!array_add_mayfail(char *, &env, &env_l, NULL, NULL, err)))
2218 return NULL;
2220 ph = mem_alloc_mayfail(struct proc_handle *, sizeof(struct proc_handle), err);
2221 if (unlikely(!ph)) {
2222 mem_free(env);
2223 return NULL;
2225 ph->fired = false;
2226 ph->detached = false;
2227 list_init(&ph->wait_list);
2228 ph->sigchld = os_signal_handle("SIGCHLD", &seq, err);
2229 if (unlikely(ph->sigchld < 0)) {
2230 mem_free(env);
2231 mem_free(ph);
2232 return NULL;
2234 if (unlikely(!ph->sigchld))
2235 iomux_enable_poll();
2237 proc_lock();
2239 if (unlikely(!os_fork(wd, path, n_handles, src, target, args, env, &ph->pid, err))) {
2240 proc_unlock();
2241 mem_free(env);
2242 proc_handle_free(ph);
2243 return NULL;
2246 e = tree_find_for_insert(&proc_tree, proc_handle_compare, ph->pid, &ins);
2247 if (unlikely(e != NULL)) {
2248 fatal("pid %ld is already present in the tree", (long)ph->pid);
2251 tree_insert_after_find(&ph->entry, &ins);
2253 proc_unlock();
2255 mem_free(env);
2257 return ph;
2260 void os_proc_free_handle(struct proc_handle *ph)
2262 proc_lock();
2263 ajla_assert_lo(list_is_empty(&ph->wait_list), (file_line, "os_proc_free_handle: freeing handle when there are processes waiting for it"));
2264 if (ph->fired) {
2265 proc_unlock();
2266 proc_handle_free(ph);
2267 } else {
2268 ph->detached = true;
2269 proc_unlock();
2273 bool os_proc_register_wait(struct proc_handle *ph, mutex_t **mutex_to_lock, struct list *list_entry, int *status)
2275 proc_lock();
2276 if (ph->fired) {
2277 *status = ph->status;
2278 proc_unlock();
2279 return true;
2280 } else {
2281 *mutex_to_lock = &proc_tree_mutex;
2282 list_add(&ph->wait_list, list_entry);
2283 proc_unlock();
2284 return false;
2288 static void process_pid_and_status(pid_t pid, int status)
2290 struct tree_entry *e;
2291 struct proc_handle *ph;
2293 proc_lock();
2295 e = tree_find(&proc_tree, proc_handle_compare, pid);
2296 if (!e) {
2297 proc_unlock();
2298 return;
2301 ph = get_struct(e, struct proc_handle, entry);
2302 ph->fired = true;
2304 if (WIFEXITED(status)) {
2305 ph->status = WEXITSTATUS(status);
2306 } else if (WIFSIGNALED(status)) {
2307 ph->status = -WTERMSIG(status);
2308 } else {
2309 proc_unlock();
2310 return;
2313 tree_delete(&ph->entry);
2315 if (!ph->detached) {
2316 call(wake_up_wait_list)(&ph->wait_list, &proc_tree_mutex, true);
2317 } else {
2318 proc_handle_free(ph);
2319 proc_unlock();
2323 static attr_noinline void proc_check_owned(void)
2325 struct tree_entry *e;
2326 struct proc_handle *ph;
2327 pid_t pid = 0;
2328 pid_t r;
2329 int status;
2331 next:
2332 proc_lock();
2333 e = tree_find_next(&proc_tree, proc_handle_compare, pid);
2334 if (likely(!e)) {
2335 proc_unlock();
2336 return;
2338 ph = get_struct(e, struct proc_handle, entry);
2339 pid = ph->pid;
2340 proc_unlock();
2342 EINTR_LOOP(r, waitpid(pid, &status, WNOHANG));
2343 if (r <= 0)
2344 goto next;
2346 process_pid_and_status(pid, status);
2347 goto next;
2350 void os_proc_check_all(void)
2352 pid_t pid;
2353 int status;
2355 proc_lock();
2356 if (likely(tree_is_empty(&proc_tree))) {
2357 proc_unlock();
2358 return;
2360 proc_unlock();
2362 if (!dll) {
2363 test_another:
2364 EINTR_LOOP(pid, waitpid(-1, &status, WNOHANG));
2365 if (unlikely(pid > 0)) {
2366 process_pid_and_status(pid, status);
2367 goto test_another;
2369 } else {
2370 proc_check_owned();
2374 #endif
2377 #ifdef OS_HAS_SIGNALS
2379 #if defined(SIGRTMAX)
2380 #define N_SIGNALS (int)(SIGRTMAX + 1)
2381 #elif defined(NSIG)
2382 #define N_SIGNALS (int)NSIG
2383 #else
2384 #define N_SIGNALS 32
2385 #endif
2387 struct signal_state {
2388 thread_volatile signal_seq_t sig_sequence;
2389 signal_seq_t last_sig_sequence;
2390 bool trapped;
2391 uintptr_t refcount;
2392 struct list wait_list;
2393 struct sigaction prev_sa;
2396 static struct signal_state *signal_states;
2397 static mutex_t signal_state_mutex;
2399 static void signal_handler(int sig)
2401 signal_states[sig].sig_sequence += 1UL;
2402 os_notify();
2405 static int os_signal_number(const char *str)
2407 #ifdef SIGABRT
2408 if (!strcmp(str, "SIGABRT")) return SIGABRT;
2409 #endif
2410 #ifdef SIGALRM
2411 if (!strcmp(str, "SIGALRM")) return SIGALRM;
2412 #endif
2413 #ifdef SIGBUS
2414 if (!strcmp(str, "SIGBUS")) return SIGBUS;
2415 #endif
2416 #ifdef SIGCHLD
2417 if (!strcmp(str, "SIGCHLD")) return SIGCHLD;
2418 #endif
2419 #ifdef SIGCLD
2420 if (!strcmp(str, "SIGCLD")) return SIGCLD;
2421 #endif
2422 #ifdef SIGCONT
2423 if (!strcmp(str, "SIGCONT")) return SIGCONT;
2424 #endif
2425 #ifdef SIGEMT
2426 if (!strcmp(str, "SIGEMT")) return SIGEMT;
2427 #endif
2428 #ifdef SIGFPE
2429 if (!strcmp(str, "SIGFPE")) return SIGFPE;
2430 #endif
2431 #ifdef SIGHUP
2432 if (!strcmp(str, "SIGHUP")) return SIGHUP;
2433 #endif
2434 #ifdef SIGILL
2435 if (!strcmp(str, "SIGILL")) return SIGILL;
2436 #endif
2437 #ifdef SIGINFO
2438 if (!strcmp(str, "SIGINFO")) return SIGINFO;
2439 #endif
2440 #ifdef SIGINT
2441 if (!strcmp(str, "SIGINT")) return SIGINT;
2442 #endif
2443 #ifdef SIGIO
2444 if (!strcmp(str, "SIGIO")) return SIGIO;
2445 #endif
2446 #ifdef SIGIOT
2447 if (!strcmp(str, "SIGIOT")) return SIGIOT;
2448 #endif
2449 #ifdef SIGKILL
2450 if (!strcmp(str, "SIGKILL")) return SIGKILL;
2451 #endif
2452 #ifdef SIGLOST
2453 if (!strcmp(str, "SIGLOST")) return SIGLOST;
2454 #endif
2455 #ifdef SIGPIPE
2456 if (!strcmp(str, "SIGPIPE")) return SIGPIPE;
2457 #endif
2458 #ifdef SIGPOLL
2459 if (!strcmp(str, "SIGPOLL")) return SIGPOLL;
2460 #endif
2461 #ifdef SIGPROF
2462 if (!strcmp(str, "SIGPROF")) return SIGPROF;
2463 #endif
2464 #ifdef SIGPWR
2465 if (!strcmp(str, "SIGPWR")) return SIGPWR;
2466 #endif
2467 #ifdef SIGQUIT
2468 if (!strcmp(str, "SIGQUIT")) return SIGQUIT;
2469 #endif
2470 #ifdef SIGSEGV
2471 if (!strcmp(str, "SIGSEGV")) return SIGSEGV;
2472 #endif
2473 #ifdef SIGSTKFLT
2474 if (!strcmp(str, "SIGSTKFLT")) return SIGSTKFLT;
2475 #endif
2476 #ifdef SIGSTOP
2477 if (!strcmp(str, "SIGSTOP")) return SIGSTOP;
2478 #endif
2479 #ifdef SIGTSTP
2480 if (!strcmp(str, "SIGTSTP")) return SIGTSTP;
2481 #endif
2482 #ifdef SIGSYS
2483 if (!strcmp(str, "SIGSYS")) return SIGSYS;
2484 #endif
2485 #ifdef SIGTERM
2486 if (!strcmp(str, "SIGTERM")) return SIGTERM;
2487 #endif
2488 #ifdef SIGTRAP
2489 if (!strcmp(str, "SIGTRAP")) return SIGTRAP;
2490 #endif
2491 #ifdef SIGTTIN
2492 if (!strcmp(str, "SIGTTIN")) return SIGTTIN;
2493 #endif
2494 #ifdef SIGTTOU
2495 if (!strcmp(str, "SIGTTOU")) return SIGTTOU;
2496 #endif
2497 #ifdef SIGUNUSED
2498 if (!strcmp(str, "SIGUNUSED")) return SIGUNUSED;
2499 #endif
2500 #ifdef SIGURG
2501 if (!strcmp(str, "SIGURG")) return SIGURG;
2502 #endif
2503 #ifdef SIGUSR1
2504 if (!strcmp(str, "SIGUSR1")) return SIGUSR1;
2505 #endif
2506 #ifdef SIGUSR2
2507 if (!strcmp(str, "SIGUSR2")) return SIGUSR2;
2508 #endif
2509 #ifdef SIGVTALRM
2510 if (!strcmp(str, "SIGVTALRM")) return SIGVTALRM;
2511 #endif
2512 #ifdef SIGWINCH
2513 if (!strcmp(str, "SIGWINCH")) return SIGWINCH;
2514 #endif
2515 #ifdef SIGXCPU
2516 if (!strcmp(str, "SIGXCPU")) return SIGXCPU;
2517 #endif
2518 #ifdef SIGXFSZ
2519 if (!strcmp(str, "SIGXFSZ")) return SIGXFSZ;
2520 #endif
2521 #if defined(SIGRTMIN) && defined(SIGRTMAX)
2522 if (!strncmp(str, "SIGRT", 5) && str[5]) {
2523 char *endptr;
2524 unsigned long num = strtoul(str + 5, &endptr, 10);
2525 if (unlikely(*endptr))
2526 return 0;
2527 num += SIGRTMIN;
2528 if (unlikely(num < (unsigned long)SIGRTMIN) || unlikely(num > (unsigned long)SIGRTMAX))
2529 return 0;
2530 return num;
2532 #endif
2533 return 0;
2536 int os_signal_handle(const char *str, signal_seq_t *seq, ajla_error_t *err)
2538 struct signal_state *s;
2539 int sig = os_signal_number(str);
2540 if (unlikely(!sig)) {
2541 *seq = 0;
2542 return 0;
2544 mutex_lock(&signal_state_mutex);
2545 s = &signal_states[sig];
2546 if (unlikely(s->trapped)) {
2547 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "signal %s already handled", str);
2548 goto unlock_err;
2550 if (likely(!s->refcount)) {
2551 struct sigaction sa;
2552 int r;
2554 s->sig_sequence = 0;
2555 s->last_sig_sequence = 0;
2557 (void)memset(&sa, 0, sizeof sa);
2558 sa.sa_handler = signal_handler;
2559 sigemptyset(&sa.sa_mask);
2560 #ifdef SA_RESTART
2561 if (sig != SIGTTIN && sig != SIGTTOU)
2562 sa.sa_flags |= SA_RESTART;
2563 #endif
2564 EINTR_LOOP(r, sigaction(sig, &sa, &s->prev_sa));
2565 if (unlikely(r == -1)) {
2566 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
2567 fatal_mayfail(e, err, "sigaction(%d) failed: %s", sig, error_decode(e));
2568 goto unlock_err;
2571 s->refcount++;
2572 *seq = s->last_sig_sequence;
2573 mutex_unlock(&signal_state_mutex);
2574 return sig;
2576 unlock_err:
2577 mutex_unlock(&signal_state_mutex);
2578 return -1;
2581 static void os_signal_restore(struct signal_state *s, int sig)
2583 int r;
2584 EINTR_LOOP(r, sigaction(sig, &s->prev_sa, NULL));
2585 if (unlikely(r == -1)) {
2586 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
2587 fatal("sigaction(%d) failed: %s", sig, error_decode(e));
2591 void os_signal_unhandle(int sig)
2593 struct signal_state *s;
2594 if (unlikely(!sig))
2595 return;
2596 mutex_lock(&signal_state_mutex);
2597 s = &signal_states[sig];
2598 if (!s->refcount)
2599 internal(file_line, "os_signal_unhandle: refcount underflow");
2600 s->refcount--;
2601 if (!s->refcount)
2602 os_signal_restore(s, sig);
2603 mutex_unlock(&signal_state_mutex);
2606 signal_seq_t os_signal_seq(int sig)
2608 struct signal_state *s;
2609 signal_seq_t seq;
2610 if (unlikely(!sig))
2611 return 0;
2612 mutex_lock(&signal_state_mutex);
2613 s = &signal_states[sig];
2614 if (unlikely(!s))
2615 internal(file_line, "os_signal_unhandle: unhandled signal");
2616 seq = s->last_sig_sequence;
2617 mutex_unlock(&signal_state_mutex);
2618 return seq;
2621 bool os_signal_wait(int sig, signal_seq_t seq, mutex_t **mutex_to_lock, struct list *list_entry)
2623 struct signal_state *s;
2625 if (unlikely(!sig)) {
2626 iomux_never(mutex_to_lock, list_entry);
2627 return true;
2630 mutex_lock(&signal_state_mutex);
2631 s = &signal_states[sig];
2632 if (unlikely(seq != s->last_sig_sequence)) {
2633 mutex_unlock(&signal_state_mutex);
2634 return false;
2636 *mutex_to_lock = &signal_state_mutex;
2637 list_add(&s->wait_list, list_entry);
2638 mutex_unlock(&signal_state_mutex);
2640 return true;
2643 void os_signal_check_all(void)
2645 int sig = 0;
2646 again:
2647 mutex_lock(&signal_state_mutex);
2648 for (; sig < N_SIGNALS; sig++) {
2649 struct signal_state *s = &signal_states[sig];
2650 signal_seq_t seq = s->sig_sequence;
2651 if (unlikely(seq != s->last_sig_sequence)) {
2652 s->last_sig_sequence = seq;
2653 call(wake_up_wait_list)(&s->wait_list, &signal_state_mutex, true);
2654 sig++;
2655 goto again;
2658 mutex_unlock(&signal_state_mutex);
2661 #ifdef HAVE_CODEGEN_TRAPS
2663 void *u_data_trap_lookup(void *ptr);
2664 void *c_data_trap_lookup(void *ptr);
2666 static void sigfpe_handler(int attr_unused sig, siginfo_t *siginfo, void *ucontext)
2668 ucontext_t *uc = ucontext;
2669 #if defined(ARCH_ALPHA)
2670 if (unlikely(siginfo->si_code != FPE_FLTINV))
2671 fatal("unexpected SIGFPE received: %d", siginfo->si_code);
2672 /*debug("bla: %lx, %lx, %lx", uc->uc_mcontext.sc_regs[0x7], uc->uc_mcontext.sc_regs[0xf], uc->uc_mcontext.sc_regs[0x10]);*/
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 /*debug("bla: %llx, %llx, %llx", uc->uc_mcontext.gregs[0x4], uc->uc_mcontext.gregs[0x17], uc->uc_mcontext.gregs[0x16]);*/
2679 uc->uc_mcontext.pc = ptr_to_num(call(data_trap_lookup)(num_to_ptr(uc->uc_mcontext.pc)));
2680 #endif
2683 #endif
2685 #ifdef SA_SIGINFO
2686 void os_signal_trap(int sig, void (*handler)(int, siginfo_t *, void *))
2688 if (OS_SUPPORTS_TRAPS) {
2689 struct signal_state *s = &signal_states[sig];
2690 struct sigaction sa;
2691 int r;
2693 s->trapped = true;
2695 (void)memset(&sa, 0, sizeof sa);
2696 sa.sa_sigaction = handler;
2697 sigemptyset(&sa.sa_mask);
2698 sa.sa_flags |= SA_SIGINFO;
2699 EINTR_LOOP(r, sigaction(sig, &sa, &s->prev_sa));
2700 if (unlikely(r == -1)) {
2701 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
2702 fatal("sigaction(%d) failed: %s", sig, error_decode(e));
2706 void os_signal_untrap(int sig)
2708 if (OS_SUPPORTS_TRAPS) {
2709 struct signal_state *s = &signal_states[sig];
2710 ajla_assert_lo(s->trapped, (file_line, "os_signal_untrap: signal %d not trapped", sig));
2711 os_signal_restore(s, sig);
2712 s->trapped = false;
2715 #endif
2717 #else
2719 int os_signal_handle(const char attr_unused *str, signal_seq_t attr_unused *seq, ajla_error_t attr_unused *err)
2721 *seq = NULL;
2722 return 0;
2725 void os_signal_unhandle(int attr_unused sig)
2729 signal_seq_t os_signal_seq(int attr_unused sig)
2731 return 0;
2734 bool os_signal_wait(int attr_unused sig, signal_seq_t attr_unused seq, mutex_t **mutex_to_lock, struct list *list_entry)
2736 iomux_never(mutex_to_lock, list_entry);
2737 return true;
2740 void os_signal_check_all(void)
2744 #endif
2747 #ifdef HAVE_NETWORK
2749 handle_t os_socket(int domain, int type, int protocol, ajla_error_t *err)
2751 int r, h;
2752 domain = os_socket_pf(domain, err);
2753 if (unlikely(domain == -1))
2754 return -1;
2755 type = os_socket_type(type, err);
2756 if (unlikely(type == -1))
2757 return -1;
2758 #if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC)
2759 EINTR_LOOP(h, socket(domain, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol));
2760 if (likely(h != -1)) {
2761 obj_registry_insert(OBJ_TYPE_HANDLE, h, file_line);
2762 return h;
2764 if (errno != EINVAL) {
2765 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
2766 fatal_mayfail(e, err, "can't create socket (%d, %d, %d): %s", domain, type, protocol, error_decode(e));
2767 return -1;
2769 #endif
2770 os_lock_fork(false);
2771 EINTR_LOOP(h, socket(domain, type, protocol));
2772 if (unlikely(h == -1)) {
2773 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
2774 os_unlock_fork(false);
2775 fatal_mayfail(e, err, "can't create socket (%d, %d, %d): %s", domain, type, protocol, error_decode(e));
2776 return -1;
2778 os_set_cloexec(h);
2779 os_unlock_fork(false);
2780 obj_registry_insert(OBJ_TYPE_HANDLE, h, file_line);
2781 EINTR_LOOP(r, fcntl(h, F_SETFL, O_NONBLOCK));
2782 if (unlikely(r == -1)) {
2783 int er = errno;
2784 fatal("fcntl(F_SETFL, O_NONBLOCK) on a socket failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
2786 return h;
2789 bool os_bind_connect(bool bnd, handle_t h, unsigned char *addr, size_t addr_len, ajla_error_t *err)
2791 int r;
2792 struct sockaddr *sa;
2793 ajla_error_t e;
2794 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
2795 sa = os_get_sock_addr(addr, &addr_len, err);
2796 if (unlikely(!sa))
2797 return false;
2798 if (likely(!bnd))
2799 EINTR_LOOP(r, connect(h, sa, addr_len));
2800 else
2801 EINTR_LOOP(r, bind(h, sa, addr_len));
2802 mem_free_aligned(sa);
2803 if (unlikely(!r))
2804 return true;
2805 if (likely(!bnd) && likely(errno == EINPROGRESS))
2806 return true;
2807 e = error_from_errno(EC_SYSCALL, errno);
2808 fatal_mayfail(e, err, "can't %s socket: %s", !bnd ? "connect" : "bind", error_decode(e));
2809 return false;
2812 bool os_connect_completed(handle_t h, ajla_error_t *err)
2814 ajla_error_t e;
2815 int r;
2816 int er;
2817 socklen_t er_l;
2818 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
2819 er_l = sizeof er;
2820 EINTR_LOOP(r, getsockopt(h, SOL_SOCKET, SO_ERROR, &er, &er_l));
2821 if (unlikely(r == -1)) {
2822 e = error_from_errno(EC_SYSCALL, errno);
2823 fatal_mayfail(e, err, "getsockopt returned an error: %s", error_decode(e));
2824 return false;
2826 if (unlikely(er)) {
2827 e = error_from_errno(EC_SYSCALL, er);
2828 fatal_mayfail(e, err, "can't connect socket: %s", error_decode(e));
2829 return false;
2831 return true;
2834 bool os_listen(handle_t h, ajla_error_t *err)
2836 int r;
2837 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
2838 EINTR_LOOP(r, listen(h, signed_maximum(int)));
2839 if (unlikely(r == -1)) {
2840 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
2841 fatal_mayfail(e, err, "listen returned an error: %s", error_decode(e));
2842 return false;
2844 return true;
2847 int os_accept(handle_t h, handle_t *result, ajla_error_t *err)
2849 int r;
2850 ajla_error_t e;
2851 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
2852 #ifdef HAVE_ACCEPT4
2853 EINTR_LOOP(r, accept4(h, NULL, 0, SOCK_NONBLOCK | SOCK_CLOEXEC));
2854 if (likely(r != -1)) {
2855 *result = r;
2856 obj_registry_insert(OBJ_TYPE_HANDLE, r, file_line);
2857 return 0;
2859 if (errno == EAGAIN || errno == EWOULDBLOCK)
2860 return OS_RW_WOULDBLOCK;
2861 if (errno != ENOSYS) {
2862 e = error_from_errno(EC_SYSCALL, errno);
2863 fatal_mayfail(e, err, "accept returned an error: %s", error_decode(e));
2864 return OS_RW_ERROR;
2866 #endif
2867 os_lock_fork(false);
2868 EINTR_LOOP(r, accept(h, NULL, 0));
2869 if (unlikely(r == -1)) {
2870 os_unlock_fork(false);
2871 if (errno == EAGAIN || errno == EWOULDBLOCK)
2872 return OS_RW_WOULDBLOCK;
2873 e = error_from_errno(EC_SYSCALL, errno);
2874 fatal_mayfail(e, err, "accept returned an error: %s", error_decode(e));
2875 return OS_RW_ERROR;
2877 os_set_cloexec(r);
2878 os_unlock_fork(false);
2879 *result = r;
2880 obj_registry_insert(OBJ_TYPE_HANDLE, r, file_line);
2881 EINTR_LOOP(r, fcntl(r, F_SETFL, O_NONBLOCK));
2882 if (unlikely(r == -1)) {
2883 int er = errno;
2884 fatal("fcntl(F_SETFL, O_NONBLOCK) on a socket failed: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
2886 return 0;
2889 bool os_getsockpeername(bool peer, handle_t h, unsigned char **addr, size_t *addr_len, ajla_error_t *err)
2891 int r;
2892 struct sockaddr *sa;
2893 socklen_t addrlen;
2895 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
2897 sa = mem_align_mayfail(struct sockaddr *, SOCKADDR_MAX_LEN, SOCKADDR_ALIGN, err);
2898 if (unlikely(!sa))
2899 return false;
2900 addrlen = SOCKADDR_MAX_LEN;
2902 if (!peer) {
2903 EINTR_LOOP(r, getsockname(h, sa, &addrlen));
2904 } else {
2905 #ifdef HAVE_GETPEERNAME
2906 EINTR_LOOP(r, getpeername(h, sa, &addrlen));
2907 #else
2908 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "getpeername not supported");
2909 goto free_ret_false;
2910 #endif
2912 if (unlikely(r == -1)) {
2913 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
2914 fatal_mayfail(e, err, "%s returned an error: %s", !peer ? "getsockname" : "getpeername", error_decode(e));
2915 goto free_ret_false;
2918 *addr = os_get_ajla_addr(sa, &addrlen, err);
2919 if (unlikely(!*addr))
2920 goto free_ret_false;
2922 *addr_len = addrlen;
2924 mem_free_aligned(sa);
2925 return true;
2927 free_ret_false:
2928 mem_free_aligned(sa);
2929 return false;
2932 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)
2934 struct sockaddr *sa;
2935 socklen_t addrlen;
2936 ssize_t r;
2937 int f;
2939 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
2941 f = translate_flags(os_socket_msg, flags, err);
2942 if (unlikely(f < 0))
2943 return OS_RW_ERROR;
2945 sa = mem_align_mayfail(struct sockaddr *, SOCKADDR_MAX_LEN, SOCKADDR_ALIGN, err);
2946 if (unlikely(!sa))
2947 return OS_RW_ERROR;
2948 addrlen = SOCKADDR_MAX_LEN;
2950 EINTR_LOOP(r, recvfrom(h, buffer, len, f, sa, &addrlen));
2952 if (r >= 0) {
2953 if (unlikely(addrlen > SOCKADDR_MAX_LEN)) {
2954 fatal_mayfail(error_ajla(EC_SYSCALL, AJLA_ERROR_SIZE_OVERFLOW), err, "the system returned too long address");
2955 mem_free_aligned(sa);
2956 return OS_RW_ERROR;
2958 if (!addrlen) {
2959 if (unlikely(!array_init_mayfail(unsigned char, addr, addr_len, err))) {
2960 mem_free_aligned(sa);
2961 return OS_RW_ERROR;
2963 } else {
2964 *addr = os_get_ajla_addr(sa, &addrlen, err);
2965 if (unlikely(!*addr)) {
2966 mem_free_aligned(sa);
2967 return OS_RW_ERROR;
2969 *addr_len = addrlen;
2972 mem_free_aligned(sa);
2973 return os_rdwr_return(r, "receiving", err);
2976 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)
2978 struct sockaddr *sa;
2979 ssize_t r;
2980 int f;
2982 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
2984 f = translate_flags(os_socket_msg, flags, err);
2985 if (unlikely(f < 0))
2986 return OS_RW_ERROR;
2988 if (addr_len != 0) {
2989 sa = os_get_sock_addr(addr, &addr_len, err);
2990 if (unlikely(!sa))
2991 return OS_RW_ERROR;
2992 EINTR_LOOP(r, sendto(h, buffer, len, f, sa, addr_len));
2993 mem_free_aligned(sa);
2994 } else {
2995 EINTR_LOOP(r, send(h, buffer, len, f));
2998 return os_rdwr_return(r, "sending", err);
3001 bool os_getsockopt(handle_t h, int level, int option, char **buffer, size_t *buffer_len, ajla_error_t *err)
3003 int r;
3004 socklen_t opt_len;
3006 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
3008 level = os_socket_level(level, err);
3009 if (unlikely(level < 0))
3010 return false;
3012 option = os_socket_option(option, err);
3013 if (unlikely(level < 0))
3014 return false;
3016 opt_len = 4096;
3017 *buffer = mem_alloc_mayfail(char *, opt_len, err);
3018 if (unlikely(!*buffer))
3019 return false;
3021 EINTR_LOOP(r, getsockopt(h, level, option, *buffer, &opt_len));
3023 if (unlikely(r == -1)) {
3024 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
3025 fatal_mayfail(e, err, "getsockopt returned an error: %s", error_decode(e));
3026 mem_free(*buffer);
3027 return false;
3030 *buffer_len = opt_len;
3031 return true;
3034 bool os_setsockopt(handle_t h, int level, int option, const char *buffer, size_t buffer_len, ajla_error_t *err)
3036 int r;
3038 obj_registry_verify(OBJ_TYPE_HANDLE, h, file_line);
3040 level = os_socket_level(level, err);
3041 if (unlikely(level < 0))
3042 return false;
3044 option = os_socket_option(option, err);
3045 if (unlikely(level < 0))
3046 return false;
3048 EINTR_LOOP(r, setsockopt(h, level, option, buffer, buffer_len));
3050 if (unlikely(r == -1)) {
3051 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
3052 fatal_mayfail(e, err, "setsockopt returned an error: %s", error_decode(e));
3053 return false;
3056 return true;
3059 #ifdef HAVE_GETADDRINFO
3060 bool os_getaddrinfo(const char *host, int port, struct address **result, size_t *result_l, ajla_error_t *err)
3062 char port_str[6];
3063 int r;
3064 size_t i;
3065 struct addrinfo *res = NULL, *rs;
3067 if (unlikely(!array_init_mayfail(struct address, result, result_l, err)))
3068 return false;
3070 snprintf(port_str, sizeof port_str, "%d", port);
3071 r = getaddrinfo(host, port_str, NULL, &res);
3072 if (unlikely(r)) {
3073 if (unlikely(r == EAI_SYSTEM))
3074 fatal_mayfail(error_from_errno(EC_SYSCALL, errno), err, "host not found");
3075 else
3076 fatal_mayfail(error_ajla_aux(EC_SYSCALL, AJLA_ERROR_GAI, abs((int)r)), err, "host not found");
3077 goto fail;
3080 for (rs = res; rs; rs = rs->ai_next) {
3081 void *xresult;
3082 struct address addr;
3083 ajla_error_t e;
3084 socklen_t addrlen = rs->ai_addrlen;
3086 memset(&addr.entry, 0, sizeof addr.entry); /* avoid warning */
3088 addr.address = os_get_ajla_addr(rs->ai_addr, &addrlen, &e);
3089 if (unlikely(!addr.address))
3090 continue;
3091 addr.address_length = addrlen;
3093 if (unlikely(!array_add_mayfail(struct address, result, result_l, addr, &xresult, err))) {
3094 *result = xresult;
3095 goto fail;
3099 if (unlikely(!*result_l)) {
3100 fatal_mayfail(error_ajla_aux(EC_SYSCALL, AJLA_ERROR_GAI, abs(EAI_NONAME)), err, "host not found");
3101 goto fail;
3104 freeaddrinfo(res);
3105 return true;
3107 fail:
3108 if (res)
3109 freeaddrinfo(res);
3110 for (i = 0; i < *result_l; i++)
3111 mem_free((*result)[i].address);
3112 mem_free(*result);
3113 return false;
3115 #else
3116 #ifndef NO_DATA
3117 #define NO_DATA 1
3118 #endif
3119 #ifndef HAVE_H_ERRNO
3120 #define h_errno 1
3121 #endif
3122 bool os_getaddrinfo(const char *host, int port, struct address **result, size_t *result_l, ajla_error_t *err)
3124 struct hostent *he;
3125 size_t i;
3126 void *xresult;
3127 char *a;
3129 if (unlikely(!array_init_mayfail(struct address, result, result_l, err)))
3130 return false;
3132 he = gethostbyname(host);
3134 if (unlikely(!he)) {
3135 fatal_mayfail(error_ajla_aux(EC_SYSCALL, AJLA_ERROR_H_ERRNO, h_errno), err, "host not found");
3136 goto fail;
3139 if (he->h_addrtype != AF_INET || he->h_length != 4 || !he->h_addr) {
3140 fatal_mayfail(error_ajla_aux(EC_SYSCALL, AJLA_ERROR_H_ERRNO, NO_DATA), err, "host not found");
3141 goto fail;
3144 #ifdef h_addr
3145 for (i = 0; (a = he->h_addr_list[i]); i++)
3146 #else
3147 a = he->h_addr;
3148 #endif
3150 struct sockaddr_in sin;
3151 struct sockaddr sa;
3152 struct address addr;
3153 ajla_error_t e;
3154 socklen_t addrlen = sizeof sin;
3156 sin.sin_family = AF_INET;
3157 sin.sin_port = htons(port);
3158 memcpy(&sin.sin_addr, a, 4);
3160 memcpy(&sa, &sin, sizeof sin);
3162 addr.address = os_get_ajla_addr(&sa, &addrlen, &e);
3163 if (unlikely(!addr.address))
3164 continue;
3165 addr.address_length = addrlen;
3167 if (unlikely(!array_add_mayfail(struct address, result, result_l, addr, &xresult, err))) {
3168 *result = xresult;
3169 goto fail;
3173 if (unlikely(!*result_l)) {
3174 fatal_mayfail(error_ajla_aux(EC_SYSCALL, AJLA_ERROR_H_ERRNO, NO_DATA), err, "host not found");
3175 goto fail;
3178 return true;
3180 fail:
3181 for (i = 0; i < *result_l; i++)
3182 mem_free((*result)[i].address);
3183 mem_free(*result);
3184 return false;
3186 #endif
3188 #ifdef HAVE_GETNAMEINFO
3189 char *os_getnameinfo(unsigned char *addr, size_t addr_len, ajla_error_t *err)
3191 struct sockaddr *sa;
3192 int r;
3193 char *h;
3194 size_t h_len;
3195 sa = os_get_sock_addr(addr, &addr_len, err);
3196 if (unlikely(!sa))
3197 return NULL;
3198 #ifdef EAI_OVERFLOW
3199 h_len = 64;
3200 alloc_buffer_again:
3201 #else
3202 h_len = NI_MAXHOST;
3203 #endif
3204 h = mem_alloc_mayfail(char *, h_len, err);
3205 if (unlikely(!h)) {
3206 mem_free_aligned(sa);
3207 return NULL;
3209 r = getnameinfo(sa, addr_len, h, h_len, NULL, 0, 0);
3210 if (unlikely(r)) {
3211 #ifdef EAI_OVERFLOW
3212 if (unlikely(r == EAI_OVERFLOW)) {
3213 mem_free(h);
3214 h_len *= 2;
3215 if (unlikely(!h_len)) {
3216 fatal_mayfail(error_ajla(EC_SYSCALL, AJLA_ERROR_SIZE_OVERFLOW), err, "name buffer overflow");
3217 mem_free_aligned(sa);
3218 return NULL;
3220 goto alloc_buffer_again;
3222 #endif
3223 if (unlikely(r == EAI_SYSTEM)) {
3224 fatal_mayfail(error_from_errno(EC_SYSCALL, errno), err, "host not found");
3225 } else {
3226 fatal_mayfail(error_ajla_aux(EC_SYSCALL, AJLA_ERROR_GAI, abs((int)r)), err, "host not found");
3228 mem_free(h);
3229 mem_free_aligned(sa);
3230 return NULL;
3232 mem_free_aligned(sa);
3233 return h;
3235 #elif defined(HAVE_GETHOSTBYADDR)
3236 char *os_getnameinfo(unsigned char *addr, size_t addr_len, ajla_error_t *err)
3238 struct sockaddr *sa;
3239 struct hostent *he;
3240 char *name;
3241 size_t le;
3242 sa = os_get_sock_addr(addr, &addr_len, err);
3243 if (unlikely(!sa))
3244 return NULL;
3245 switch (sa->sa_family) {
3246 case AF_INET: {
3247 struct sockaddr_in *sin;
3248 if (unlikely(addr_len < offsetof(struct sockaddr_in, sin_addr) + 4)) {
3249 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "too short address");
3250 mem_free_aligned(sa);
3251 return NULL;
3253 sin = cast_ptr(struct sockaddr_in *, sa);
3254 he = gethostbyaddr(cast_ptr(void *, &sin->sin_addr.s_addr), 4, sa->sa_family);
3255 break;
3257 #ifdef AF_INET6
3258 case AF_INET6: {
3259 struct sockaddr_in6 *sin6;
3260 if (unlikely(addr_len < offsetof(struct sockaddr_in6, sin6_addr) + 16)) {
3261 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_INVALID_OPERATION), err, "too short address");
3262 mem_free_aligned(sa);
3263 return NULL;
3265 sin6 = cast_ptr(struct sockaddr_in6 *, sa);
3266 he = gethostbyaddr(&sin6->sin6_addr.s6_addr, 16, sa->sa_family);
3267 break;
3269 #endif
3270 default: {
3271 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "address family %d not supported", sa->sa_family);
3272 mem_free_aligned(sa);
3273 return NULL;
3276 mem_free_aligned(sa);
3277 if (unlikely(!he) || !he->h_name) {
3278 fatal_mayfail(error_ajla_aux(EC_SYSCALL, AJLA_ERROR_H_ERRNO, h_errno), err, "host not found");
3279 return NULL;
3281 le = strlen(he->h_name);
3282 name = mem_alloc_mayfail(char *, le + 1, err);
3283 if (unlikely(!name))
3284 return NULL;
3285 return memcpy(name, he->h_name, le + 1);
3287 #else
3288 char *os_getnameinfo(unsigned char attr_unused *addr, size_t attr_unused addr_len, ajla_error_t *err)
3290 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "getnameinfo not supported");
3291 return NULL;
3293 #endif
3295 #else
3297 handle_t os_socket(int attr_unused domain, int attr_unused type, int attr_unused protocol, ajla_error_t *err)
3299 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "sockets not supported");
3300 return -1;
3303 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)
3305 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "sockets not supported");
3306 return false;
3309 bool os_connect_completed(handle_t attr_unused h, ajla_error_t *err)
3311 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "sockets not supported");
3312 return false;
3315 bool os_listen(handle_t attr_unused h, ajla_error_t *err)
3317 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "sockets not supported");
3318 return false;
3321 int os_accept(handle_t attr_unused h, handle_t attr_unused *result, ajla_error_t *err)
3323 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "sockets not supported");
3324 return OS_RW_ERROR;
3327 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)
3329 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "sockets not supported");
3330 return false;
3333 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)
3335 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "sockets not supported");
3336 return OS_RW_ERROR;
3339 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)
3341 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "sockets not supported");
3342 return false;
3345 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)
3347 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "sockets not supported");
3348 return false;
3351 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)
3353 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "sockets not supported");
3354 return false;
3357 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)
3359 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "sockets not supported");
3360 return false;
3363 char *os_getnameinfo(unsigned char attr_unused *addr, size_t attr_unused addr_len, ajla_error_t *err)
3365 fatal_mayfail(error_ajla(EC_SYNC, AJLA_ERROR_NOT_SUPPORTED), err, "sockets not supported");
3366 return NULL;
3369 #endif
3372 const char *os_decode_error(ajla_error_t error, char attr_unused *(*tls_buffer)(void))
3374 switch (error.error_type) {
3375 #if defined(HAVE_NETWORK) && defined(HAVE_GETADDRINFO)
3376 case AJLA_ERROR_GAI: {
3377 return gai_strerror(error.error_aux * (EAI_NONAME < 0 ? -1 : 1));
3379 #else
3380 case AJLA_ERROR_H_ERRNO: {
3381 #if defined(HAVE_NETWORK) && defined(HAVE_HSTRERROR)
3382 return hstrerror(error.error_aux);
3383 #else
3384 return "Unknown host";
3385 #endif
3387 #endif
3389 return NULL;
3393 #ifdef OS_HAS_DLOPEN
3394 struct dl_handle_t *os_dlopen(const char *filename, ajla_error_t *err, char **err_msg)
3396 struct dl_handle_t *dlh;
3397 dlh = dlopen(filename, RTLD_LAZY);
3398 if (unlikely(!dlh)) {
3399 ajla_error_t e;
3400 *err_msg = dlerror();
3401 e = error_ajla(EC_SYNC, AJLA_ERROR_LIBRARY_NOT_FOUND);
3402 fatal_mayfail(e, err, "can't open dynamic library '%s': %s", filename, *err_msg);
3403 return NULL;
3405 return dlh;
3408 void os_dlclose(struct dl_handle_t *dlh)
3410 int r = dlclose(dlh);
3411 #if defined(OS_CYGWIN)
3412 /* dlclose fails if we attempt to unload non-cygwin dll */
3413 if (unlikely(r == -1) && errno == ENOENT)
3414 return;
3415 #endif
3416 if (unlikely(r))
3417 internal(file_line, "dlclose failed: %s", dlerror());
3420 bool os_dlsym(struct dl_handle_t *dlh, const char *symbol, void **result)
3422 void *r;
3423 r = dlsym(dlh, symbol);
3424 if (unlikely(!r))
3425 return false;
3426 *result = r;
3427 return true;
3429 #endif
3432 #ifdef OS_HAVE_NOTIFY_PIPE
3433 handle_t os_notify_pipe[2];
3435 void os_notify(void)
3437 int r;
3438 char c = 0;
3439 EINTR_LOOP(r, write(os_notify_pipe[1], &c, 1));
3440 if (unlikely(r == -1)) {
3441 int er = errno;
3442 if (unlikely(er != EAGAIN) && unlikely(er != EWOULDBLOCK) && unlikely(er != EBADF)) {
3443 fatal("error writing to the notify pipe: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
3448 bool os_drain_notify_pipe(void)
3450 static char buffer[1024];
3451 int r;
3452 EINTR_LOOP(r, read(os_notify_pipe[0], buffer, sizeof(buffer)));
3453 if (likely(r == -1)) {
3454 int er = errno;
3455 if (unlikely(er != EAGAIN) && unlikely(er != EWOULDBLOCK)) {
3456 fatal("error reading the notify pipe: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
3459 return !r;
3462 void os_shutdown_notify_pipe(void)
3464 int r;
3465 EINTR_LOOP(r, dup2(os_notify_pipe[0], os_notify_pipe[1]));
3466 if (unlikely(r == -1)) {
3467 int er = errno;
3468 fatal("error shutting down the notify pipe: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
3470 #ifdef DEBUG
3471 os_notify();
3472 #endif
3474 #endif
3477 #if defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME)
3479 const char *os_get_flavor(void)
3481 #if defined(OS_DOS)
3482 return "DOS";
3483 #elif defined(OS_CYGWIN)
3484 return "Cygwin";
3485 #else
3486 return "Unix";
3487 #endif
3490 void os_get_uname(os_utsname_t *un)
3492 int r;
3493 EINTR_LOOP(r, uname(un));
3494 if (unlikely(r == -1)) {
3495 int er = errno;
3496 fatal("uname returned error: %d, %s", er, error_decode(error_from_errno(EC_SYSCALL, er)));
3498 if (sizeof un->sysname >= 10 && likely(!strcmp(un->sysname, "Linux")))
3499 strcpy(un->sysname, "GNU/Linux"); /* make RMS happy */
3502 bool os_kernel_version(const char *sys, const char *vers)
3504 static os_utsname_t un;
3505 static bool have_un = false;
3506 const char *last_comp, *ptr_sys, *ptr_wanted;
3507 if (!have_un) {
3508 os_get_uname(&un);
3509 have_un = true;
3511 if (unlikely(strcmp(sys, un.sysname)))
3512 return false;
3513 last_comp = strrchr(vers, '.');
3514 if (last_comp)
3515 last_comp++;
3516 else
3517 last_comp = vers;
3518 if (strncmp(un.release, vers, last_comp - vers))
3519 return false;
3520 ptr_sys = un.release + (last_comp - vers);
3521 ptr_wanted = vers + (last_comp - vers);
3522 if (!*ptr_wanted)
3523 return true;
3524 if (likely(*ptr_sys >= '0') && likely(*ptr_sys <= '9')) {
3525 if (atoi(ptr_sys) >= atoi(ptr_wanted)) {
3526 return true;
3529 return false;
3532 #else
3534 void os_get_uname(os_utsname_t *un)
3536 memset(un, 0, sizeof(os_utsname_t));
3537 strcpy(un->sysname, "Posix");
3538 #ifdef ARCH_NAME
3539 strcpy(un->machine, ARCH_NAME);
3540 #endif
3543 bool os_kernel_version(const char attr_unused *sys, const char attr_unused *vers)
3545 return false;
3548 #endif
3550 char *os_get_host_name(ajla_error_t *err)
3552 #ifdef HAVE_GETHOSTNAME
3553 size_t s = 128;
3554 char *hn;
3555 int r;
3557 try_more:
3558 s *= 2;
3559 if (unlikely(!s)) {
3560 fatal_mayfail(error_ajla(EC_ASYNC, AJLA_ERROR_SIZE_OVERFLOW), err, "overflow when allocating host name");
3561 return NULL;
3563 hn = mem_alloc_mayfail(char *, s, err);
3564 if (unlikely(!hn))
3565 return NULL;
3567 EINTR_LOOP(r, gethostname(hn, s));
3569 if (unlikely(r == -1)) {
3570 ajla_error_t e = error_from_errno(EC_SYSCALL, errno);
3571 mem_free(hn);
3572 if (errno == EINVAL || errno == ENAMETOOLONG)
3573 goto try_more;
3574 fatal_mayfail(e, err, "can't get hostname: %s", error_decode(e));
3575 return NULL;
3578 if (unlikely(strnlen(hn, s) >= s - 1)) {
3579 mem_free(hn);
3580 goto try_more;
3583 return hn;
3584 #else
3585 char *e = getenv("HOSTNAME");
3586 if (!e)
3587 e = "";
3588 return str_dup(e, -1, err);
3589 #endif
3592 void os_init(void)
3594 ajla_error_t sink;
3595 int r, i;
3597 #if defined(OS_DOS)
3598 EINTR_LOOP(r, close(3));
3599 EINTR_LOOP(r, close(4));
3600 #endif
3602 n_std_handles = 0;
3603 while (1) {
3604 struct stat st;
3605 EINTR_LOOP(r, fstat(n_std_handles, &st));
3606 if (r)
3607 break;
3608 n_std_handles++;
3610 if (unlikely(n_std_handles < 3))
3611 exit(127);
3613 #ifdef HAVE_AT_FUNCTIONS
3614 if (os_kernel_version("GNU/Linux", "3") ||
3615 os_kernel_version("GNU/Linux", "2.6.23")) {
3616 have_O_CLOEXEC_openat = true;
3617 } else {
3618 int h, r;
3619 int flags;
3620 os_stat_t st;
3621 EINTR_LOOP(r, fstatat(AT_FDCWD, "/", &st, AT_SYMLINK_NOFOLLOW));
3622 if (unlikely(r == -1))
3623 goto skip_test;
3625 EINTR_LOOP(h, openat(AT_FDCWD, "/dev/null", O_RDONLY | O_CLOEXEC));
3626 if (unlikely(h == -1))
3627 goto skip_test;
3629 EINTR_LOOP(flags, fcntl(h, F_GETFD));
3630 if (likely(flags >= 0) && likely(flags & FD_CLOEXEC))
3631 have_O_CLOEXEC_openat = true;
3632 os_close_handle(h);
3633 skip_test:;
3635 #endif
3637 os_cwd = os_get_cwd(&sink);
3638 if (unlikely(dir_handle_is_valid(os_cwd))) {
3639 os_set_original_cwd();
3640 } else {
3641 os_cwd = os_get_cwd(NULL);
3644 os_init_path_to_exe();
3646 #ifdef OS_HAVE_NOTIFY_PIPE
3647 os_pipe(os_notify_pipe, 3, NULL);
3648 #endif
3650 #ifdef OS_HAS_SIGNALS
3651 signal_states = mem_alloc_array_mayfail(mem_calloc_mayfail, struct signal_state *, 0, 0, N_SIGNALS, sizeof(struct signal_state), NULL);
3652 for (i = 0; i < N_SIGNALS; i++)
3653 list_init(&signal_states[i].wait_list);
3654 if (!dll) {
3655 #ifdef HAVE_CODEGEN_TRAPS
3656 os_signal_trap(SIGFPE, sigfpe_handler);
3657 #if defined(ARCH_MIPS)
3658 os_signal_trap(SIGTRAP, sigfpe_handler);
3659 #endif
3660 #endif
3662 #endif
3665 void os_done(void)
3667 #ifdef OS_HAS_SIGNALS
3668 int sig;
3669 if (!dll) {
3670 #ifdef HAVE_CODEGEN_TRAPS
3671 os_signal_untrap(SIGFPE);
3672 #if defined(ARCH_MIPS)
3673 os_signal_untrap(SIGTRAP);
3674 #endif
3675 #endif
3677 for (sig = 0; sig < N_SIGNALS; sig++) {
3678 if (unlikely(signal_states[sig].trapped) || unlikely(signal_states[sig].refcount != 0))
3679 internal(file_line, "signal %d leaked", sig);
3681 mem_free(signal_states);
3682 signal_states = NULL;
3683 #endif
3685 #ifdef OS_HAVE_NOTIFY_PIPE
3686 os_close(os_notify_pipe[0]);
3687 os_close(os_notify_pipe[1]);
3688 #endif
3690 os_dir_close(os_cwd);
3692 mem_free(os_path_to_exe);
3695 void os_init_multithreaded(void)
3697 unsigned u;
3699 os_init_calendar_lock();
3701 rwmutex_init(&fork_lock);
3702 os_threads_initialized = true;
3704 #if !defined(OS_DOS)
3705 tree_init(&proc_tree);
3706 mutex_init(&proc_tree_mutex);
3707 #endif
3709 #ifdef OS_HAS_SIGNALS
3710 mutex_init(&signal_state_mutex);
3711 #endif
3713 for (u = 0; u < n_std_handles; u++)
3714 obj_registry_insert(OBJ_TYPE_HANDLE, u, file_line);
3715 #ifdef OS_DOS
3716 dos_init();
3717 #endif
3720 void os_done_multithreaded(void)
3722 unsigned u;
3723 #ifdef OS_DOS
3724 dos_done();
3725 #endif
3727 for (u = 0; u < n_std_handles; u++)
3728 obj_registry_remove(OBJ_TYPE_HANDLE, u, file_line);
3730 #ifdef OS_HAS_SIGNALS
3731 mutex_done(&signal_state_mutex);
3732 #endif
3734 #if !defined(OS_DOS)
3735 if (unlikely(!tree_is_empty(&proc_tree))) {
3736 struct proc_handle *ph = get_struct(tree_any(&proc_tree), struct proc_handle, entry);
3737 tree_delete(&ph->entry);
3738 proc_handle_free(ph);
3740 mutex_done(&proc_tree_mutex);
3741 #endif
3743 os_threads_initialized = false;
3744 rwmutex_done(&fork_lock);
3746 os_done_calendar_lock();
3749 #endif