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