platform-nix.c 23 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109
  1. /*
  2. * Copyright (c) 2023 Lain Bailey <[email protected]>
  3. *
  4. * Permission to use, copy, modify, and distribute this software for any
  5. * purpose with or without fee is hereby granted, provided that the above
  6. * copyright notice and this permission notice appear in all copies.
  7. *
  8. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #include <stdio.h>
  17. #include <errno.h>
  18. #include <sys/types.h>
  19. #include <sys/stat.h>
  20. #include <sys/statvfs.h>
  21. #include <dirent.h>
  22. #include <stdlib.h>
  23. #include <limits.h>
  24. #include <dlfcn.h>
  25. #include <unistd.h>
  26. #include <glob.h>
  27. #include <time.h>
  28. #include <signal.h>
  29. #include <uuid/uuid.h>
  30. #include "obsconfig.h"
  31. #if !defined(__APPLE__)
  32. #include <sys/times.h>
  33. #include <sys/wait.h>
  34. #include <libgen.h>
  35. #if defined(__FreeBSD__) || defined(__OpenBSD__)
  36. #include <sys/param.h>
  37. #include <sys/queue.h>
  38. #include <sys/socket.h>
  39. #include <sys/sysctl.h>
  40. #include <sys/user.h>
  41. #include <unistd.h>
  42. #if defined(__FreeBSD__)
  43. #include <libprocstat.h>
  44. #endif
  45. #else
  46. #include <sys/resource.h>
  47. #endif
  48. #if !defined(__OpenBSD__)
  49. #include <sys/sysinfo.h>
  50. #endif
  51. #include <spawn.h>
  52. #endif
  53. #include "darray.h"
  54. #include "dstr.h"
  55. #include "platform.h"
  56. #include "threading.h"
  57. void *os_dlopen(const char *path)
  58. {
  59. struct dstr dylib_name;
  60. if (!path)
  61. return NULL;
  62. dstr_init_copy(&dylib_name, path);
  63. #ifdef __APPLE__
  64. if (!dstr_find(&dylib_name, ".framework") && !dstr_find(&dylib_name, ".plugin") &&
  65. !dstr_find(&dylib_name, ".dylib") && !dstr_find(&dylib_name, ".so"))
  66. #else
  67. if (!dstr_find(&dylib_name, ".so"))
  68. #endif
  69. dstr_cat(&dylib_name, ".so");
  70. #ifdef __APPLE__
  71. int dlopen_flags = RTLD_NOW | RTLD_FIRST;
  72. if (dstr_find(&dylib_name, "Python")) {
  73. dlopen_flags = dlopen_flags | RTLD_GLOBAL;
  74. } else {
  75. dlopen_flags = dlopen_flags | RTLD_LOCAL;
  76. }
  77. void *res = dlopen(dylib_name.array, dlopen_flags);
  78. #else
  79. void *res = dlopen(dylib_name.array, RTLD_NOW);
  80. #endif
  81. if (!res)
  82. blog(LOG_ERROR, "os_dlopen(%s->%s): %s\n", path, dylib_name.array, dlerror());
  83. dstr_free(&dylib_name);
  84. return res;
  85. }
  86. void *os_dlsym(void *module, const char *func)
  87. {
  88. return dlsym(module, func);
  89. }
  90. void os_dlclose(void *module)
  91. {
  92. if (module)
  93. dlclose(module);
  94. }
  95. void get_plugin_info(const char *path, bool *is_obs_plugin, bool *can_load)
  96. {
  97. *is_obs_plugin = true;
  98. *can_load = true;
  99. UNUSED_PARAMETER(path);
  100. }
  101. bool os_is_obs_plugin(const char *path)
  102. {
  103. UNUSED_PARAMETER(path);
  104. /* not necessary on this platform */
  105. return true;
  106. }
  107. #if !defined(__APPLE__)
  108. struct os_cpu_usage_info {
  109. clock_t last_cpu_time, last_sys_time, last_user_time;
  110. int core_count;
  111. };
  112. os_cpu_usage_info_t *os_cpu_usage_info_start(void)
  113. {
  114. struct os_cpu_usage_info *info = bmalloc(sizeof(*info));
  115. struct tms time_sample;
  116. info->last_cpu_time = times(&time_sample);
  117. info->last_sys_time = time_sample.tms_stime;
  118. info->last_user_time = time_sample.tms_utime;
  119. info->core_count = sysconf(_SC_NPROCESSORS_ONLN);
  120. return info;
  121. }
  122. double os_cpu_usage_info_query(os_cpu_usage_info_t *info)
  123. {
  124. struct tms time_sample;
  125. clock_t cur_cpu_time;
  126. double percent;
  127. if (!info)
  128. return 0.0;
  129. cur_cpu_time = times(&time_sample);
  130. if (cur_cpu_time <= info->last_cpu_time || time_sample.tms_stime < info->last_sys_time ||
  131. time_sample.tms_utime < info->last_user_time)
  132. return 0.0;
  133. percent =
  134. (double)(time_sample.tms_stime - info->last_sys_time + (time_sample.tms_utime - info->last_user_time));
  135. percent /= (double)(cur_cpu_time - info->last_cpu_time);
  136. percent /= (double)info->core_count;
  137. info->last_cpu_time = cur_cpu_time;
  138. info->last_sys_time = time_sample.tms_stime;
  139. info->last_user_time = time_sample.tms_utime;
  140. return percent * 100.0;
  141. }
  142. void os_cpu_usage_info_destroy(os_cpu_usage_info_t *info)
  143. {
  144. if (info)
  145. bfree(info);
  146. }
  147. #endif
  148. bool os_sleepto_ns(uint64_t time_target)
  149. {
  150. uint64_t current = os_gettime_ns();
  151. if (time_target < current)
  152. return false;
  153. time_target -= current;
  154. struct timespec req, remain;
  155. memset(&req, 0, sizeof(req));
  156. memset(&remain, 0, sizeof(remain));
  157. req.tv_sec = time_target / 1000000000;
  158. req.tv_nsec = time_target % 1000000000;
  159. while (nanosleep(&req, &remain)) {
  160. req = remain;
  161. memset(&remain, 0, sizeof(remain));
  162. }
  163. return true;
  164. }
  165. bool os_sleepto_ns_fast(uint64_t time_target)
  166. {
  167. uint64_t current = os_gettime_ns();
  168. if (time_target < current)
  169. return false;
  170. do {
  171. uint64_t remain_us = (time_target - current + 999) / 1000;
  172. useconds_t us = remain_us >= 1000000 ? 999999 : remain_us;
  173. usleep(us);
  174. current = os_gettime_ns();
  175. } while (time_target > current);
  176. return true;
  177. }
  178. void os_sleep_ms(uint32_t duration)
  179. {
  180. usleep(duration * 1000);
  181. }
  182. #if !defined(__APPLE__)
  183. uint64_t os_gettime_ns(void)
  184. {
  185. struct timespec ts;
  186. clock_gettime(CLOCK_MONOTONIC, &ts);
  187. return ((uint64_t)ts.tv_sec * 1000000000ULL + (uint64_t)ts.tv_nsec);
  188. }
  189. /* should return $HOME/.config/[name] as default */
  190. int os_get_config_path(char *dst, size_t size, const char *name)
  191. {
  192. char *xdg_ptr = getenv("XDG_CONFIG_HOME");
  193. // If XDG_CONFIG_HOME is unset,
  194. // we use the default $HOME/.config/[name] instead
  195. if (xdg_ptr == NULL) {
  196. char *home_ptr = getenv("HOME");
  197. if (home_ptr == NULL)
  198. bcrash("Could not get $HOME\n");
  199. if (!name || !*name) {
  200. return snprintf(dst, size, "%s/.config", home_ptr);
  201. } else {
  202. return snprintf(dst, size, "%s/.config/%s", home_ptr, name);
  203. }
  204. } else {
  205. if (!name || !*name)
  206. return snprintf(dst, size, "%s", xdg_ptr);
  207. else
  208. return snprintf(dst, size, "%s/%s", xdg_ptr, name);
  209. }
  210. }
  211. /* should return $HOME/.config/[name] as default */
  212. char *os_get_config_path_ptr(const char *name)
  213. {
  214. struct dstr path;
  215. char *xdg_ptr = getenv("XDG_CONFIG_HOME");
  216. /* If XDG_CONFIG_HOME is unset,
  217. * we use the default $HOME/.config/[name] instead */
  218. if (xdg_ptr == NULL) {
  219. char *home_ptr = getenv("HOME");
  220. if (home_ptr == NULL)
  221. bcrash("Could not get $HOME\n");
  222. dstr_init_copy(&path, home_ptr);
  223. dstr_cat(&path, "/.config/");
  224. dstr_cat(&path, name);
  225. } else {
  226. dstr_init_copy(&path, xdg_ptr);
  227. dstr_cat(&path, "/");
  228. dstr_cat(&path, name);
  229. }
  230. return path.array;
  231. }
  232. int os_get_program_data_path(char *dst, size_t size, const char *name)
  233. {
  234. return snprintf(dst, size, "/usr/local/share/%s", !!name ? name : "");
  235. }
  236. char *os_get_program_data_path_ptr(const char *name)
  237. {
  238. size_t len = snprintf(NULL, 0, "/usr/local/share/%s", !!name ? name : "");
  239. char *str = bmalloc(len + 1);
  240. snprintf(str, len + 1, "/usr/local/share/%s", !!name ? name : "");
  241. str[len] = 0;
  242. return str;
  243. }
  244. #if defined(__OpenBSD__)
  245. // a bit modified version of https://stackoverflow.com/a/31495527
  246. ssize_t os_openbsd_get_executable_path(char *epath)
  247. {
  248. int mib[4];
  249. char **argv;
  250. size_t len;
  251. const char *comm;
  252. int ok = 0;
  253. mib[0] = CTL_KERN;
  254. mib[1] = KERN_PROC_ARGS;
  255. mib[2] = getpid();
  256. mib[3] = KERN_PROC_ARGV;
  257. if (sysctl(mib, 4, NULL, &len, NULL, 0) < 0)
  258. abort();
  259. if (!(argv = malloc(len)))
  260. abort();
  261. if (sysctl(mib, 4, argv, &len, NULL, 0) < 0)
  262. abort();
  263. comm = argv[0];
  264. if (*comm == '/' || *comm == '.') {
  265. if (realpath(comm, epath))
  266. ok = 1;
  267. } else {
  268. char *sp;
  269. char *xpath = strdup(getenv("PATH"));
  270. char *path = strtok_r(xpath, ":", &sp);
  271. struct stat st;
  272. if (!xpath)
  273. abort();
  274. while (path) {
  275. snprintf(epath, PATH_MAX, "%s/%s", path, comm);
  276. if (!stat(epath, &st) && (st.st_mode & S_IXUSR)) {
  277. ok = 1;
  278. break;
  279. }
  280. path = strtok_r(NULL, ":", &sp);
  281. }
  282. free(xpath);
  283. }
  284. free(argv);
  285. return ok ? (ssize_t)strlen(epath) : -1;
  286. }
  287. #endif
  288. char *os_get_executable_path_ptr(const char *name)
  289. {
  290. char exe[PATH_MAX];
  291. #if defined(__FreeBSD__) || defined(__DragonFly__)
  292. int sysctlname[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
  293. size_t pathlen = PATH_MAX;
  294. ssize_t count;
  295. if (sysctl(sysctlname, nitems(sysctlname), exe, &pathlen, NULL, 0) == -1) {
  296. blog(LOG_ERROR, "sysctl(KERN_PROC_PATHNAME) failed, errno %d", errno);
  297. return NULL;
  298. }
  299. count = pathlen;
  300. #elif defined(__OpenBSD__)
  301. ssize_t count = os_openbsd_get_executable_path(exe);
  302. #else
  303. ssize_t count = readlink("/proc/self/exe", exe, PATH_MAX - 1);
  304. if (count >= 0) {
  305. exe[count] = '\0';
  306. }
  307. #endif
  308. const char *path_out = NULL;
  309. struct dstr path;
  310. if (count == -1) {
  311. return NULL;
  312. }
  313. path_out = dirname(exe);
  314. if (!path_out) {
  315. return NULL;
  316. }
  317. dstr_init_copy(&path, path_out);
  318. dstr_cat(&path, "/");
  319. if (name && *name) {
  320. dstr_cat(&path, name);
  321. }
  322. return path.array;
  323. }
  324. bool os_get_emulation_status(void)
  325. {
  326. return false;
  327. }
  328. #endif
  329. bool os_file_exists(const char *path)
  330. {
  331. return access(path, F_OK) == 0;
  332. }
  333. size_t os_get_abs_path(const char *path, char *abspath, size_t size)
  334. {
  335. size_t min_size = size < PATH_MAX ? size : PATH_MAX;
  336. char newpath[PATH_MAX];
  337. int ret;
  338. if (!abspath)
  339. return 0;
  340. if (!realpath(path, newpath))
  341. return 0;
  342. ret = snprintf(abspath, min_size, "%s", newpath);
  343. return ret >= 0 ? ret : 0;
  344. }
  345. char *os_get_abs_path_ptr(const char *path)
  346. {
  347. char *ptr = bmalloc(512);
  348. if (!os_get_abs_path(path, ptr, 512)) {
  349. bfree(ptr);
  350. ptr = NULL;
  351. }
  352. return ptr;
  353. }
  354. struct os_dir {
  355. const char *path;
  356. DIR *dir;
  357. struct dirent *cur_dirent;
  358. struct os_dirent out;
  359. };
  360. os_dir_t *os_opendir(const char *path)
  361. {
  362. struct os_dir *dir;
  363. DIR *dir_val;
  364. dir_val = opendir(path);
  365. if (!dir_val)
  366. return NULL;
  367. dir = bzalloc(sizeof(struct os_dir));
  368. dir->dir = dir_val;
  369. dir->path = path;
  370. return dir;
  371. }
  372. static inline bool is_dir(const char *path)
  373. {
  374. struct stat stat_info;
  375. if (stat(path, &stat_info) == 0)
  376. return !!S_ISDIR(stat_info.st_mode);
  377. blog(LOG_DEBUG, "is_dir: stat for %s failed, errno: %d", path, errno);
  378. return false;
  379. }
  380. struct os_dirent *os_readdir(os_dir_t *dir)
  381. {
  382. struct dstr file_path = {0};
  383. if (!dir)
  384. return NULL;
  385. dir->cur_dirent = readdir(dir->dir);
  386. if (!dir->cur_dirent)
  387. return NULL;
  388. const size_t length = strlen(dir->cur_dirent->d_name);
  389. if (sizeof(dir->out.d_name) <= length)
  390. return NULL;
  391. memcpy(dir->out.d_name, dir->cur_dirent->d_name, length + 1);
  392. dstr_copy(&file_path, dir->path);
  393. dstr_cat(&file_path, "/");
  394. dstr_cat(&file_path, dir->out.d_name);
  395. dir->out.directory = is_dir(file_path.array);
  396. dstr_free(&file_path);
  397. return &dir->out;
  398. }
  399. void os_closedir(os_dir_t *dir)
  400. {
  401. if (dir) {
  402. closedir(dir->dir);
  403. bfree(dir);
  404. }
  405. }
  406. #ifndef __APPLE__
  407. int64_t os_get_free_space(const char *path)
  408. {
  409. struct statvfs info;
  410. int64_t ret = (int64_t)statvfs(path, &info);
  411. if (ret == 0)
  412. ret = (int64_t)info.f_bsize * (int64_t)info.f_bfree;
  413. return ret;
  414. }
  415. #endif
  416. struct posix_glob_info {
  417. struct os_glob_info base;
  418. glob_t gl;
  419. };
  420. int os_glob(const char *pattern, int flags, os_glob_t **pglob)
  421. {
  422. struct posix_glob_info pgi;
  423. int ret = glob(pattern, 0, NULL, &pgi.gl);
  424. if (ret == 0) {
  425. DARRAY(struct os_globent) list;
  426. da_init(list);
  427. for (size_t i = 0; i < pgi.gl.gl_pathc; i++) {
  428. struct os_globent ent = {0};
  429. ent.path = pgi.gl.gl_pathv[i];
  430. ent.directory = is_dir(ent.path);
  431. da_push_back(list, &ent);
  432. }
  433. pgi.base.gl_pathc = list.num;
  434. pgi.base.gl_pathv = list.array;
  435. *pglob = bmemdup(&pgi, sizeof(pgi));
  436. } else {
  437. *pglob = NULL;
  438. }
  439. UNUSED_PARAMETER(flags);
  440. return ret;
  441. }
  442. void os_globfree(os_glob_t *pglob)
  443. {
  444. if (pglob) {
  445. struct posix_glob_info *pgi = (struct posix_glob_info *)pglob;
  446. globfree(&pgi->gl);
  447. bfree(pgi->base.gl_pathv);
  448. bfree(pgi);
  449. }
  450. }
  451. int os_unlink(const char *path)
  452. {
  453. return unlink(path);
  454. }
  455. int os_rmdir(const char *path)
  456. {
  457. return rmdir(path);
  458. }
  459. int os_mkdir(const char *path)
  460. {
  461. if (mkdir(path, 0755) == 0)
  462. return MKDIR_SUCCESS;
  463. return (errno == EEXIST) ? MKDIR_EXISTS : MKDIR_ERROR;
  464. }
  465. int os_rename(const char *old_path, const char *new_path)
  466. {
  467. return rename(old_path, new_path);
  468. }
  469. int os_safe_replace(const char *target, const char *from, const char *backup)
  470. {
  471. if (backup && os_file_exists(target) && rename(target, backup) != 0)
  472. return -1;
  473. return rename(from, target);
  474. }
  475. #if !defined(__APPLE__)
  476. os_performance_token_t *os_request_high_performance(const char *reason)
  477. {
  478. UNUSED_PARAMETER(reason);
  479. return NULL;
  480. }
  481. void os_end_high_performance(os_performance_token_t *token)
  482. {
  483. UNUSED_PARAMETER(token);
  484. }
  485. #endif
  486. int os_copyfile(const char *file_path_in, const char *file_path_out)
  487. {
  488. FILE *file_out = NULL;
  489. FILE *file_in = NULL;
  490. uint8_t data[4096];
  491. int ret = -1;
  492. size_t size;
  493. if (os_file_exists(file_path_out))
  494. return -1;
  495. file_in = fopen(file_path_in, "rb");
  496. if (!file_in)
  497. return -1;
  498. file_out = fopen(file_path_out, "ab+");
  499. if (!file_out)
  500. goto error;
  501. do {
  502. size = fread(data, 1, sizeof(data), file_in);
  503. if (size)
  504. size = fwrite(data, 1, size, file_out);
  505. } while (size == sizeof(data));
  506. ret = feof(file_in) ? 0 : -1;
  507. error:
  508. if (file_out)
  509. fclose(file_out);
  510. fclose(file_in);
  511. return ret;
  512. }
  513. char *os_getcwd(char *path, size_t size)
  514. {
  515. return getcwd(path, size);
  516. }
  517. int os_chdir(const char *path)
  518. {
  519. return chdir(path);
  520. }
  521. #if !defined(__APPLE__)
  522. #if defined(GIO_FOUND)
  523. struct dbus_sleep_info;
  524. struct portal_inhibit_info;
  525. extern struct dbus_sleep_info *dbus_sleep_info_create(void);
  526. extern void dbus_inhibit_sleep(struct dbus_sleep_info *dbus, const char *sleep, bool active);
  527. extern void dbus_sleep_info_destroy(struct dbus_sleep_info *dbus);
  528. extern struct portal_inhibit_info *portal_inhibit_info_create(void);
  529. extern void portal_inhibit(struct portal_inhibit_info *portal, const char *reason, bool active);
  530. extern void portal_inhibit_info_destroy(struct portal_inhibit_info *portal);
  531. #endif
  532. struct os_inhibit_info {
  533. #if defined(GIO_FOUND)
  534. struct dbus_sleep_info *dbus;
  535. struct portal_inhibit_info *portal;
  536. #endif
  537. pthread_t screensaver_thread;
  538. os_event_t *stop_event;
  539. char *reason;
  540. posix_spawnattr_t attr;
  541. bool active;
  542. };
  543. os_inhibit_t *os_inhibit_sleep_create(const char *reason)
  544. {
  545. struct os_inhibit_info *info = bzalloc(sizeof(*info));
  546. sigset_t set;
  547. #if defined(GIO_FOUND)
  548. info->portal = portal_inhibit_info_create();
  549. if (!info->portal) {
  550. /* In a Flatpak, only the portal can be used for inhibition. */
  551. if (access("/.flatpak-info", F_OK) == 0) {
  552. bfree(info);
  553. return NULL;
  554. }
  555. info->dbus = dbus_sleep_info_create();
  556. }
  557. if (info->portal || info->dbus) {
  558. info->reason = bstrdup(reason);
  559. return info;
  560. }
  561. #endif
  562. os_event_init(&info->stop_event, OS_EVENT_TYPE_AUTO);
  563. posix_spawnattr_init(&info->attr);
  564. sigemptyset(&set);
  565. posix_spawnattr_setsigmask(&info->attr, &set);
  566. sigaddset(&set, SIGPIPE);
  567. posix_spawnattr_setsigdefault(&info->attr, &set);
  568. posix_spawnattr_setflags(&info->attr, POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK);
  569. info->reason = bstrdup(reason);
  570. return info;
  571. }
  572. extern char **environ;
  573. static void reset_screensaver(os_inhibit_t *info)
  574. {
  575. char *argv[3] = {(char *)"xdg-screensaver", (char *)"reset", NULL};
  576. pid_t pid;
  577. int err = posix_spawnp(&pid, "xdg-screensaver", NULL, &info->attr, argv, environ);
  578. if (err == 0) {
  579. int status;
  580. while (waitpid(pid, &status, 0) == -1)
  581. ;
  582. } else {
  583. blog(LOG_WARNING, "Failed to create xdg-screensaver: %d", err);
  584. }
  585. }
  586. static void *screensaver_thread(void *param)
  587. {
  588. os_inhibit_t *info = param;
  589. while (os_event_timedwait(info->stop_event, 30000) == ETIMEDOUT)
  590. reset_screensaver(info);
  591. return NULL;
  592. }
  593. bool os_inhibit_sleep_set_active(os_inhibit_t *info, bool active)
  594. {
  595. int ret;
  596. if (!info)
  597. return false;
  598. if (info->active == active)
  599. return false;
  600. #if defined(GIO_FOUND)
  601. if (info->portal)
  602. portal_inhibit(info->portal, info->reason, active);
  603. if (info->dbus)
  604. dbus_inhibit_sleep(info->dbus, info->reason, active);
  605. if (info->portal || info->dbus) {
  606. info->active = active;
  607. return true;
  608. }
  609. #endif
  610. if (!info->stop_event)
  611. return true;
  612. if (active) {
  613. ret = pthread_create(&info->screensaver_thread, NULL, &screensaver_thread, info);
  614. if (ret < 0) {
  615. blog(LOG_ERROR, "Failed to create screensaver "
  616. "inhibitor thread");
  617. return false;
  618. }
  619. } else {
  620. os_event_signal(info->stop_event);
  621. pthread_join(info->screensaver_thread, NULL);
  622. }
  623. info->active = active;
  624. return true;
  625. }
  626. void os_inhibit_sleep_destroy(os_inhibit_t *info)
  627. {
  628. if (info) {
  629. os_inhibit_sleep_set_active(info, false);
  630. #if defined(GIO_FOUND)
  631. if (info->portal) {
  632. portal_inhibit_info_destroy(info->portal);
  633. } else if (info->dbus) {
  634. dbus_sleep_info_destroy(info->dbus);
  635. } else {
  636. os_event_destroy(info->stop_event);
  637. posix_spawnattr_destroy(&info->attr);
  638. }
  639. #else
  640. os_event_destroy(info->stop_event);
  641. posix_spawnattr_destroy(&info->attr);
  642. #endif
  643. bfree(info->reason);
  644. bfree(info);
  645. }
  646. }
  647. #endif
  648. void os_breakpoint()
  649. {
  650. raise(SIGTRAP);
  651. }
  652. void os_oom()
  653. {
  654. raise(SIGTRAP);
  655. }
  656. #ifndef __APPLE__
  657. static int physical_cores = 0;
  658. static int logical_cores = 0;
  659. static bool core_count_initialized = false;
  660. static void os_get_cores_internal(void)
  661. {
  662. if (core_count_initialized)
  663. return;
  664. core_count_initialized = true;
  665. logical_cores = sysconf(_SC_NPROCESSORS_ONLN);
  666. #if defined(__linux__)
  667. int physical_id = -1;
  668. int last_physical_id = -1;
  669. int core_count = 0;
  670. char *line = NULL;
  671. size_t linecap = 0;
  672. FILE *fp;
  673. struct dstr proc_phys_id;
  674. struct dstr proc_phys_ids;
  675. fp = fopen("/proc/cpuinfo", "r");
  676. if (!fp)
  677. return;
  678. dstr_init(&proc_phys_id);
  679. dstr_init(&proc_phys_ids);
  680. while (getline(&line, &linecap, fp) != -1) {
  681. if (!strncmp(line, "physical id", 11)) {
  682. char *start = strchr(line, ':');
  683. if (!start || *(++start) == '\0')
  684. continue;
  685. physical_id = atoi(start);
  686. dstr_free(&proc_phys_id);
  687. dstr_init(&proc_phys_id);
  688. dstr_catf(&proc_phys_id, "%d", physical_id);
  689. }
  690. if (!strncmp(line, "cpu cores", 9)) {
  691. char *start = strchr(line, ':');
  692. if (!start || *(++start) == '\0')
  693. continue;
  694. if (dstr_is_empty(&proc_phys_ids) ||
  695. (!dstr_is_empty(&proc_phys_ids) && !dstr_find(&proc_phys_ids, proc_phys_id.array))) {
  696. dstr_cat_dstr(&proc_phys_ids, &proc_phys_id);
  697. dstr_cat(&proc_phys_ids, " ");
  698. core_count += atoi(start);
  699. }
  700. }
  701. if (*line == '\n' && physical_id != last_physical_id) {
  702. last_physical_id = physical_id;
  703. }
  704. }
  705. if (core_count == 0)
  706. physical_cores = logical_cores;
  707. else
  708. physical_cores = core_count;
  709. fclose(fp);
  710. dstr_free(&proc_phys_ids);
  711. dstr_free(&proc_phys_id);
  712. free(line);
  713. #elif defined(__FreeBSD__)
  714. char *text = os_quick_read_utf8_file("/var/run/dmesg.boot");
  715. char *core_count = text;
  716. int packages = 0;
  717. int cores = 0;
  718. struct dstr proc_packages;
  719. struct dstr proc_cores;
  720. dstr_init(&proc_packages);
  721. dstr_init(&proc_cores);
  722. if (!text || !*text) {
  723. physical_cores = logical_cores;
  724. return;
  725. }
  726. core_count = strstr(core_count, "\nFreeBSD/SMP: ");
  727. if (!core_count)
  728. goto FreeBSD_cores_cleanup;
  729. core_count++;
  730. core_count = strstr(core_count, "\nFreeBSD/SMP: ");
  731. if (!core_count)
  732. goto FreeBSD_cores_cleanup;
  733. core_count = strstr(core_count, ": ");
  734. core_count += 2;
  735. size_t len = strcspn(core_count, " ");
  736. dstr_ncopy(&proc_packages, core_count, len);
  737. core_count = strstr(core_count, "package(s) x ");
  738. if (!core_count)
  739. goto FreeBSD_cores_cleanup;
  740. core_count += 13;
  741. len = strcspn(core_count, " ");
  742. dstr_ncopy(&proc_cores, core_count, len);
  743. FreeBSD_cores_cleanup:
  744. if (!dstr_is_empty(&proc_packages))
  745. packages = atoi(proc_packages.array);
  746. if (!dstr_is_empty(&proc_cores))
  747. cores = atoi(proc_cores.array);
  748. if (packages == 0)
  749. physical_cores = logical_cores;
  750. else if (cores == 0)
  751. physical_cores = packages;
  752. else
  753. physical_cores = packages * cores;
  754. dstr_free(&proc_cores);
  755. dstr_free(&proc_packages);
  756. bfree(text);
  757. #else
  758. physical_cores = logical_cores;
  759. #endif
  760. }
  761. int os_get_physical_cores(void)
  762. {
  763. if (!core_count_initialized)
  764. os_get_cores_internal();
  765. return physical_cores;
  766. }
  767. int os_get_logical_cores(void)
  768. {
  769. if (!core_count_initialized)
  770. os_get_cores_internal();
  771. return logical_cores;
  772. }
  773. #ifdef __FreeBSD__
  774. uint64_t os_get_sys_free_size(void)
  775. {
  776. uint64_t mem_free = 0;
  777. size_t length = sizeof(mem_free);
  778. if (sysctlbyname("vm.stats.vm.v_free_count", &mem_free, &length, NULL, 0) < 0)
  779. return 0;
  780. return mem_free;
  781. }
  782. static inline bool os_get_proc_memory_usage_internal(struct kinfo_proc *kinfo)
  783. {
  784. int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()};
  785. size_t length = sizeof(*kinfo);
  786. if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), kinfo, &length, NULL, 0) < 0)
  787. return false;
  788. return true;
  789. }
  790. bool os_get_proc_memory_usage(os_proc_memory_usage_t *usage)
  791. {
  792. struct kinfo_proc kinfo;
  793. if (!os_get_proc_memory_usage_internal(&kinfo))
  794. return false;
  795. usage->resident_size = (uint64_t)kinfo.ki_rssize * sysconf(_SC_PAGESIZE);
  796. usage->virtual_size = (uint64_t)kinfo.ki_size;
  797. return true;
  798. }
  799. uint64_t os_get_proc_resident_size(void)
  800. {
  801. struct kinfo_proc kinfo;
  802. if (!os_get_proc_memory_usage_internal(&kinfo))
  803. return 0;
  804. return (uint64_t)kinfo.ki_rssize * sysconf(_SC_PAGESIZE);
  805. }
  806. uint64_t os_get_proc_virtual_size(void)
  807. {
  808. struct kinfo_proc kinfo;
  809. if (!os_get_proc_memory_usage_internal(&kinfo))
  810. return 0;
  811. return (uint64_t)kinfo.ki_size;
  812. }
  813. #else
  814. typedef struct {
  815. unsigned long virtual_size;
  816. unsigned long resident_size;
  817. unsigned long share_pages;
  818. unsigned long text;
  819. unsigned long library;
  820. unsigned long data;
  821. unsigned long dirty_pages;
  822. } statm_t;
  823. static inline bool os_get_proc_memory_usage_internal(statm_t *statm)
  824. {
  825. const char *statm_path = "/proc/self/statm";
  826. FILE *f = fopen(statm_path, "r");
  827. if (!f)
  828. return false;
  829. if (fscanf(f, "%lu %lu %lu %lu %lu %lu %lu", &statm->virtual_size, &statm->resident_size, &statm->share_pages,
  830. &statm->text, &statm->library, &statm->data, &statm->dirty_pages) != 7) {
  831. fclose(f);
  832. return false;
  833. }
  834. fclose(f);
  835. return true;
  836. }
  837. bool os_get_proc_memory_usage(os_proc_memory_usage_t *usage)
  838. {
  839. statm_t statm = {};
  840. if (!os_get_proc_memory_usage_internal(&statm))
  841. return false;
  842. usage->resident_size = (uint64_t)statm.resident_size * sysconf(_SC_PAGESIZE);
  843. usage->virtual_size = statm.virtual_size;
  844. return true;
  845. }
  846. uint64_t os_get_proc_resident_size(void)
  847. {
  848. statm_t statm = {};
  849. if (!os_get_proc_memory_usage_internal(&statm))
  850. return 0;
  851. return (uint64_t)statm.resident_size * sysconf(_SC_PAGESIZE);
  852. }
  853. uint64_t os_get_proc_virtual_size(void)
  854. {
  855. statm_t statm = {};
  856. if (!os_get_proc_memory_usage_internal(&statm))
  857. return 0;
  858. return (uint64_t)statm.virtual_size;
  859. }
  860. uint64_t os_get_sys_free_size(void)
  861. {
  862. uint64_t free_memory = 0;
  863. #ifndef __OpenBSD__
  864. struct sysinfo info;
  865. if (sysinfo(&info) < 0)
  866. return 0;
  867. free_memory = ((uint64_t)info.freeram + (uint64_t)info.bufferram) * info.mem_unit;
  868. #endif
  869. return free_memory;
  870. }
  871. #endif
  872. static uint64_t total_memory = 0;
  873. static bool total_memory_initialized = false;
  874. static void os_get_sys_total_size_internal()
  875. {
  876. total_memory_initialized = true;
  877. #ifndef __OpenBSD__
  878. struct sysinfo info;
  879. if (sysinfo(&info) < 0)
  880. return;
  881. total_memory = (uint64_t)info.totalram * info.mem_unit;
  882. #endif
  883. }
  884. uint64_t os_get_sys_total_size(void)
  885. {
  886. if (!total_memory_initialized)
  887. os_get_sys_total_size_internal();
  888. return total_memory;
  889. }
  890. #endif
  891. #ifndef __APPLE__
  892. uint64_t os_get_free_disk_space(const char *dir)
  893. {
  894. struct statvfs info;
  895. if (statvfs(dir, &info) != 0)
  896. return 0;
  897. return (uint64_t)info.f_frsize * (uint64_t)info.f_bavail;
  898. }
  899. #endif
  900. char *os_generate_uuid(void)
  901. {
  902. uuid_t uuid;
  903. // 36 char UUID + NULL
  904. char *out = bmalloc(37);
  905. uuid_generate(uuid);
  906. uuid_unparse_lower(uuid, out);
  907. return out;
  908. }