master
c 129 lines 4.02 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "run_dir.h"
4 #include "libnetdata/libnetdata.h"
5
6 static char *cached_run_dir = NULL;
7 static SPINLOCK spinlock = SPINLOCK_INITIALIZER;
8
9 static inline bool is_dir_accessible(const char *dir, bool rw) {
10 struct stat st;
11 if (stat(dir, &st) == -1)
12 return false;
13
14 if (!S_ISDIR(st.st_mode))
15 return false;
16
17 // Check if we can write to the directory
18 if (access(dir, rw ? W_OK : R_OK) == -1)
19 return false;
20
21 return true;
22 }
23
24 static inline bool netdata_dir_in_parent(const char *parent, char *out_path, size_t out_path_len, bool rw) {
25 if (!is_dir_accessible(parent, rw))
26 return false;
27
28 snprintfz(out_path, out_path_len, "%s/netdata", parent);
29 if (mkdir(out_path, 0755) == -1 && errno != EEXIST)
30 return false;
31
32 return is_dir_accessible(out_path, rw);
33 }
34
35 static char *detect_run_dir(bool rw) {
36 char path[FILENAME_MAX + 1];
37
38 if(!rw) {
39 // First check for environment variable
40 const char *env_dir = getenv("NETDATA_RUN_DIR");
41 if (env_dir && *env_dir) {
42 if (is_dir_accessible(env_dir, rw))
43 return strdupz(env_dir);
44 }
45 }
46
47 #if defined(OS_LINUX)
48 // First try /run/netdata
49 if (netdata_dir_in_parent("/run", path, sizeof(path), rw))
50 goto success;
51 #endif
52
53 #if defined(OS_MACOS)
54 // macOS typically uses /private/var/run
55 if (netdata_dir_in_parent("/private/var/run", path, sizeof(path), rw))
56 goto success;
57 #endif
58
59 #if defined(OS_LINUX) || defined(OS_FREEBSD) || defined(OS_MACOS)
60 // Then try /var/run/netdata
61 if (netdata_dir_in_parent("/var/run", path, sizeof(path), rw))
62 goto success;
63 #endif
64
65 //#if defined(OS_WINDOWS)
66 // // On MSYS2/Cygwin get TEMP and convert it properly
67 // WCHAR temp_pathW[MAX_PATH];
68 // DWORD len = GetEnvironmentVariableW(L"TEMP", temp_pathW, MAX_PATH);
69 // if (len > 0 && len < MAX_PATH) {
70 // // Convert Windows wide path to UTF-8
71 // int utf8_len = WideCharToMultiByte(CP_UTF8, 0, temp_pathW, -1, NULL, 0, NULL, NULL);
72 // if (utf8_len > 0 && utf8_len < FILENAME_MAX) {
73 // char win_path[FILENAME_MAX + 1];
74 // if (WideCharToMultiByte(CP_UTF8, 0, temp_pathW, -1, win_path, sizeof(win_path), NULL, NULL)) {
75 // // Convert Windows path to Unix path using Cygwin API
76 // ssize_t unix_size = cygwin_conv_path(CCP_WIN_A_TO_POSIX, win_path, NULL, 0);
77 // if (unix_size > 0) {
78 // char unix_path[FILENAME_MAX + 1];
79 // if (cygwin_conv_path(CCP_WIN_A_TO_POSIX, win_path, unix_path, sizeof(unix_path)) == 0) {
80 // if (is_dir_accessible(unix_path, rw)) {
81 // snprintfz(path, sizeof(path), "%s/netdata", unix_path);
82 // if (!rw)
83 // goto success;
84 //
85 // if (mkdir(path, 0755) == 0 || errno == EEXIST)
86 // goto success;
87 // }
88 // }
89 // }
90 // }
91 // }
92 // }
93 //#endif
94
95 // Fallback to /tmp/netdata - force creation if needed
96 if (!is_dir_accessible("/tmp", rw)) {
97 // Try to create /tmp with standard permissions (including sticky bit)
98 if (rw && mkdir("/tmp", 01777) == -1 && errno != EEXIST)
99 return NULL;
100 }
101
102 snprintfz(path, sizeof(path), "/tmp/netdata");
103 if (rw && mkdir(path, 0755) == -1 && errno != EEXIST)
104 return NULL;
105
106 success:
107 // Set the environment variable for child processes
108 if(rw)
109 nd_setenv("NETDATA_RUN_DIR", path, 1);
110
111 return strdupz(path);
112 }
113
114 const char *os_run_dir(bool rw) {
115 // Fast path - return cached directory if available
116 if(cached_run_dir)
117 return cached_run_dir;
118
119 spinlock_lock(&spinlock);
120
121 // Check again under lock in case another thread set it
122 if(!cached_run_dir)
123 cached_run_dir = detect_run_dir(rw);
124
125 spinlock_unlock(&spinlock);
126
127 errno_clear();
128 return cached_run_dir;
129 }