os_linux.c (6288B)
1 /* See LICENSE for license details. */ 2 3 /* NOTE(rnp): provides the platform layer for the beamformer. This code must 4 * be provided by any platform the beamformer is ported to. */ 5 6 #include "util.h" 7 8 #include <dlfcn.h> 9 #include <fcntl.h> 10 #include <linux/futex.h> 11 #include <poll.h> 12 #include <pthread.h> 13 #include <sys/inotify.h> 14 #include <sys/mman.h> 15 #include <sys/stat.h> 16 #include <sys/syscall.h> 17 #include <unistd.h> 18 19 i64 syscall(i64, ...); 20 21 #ifdef _DEBUG 22 static void * 23 os_get_module(char *name, Stream *e) 24 { 25 void *result = dlopen(name, RTLD_NOW|RTLD_LOCAL|RTLD_NOLOAD); 26 if (!result && e) { 27 s8 errs[] = {s8("os_get_module(\""), c_str_to_s8(name), s8("\"): "), 28 c_str_to_s8(dlerror()), s8("\n")}; 29 stream_append_s8_array(e, errs, ARRAY_COUNT(errs)); 30 } 31 return result; 32 } 33 #endif 34 35 static OS_WRITE_FILE_FN(os_write_file) 36 { 37 while (raw.len) { 38 iz r = write(file, raw.data, raw.len); 39 if (r < 0) return 0; 40 raw = s8_cut_head(raw, r); 41 } 42 return 1; 43 } 44 45 static void __attribute__((noreturn)) 46 os_fatal(s8 msg) 47 { 48 os_write_file(STDERR_FILENO, msg); 49 _exit(1); 50 unreachable(); 51 } 52 53 static OS_ALLOC_ARENA_FN(os_alloc_arena) 54 { 55 Arena result; 56 iz pagesize = sysconf(_SC_PAGESIZE); 57 if (capacity % pagesize != 0) 58 capacity += pagesize - capacity % pagesize; 59 60 iz oldsize = old.end - old.beg; 61 if (oldsize > capacity) 62 return old; 63 64 if (old.beg) 65 munmap(old.beg, oldsize); 66 67 result.beg = mmap(0, capacity, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); 68 if (result.beg == MAP_FAILED) 69 os_fatal(s8("os_alloc_arena: couldn't allocate memory\n")); 70 result.end = result.beg + capacity; 71 return result; 72 } 73 74 static OS_CLOSE_FN(os_close) 75 { 76 close(file); 77 } 78 79 static OS_OPEN_FOR_WRITE_FN(os_open_for_write) 80 { 81 iptr result = open(fname, O_WRONLY|O_TRUNC); 82 if (result == -1) 83 result = INVALID_FILE; 84 return result; 85 } 86 87 static OS_READ_WHOLE_FILE_FN(os_read_whole_file) 88 { 89 s8 result = {0}; 90 91 struct stat sb; 92 i32 fd = open(file, O_RDONLY); 93 if (fd >= 0 && fstat(fd, &sb) >= 0) { 94 result = s8_alloc(arena, sb.st_size); 95 iz rlen = read(fd, result.data, result.len); 96 if (rlen != result.len) 97 result = (s8){0}; 98 } 99 if (fd >= 0) close(fd); 100 101 return result; 102 } 103 104 static OS_WRITE_NEW_FILE_FN(os_write_new_file) 105 { 106 iptr fd = open(fname, O_WRONLY|O_TRUNC|O_CREAT, 0600); 107 if (fd == INVALID_FILE) 108 return 0; 109 b32 ret = os_write_file(fd, raw); 110 close(fd); 111 return ret; 112 } 113 114 static b32 115 os_file_exists(char *path) 116 { 117 struct stat st; 118 b32 result = stat(path, &st) == 0; 119 return result; 120 } 121 122 static OS_READ_FILE_FN(os_read_file) 123 { 124 iz r = 0, total_read = 0; 125 do { 126 if (r != -1) 127 total_read += r; 128 r = read(file, buf + total_read, size - total_read); 129 } while (r); 130 return total_read; 131 } 132 133 static void * 134 os_open_shared_memory_area(char *name, iz cap) 135 { 136 void *result = 0; 137 i32 fd = shm_open(name, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR); 138 if (fd > 0) { 139 if (ftruncate(fd, cap) != -1) { 140 void *new = mmap(NULL, cap, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 141 if (new != MAP_FAILED) 142 result = new; 143 } 144 close(fd); 145 } 146 return result; 147 } 148 149 /* NOTE: complete garbage because there is no standarized copyfile() in POSix */ 150 static b32 151 os_copy_file(char *name, char *new) 152 { 153 b32 result = 0; 154 struct stat sb; 155 if (stat(name, &sb) < 0) 156 return 0; 157 158 i32 fd_old = open(name, O_RDONLY); 159 i32 fd_new = open(new, O_WRONLY|O_TRUNC, sb.st_mode); 160 161 if (fd_old < 0 || fd_new < 0) 162 goto ret; 163 u8 buf[4096]; 164 iz copied = 0; 165 while (copied != sb.st_size) { 166 iz r = read(fd_old, buf, ARRAY_COUNT(buf)); 167 if (r < 0) goto ret; 168 iz w = write(fd_new, buf, r); 169 if (w < 0) goto ret; 170 copied += w; 171 } 172 result = 1; 173 ret: 174 if (fd_old != -1) close(fd_old); 175 if (fd_new != -1) close(fd_new); 176 return result; 177 } 178 179 static void * 180 os_load_library(char *name, char *temp_name, Stream *e) 181 { 182 if (temp_name) { 183 if (os_copy_file(name, temp_name)) 184 name = temp_name; 185 } 186 187 void *result = dlopen(name, RTLD_NOW|RTLD_LOCAL); 188 if (!result && e) { 189 s8 errs[] = {s8("os_load_library(\""), c_str_to_s8(name), s8("\"): "), 190 c_str_to_s8(dlerror()), s8("\n")}; 191 stream_append_s8_array(e, errs, ARRAY_COUNT(errs)); 192 } 193 194 if (temp_name) 195 unlink(temp_name); 196 197 return result; 198 } 199 200 static void * 201 os_lookup_dynamic_symbol(void *h, char *name, Stream *e) 202 { 203 void *result = 0; 204 if (h) { 205 result = dlsym(h, name); 206 if (!result && e) { 207 s8 errs[] = {s8("os_lookup_dynamic_symbol(\""), c_str_to_s8(name), 208 s8("\"): "), c_str_to_s8(dlerror()), s8("\n")}; 209 stream_append_s8_array(e, errs, ARRAY_COUNT(errs)); 210 } 211 } 212 return result; 213 } 214 215 static void 216 os_unload_library(void *h) 217 { 218 /* NOTE: glibc is buggy gnuware so we need to check this */ 219 if (h) 220 dlclose(h); 221 } 222 223 static OS_ADD_FILE_WATCH_FN(os_add_file_watch) 224 { 225 s8 directory = path; 226 directory.len = s8_scan_backwards(path, '/'); 227 ASSERT(directory.len > 0); 228 229 u64 hash = s8_hash(directory); 230 FileWatchContext *fwctx = &os->file_watch_context; 231 FileWatchDirectory *dir = lookup_file_watch_directory(fwctx, hash); 232 if (!dir) { 233 ASSERT(path.data[directory.len] == '/'); 234 235 dir = fwctx->directory_watches + fwctx->directory_watch_count++; 236 dir->hash = hash; 237 dir->name = push_s8_zero(a, directory); 238 i32 mask = IN_MOVED_TO|IN_CLOSE_WRITE; 239 dir->handle = inotify_add_watch(fwctx->handle, (c8 *)dir->name.data, mask); 240 } 241 242 insert_file_watch(dir, s8_cut_head(path, dir->name.len + 1), user_data, callback); 243 } 244 245 i32 pthread_setname_np(pthread_t, char *); 246 static iptr 247 os_create_thread(Arena arena, iptr user_context, s8 name, os_thread_entry_point_fn *fn) 248 { 249 pthread_t result; 250 pthread_create(&result, 0, (void *(*)(void *))fn, (void *)user_context); 251 pthread_setname_np(result, (char *)name.data); 252 return (iptr)result; 253 } 254 255 static OS_WAIT_ON_VALUE_FN(os_wait_on_value) 256 { 257 struct timespec *timeout = 0, timeout_value; 258 if (timeout_ms != (u32)-1) { 259 timeout_value.tv_sec = timeout_ms / 1000; 260 timeout_value.tv_nsec = (timeout_ms % 1000) * 1000000; 261 timeout = &timeout_value; 262 } 263 return syscall(SYS_futex, value, FUTEX_WAIT, current, timeout, 0, 0) == 0; 264 } 265 266 static OS_WAKE_WAITERS_FN(os_wake_waiters) 267 { 268 if (sync) { 269 atomic_inc(sync, 1); 270 syscall(SYS_futex, sync, FUTEX_WAKE, I32_MAX, 0, 0, 0); 271 } 272 } 273 274 /* TODO(rnp): what do if not X11? */ 275 iptr glfwGetGLXContext(iptr); 276 277 static iptr 278 os_get_native_gl_context(iptr window) 279 { 280 return glfwGetGLXContext(window); 281 }