os_unix.c (5410B)
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 <sys/inotify.h> 12 #include <sys/mman.h> 13 #include <sys/stat.h> 14 #include <unistd.h> 15 16 static PLATFORM_WRITE_FILE_FN(os_write_file) 17 { 18 while (raw.len) { 19 size r = write(file, raw.data, raw.len); 20 if (r < 0) return 0; 21 raw = s8_cut_head(raw, r); 22 } 23 return 1; 24 } 25 26 static void 27 os_write_err_msg(s8 msg) 28 { 29 os_write_file(STDERR_FILENO, msg); 30 } 31 32 static void __attribute__((noreturn)) 33 os_fatal(s8 msg) 34 { 35 os_write_err_msg(msg); 36 _exit(1); 37 unreachable(); 38 } 39 40 static PLATFORM_ALLOC_ARENA_FN(os_alloc_arena) 41 { 42 Arena result; 43 size pagesize = sysconf(_SC_PAGESIZE); 44 if (capacity % pagesize != 0) 45 capacity += pagesize - capacity % pagesize; 46 47 size oldsize = old.end - old.beg; 48 if (oldsize > capacity) 49 return old; 50 51 if (old.beg) 52 munmap(old.beg, oldsize); 53 54 result.beg = mmap(0, capacity, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); 55 if (result.beg == MAP_FAILED) 56 os_fatal(s8("os_alloc_arena: couldn't allocate memory\n")); 57 result.end = result.beg + capacity; 58 return result; 59 } 60 61 static PLATFORM_CLOSE_FN(os_close) 62 { 63 close(file); 64 } 65 66 static PLATFORM_OPEN_FOR_WRITE_FN(os_open_for_write) 67 { 68 iptr result = open(fname, O_WRONLY|O_TRUNC); 69 if (result == -1) 70 result = INVALID_FILE; 71 return result; 72 } 73 74 static s8 75 os_read_file(Arena *a, char *file, size filesize) 76 { 77 s8 result = {0}; 78 79 i32 fd = open(file, O_RDONLY); 80 if (fd >= 0) { 81 result = s8alloc(a, filesize); 82 size rlen = read(fd, result.data, result.len); 83 if (rlen != result.len) { 84 result = (s8){0}; 85 } 86 close(fd); 87 } 88 89 return result; 90 } 91 92 static PLATFORM_WRITE_NEW_FILE_FN(os_write_new_file) 93 { 94 iptr fd = open(fname, O_WRONLY|O_TRUNC|O_CREAT, 0600); 95 if (fd == INVALID_FILE) 96 return 0; 97 b32 ret = os_write_file(fd, raw); 98 close(fd); 99 return ret; 100 } 101 102 static FileStats 103 os_get_file_stats(char *fname) 104 { 105 struct stat st; 106 107 if (stat(fname, &st) < 0) { 108 return ERROR_FILE_STATS; 109 } 110 111 return (FileStats){ 112 .filesize = st.st_size, 113 .timestamp = (f64)st.st_mtim.tv_sec + (f64)st.st_mtim.tv_nsec * 1e-9, 114 }; 115 } 116 117 static Pipe 118 os_open_named_pipe(char *name) 119 { 120 mkfifo(name, 0660); 121 return (Pipe){.file = open(name, O_RDONLY|O_NONBLOCK), .name = name}; 122 } 123 124 static PLATFORM_READ_PIPE_FN(os_read_pipe) 125 { 126 size r = 0, total_read = 0; 127 do { 128 if (r != -1) 129 total_read += r; 130 r = read(pipe, buf + total_read, len - total_read); 131 } while (r); 132 return total_read; 133 } 134 135 static void * 136 os_open_shared_memory_area(char *name, size cap) 137 { 138 i32 fd = shm_open(name, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR); 139 if (fd == -1) 140 return NULL; 141 142 if (ftruncate(fd, cap) == -1) { 143 close(fd); 144 return NULL; 145 } 146 147 void *new = mmap(NULL, cap, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 148 close(fd); 149 150 if (new == MAP_FAILED) 151 return NULL; 152 153 return new; 154 } 155 156 /* NOTE: complete garbage because there is no standarized copyfile() in POSix */ 157 static b32 158 os_copy_file(char *name, char *new) 159 { 160 b32 result = 0; 161 struct stat sb; 162 if (stat(name, &sb) < 0) 163 return 0; 164 165 i32 fd_old = open(name, O_RDONLY); 166 i32 fd_new = open(new, O_WRONLY|O_TRUNC, sb.st_mode); 167 168 if (fd_old < 0 || fd_new < 0) 169 goto ret; 170 u8 buf[4096]; 171 size copied = 0; 172 while (copied != sb.st_size) { 173 size r = read(fd_old, buf, ARRAY_COUNT(buf)); 174 if (r < 0) goto ret; 175 size w = write(fd_new, buf, r); 176 if (w < 0) goto ret; 177 copied += w; 178 } 179 result = 1; 180 ret: 181 if (fd_old != -1) close(fd_old); 182 if (fd_new != -1) close(fd_new); 183 return result; 184 } 185 186 static void * 187 os_load_library(char *name, char *temp_name, Stream *e) 188 { 189 if (temp_name) { 190 if (os_copy_file(name, temp_name)) 191 name = temp_name; 192 } 193 void *res = dlopen(name, RTLD_NOW|RTLD_LOCAL); 194 if (!res && e) { 195 s8 errs[] = {s8("WARNING: os_load_library("), cstr_to_s8(name), s8("): "), 196 cstr_to_s8(dlerror()), s8("\n")}; 197 stream_append_s8_array(e, errs, ARRAY_COUNT(errs)); 198 os_write_err_msg(stream_to_s8(e)); 199 e->widx = 0; 200 } 201 202 if (temp_name) 203 unlink(temp_name); 204 205 return res; 206 } 207 208 static void * 209 os_lookup_dynamic_symbol(void *h, char *name, Stream *e) 210 { 211 if (!h) 212 return 0; 213 void *res = dlsym(h, name); 214 if (!res && e) { 215 s8 errs[] = {s8("WARNING: os_lookup_dynamic_symbol("), cstr_to_s8(name), s8("): "), 216 cstr_to_s8(dlerror()), s8("\n")}; 217 stream_append_s8_array(e, errs, ARRAY_COUNT(errs)); 218 os_write_err_msg(stream_to_s8(e)); 219 e->widx = 0; 220 } 221 222 return res; 223 } 224 225 static void 226 os_unload_library(void *h) 227 { 228 /* NOTE: glibc is buggy gnuware so we need to check this */ 229 if (h) 230 dlclose(h); 231 } 232 233 static PLATFORM_ADD_FILE_WATCH_FN(os_add_file_watch) 234 { 235 s8 directory = path; 236 directory.len = s8_scan_backwards(path, '/'); 237 ASSERT(directory.len > 0); 238 239 u64 hash = s8_hash(directory); 240 FileWatchContext *fwctx = &platform->file_watch_context; 241 FileWatchDirectory *dir = lookup_file_watch_directory(fwctx, hash); 242 if (!dir) { 243 ASSERT(path.data[directory.len] == '/'); 244 245 dir = fwctx->directory_watches + fwctx->directory_watch_count++; 246 dir->hash = hash; 247 248 dir->name = push_s8(a, directory); 249 arena_commit(a, 1); 250 dir->name.data[dir->name.len] = 0; 251 252 i32 mask = IN_MOVED_TO|IN_CLOSE_WRITE; 253 dir->handle = inotify_add_watch(fwctx->handle, (c8 *)dir->name.data, mask); 254 } 255 256 insert_file_watch(dir, s8_cut_head(path, dir->name.len + 1), user_data, callback); 257 }