]> git.proxmox.com Git - pve-cluster.git/blob - data/src/cfs-utils.c
acf65b2c0655ce94f567fbec8f971c72a889cbea
[pve-cluster.git] / data / src / cfs-utils.c
1 /*
2 Copyright (C) 2010 Proxmox Server Solutions GmbH
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Affero General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Affero General Public License for more details.
13
14 You should have received a copy of the GNU Affero General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17 Author: Dietmar Maurer <dietmar@proxmox.com>
18
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif /* HAVE_CONFIG_H */
24
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <utime.h>
32 #include <sys/stat.h>
33 #include <glib.h>
34 #include <qb/qblog.h>
35
36 #include "cfs-utils.h"
37
38 static const char * hexchar = "0123456789abcdef";
39
40 /* convert utf8 to json and syslog compatible ascii */
41 void
42 utf8_to_ascii(
43 char *buf,
44 int bufsize,
45 const char *msg,
46 gboolean quotequote)
47 {
48 g_return_if_fail(buf != NULL);
49
50 *buf = 0;
51
52 g_return_if_fail(bufsize > 10);
53
54 const char *p = msg;
55 char *d = buf;
56 char *end = buf + bufsize - 7;
57
58 if (!g_utf8_validate(msg, -1, NULL)) {
59 while (*p && d < end) {
60 char c = *p++;
61 if (c == 34 && quotequote) {
62 *d++ = '\\';
63 *d++ = '"';
64 } else if (c >= 0 && c < 32) {
65 *d++ = '#';
66 *d++ = '0';
67 *(d+1) = hexchar[c % 10]; c = c / 10;
68 *d = hexchar[c % 10];
69 d += 2;
70 } else if (c >= 32 && c < 127) {
71 *d++ = c;
72 } else {
73 *d++ = '?';
74 }
75 }
76 *d = 0;
77 return;
78 }
79
80 while (*p && d < end) {
81 gunichar u = g_utf8_get_char(p);
82 if (u == 34 && quotequote) {
83 *d++ = '\\';
84 *d++ = '"';
85 } else if (u < 32 || u == 127) {
86 *d++ = '#';
87 *(d+2) = hexchar[u % 10]; u = u / 10;
88 *(d+1) = hexchar[u % 10]; u = u / 10;
89 *d = hexchar[u % 10];
90 d += 3;
91 } else if (u < 127) {
92 *d++ = u;
93 } else if (u < 65536) {
94 *d++ = '\\';
95 *d++ = 'u';
96 *(d+3) = hexchar[u&0xf]; u = u >> 4;
97 *(d+2) = hexchar[u&0xf]; u = u >> 4;
98 *(d+1) = hexchar[u&0xf]; u = u >> 4;
99 *d = hexchar[u&0xf];
100 d += 4;
101 } else {
102 /* we simply ignore this */
103 }
104 p = g_utf8_next_char(p);
105 }
106 *d = 0;
107 }
108
109 void
110 cfs_log(
111 const gchar *log_domain,
112 GLogLevelFlags log_level,
113 const char *file,
114 int line,
115 const char *func,
116 const gchar *format,
117 ...)
118 {
119 gint level;
120
121 switch (log_level & G_LOG_LEVEL_MASK) {
122 case G_LOG_LEVEL_ERROR:
123 level=LOG_ERR;
124 break;
125 case G_LOG_LEVEL_CRITICAL:
126 level=LOG_CRIT;
127 break;
128 case G_LOG_LEVEL_WARNING:
129 level=LOG_WARNING;
130 break;
131 case G_LOG_LEVEL_MESSAGE:
132 level=LOG_NOTICE;
133 break;
134 case G_LOG_LEVEL_INFO:
135 level=LOG_INFO;
136 break;
137 case G_LOG_LEVEL_DEBUG:
138 level=LOG_DEBUG;
139 if (!cfs.debug)
140 return;
141 break;
142 default:
143 level=LOG_INFO;
144 }
145
146 va_list args;
147
148 va_start (args, format);
149 char *orgmsg = g_strdup_vprintf (format, args);
150 va_end (args);
151
152 char msg[8192];
153 utf8_to_ascii(msg, sizeof(msg), orgmsg, FALSE);
154
155 uint32_t tag = g_quark_from_string(log_domain);
156
157 qb_log_from_external_source(func, file, "%s", level, line, tag, msg);
158
159 g_free(orgmsg);
160 }
161
162 guint64
163 cluster_config_version(
164 const gpointer config_data,
165 gsize config_length)
166 {
167 GRegex *regex;
168 GMatchInfo *match_info;
169 guint64 version = 0;
170
171 regex = g_regex_new ("config_version\\s*:\\s*(\\d+)", 0, 0, NULL);
172 g_regex_match_full(regex, config_data, config_length, 0, 0, &match_info, NULL);
173 if (g_match_info_matches (match_info)) {
174 gchar *word = g_match_info_fetch (match_info, 1);
175 if (strlen(word)) {
176 version = strtoull(word, NULL, 10);
177 }
178 g_free (word);
179 }
180 g_match_info_free (match_info);
181 g_regex_unref (regex);
182
183 return version;
184 }
185
186 ssize_t
187 safe_read(
188 int fd,
189 char *buf,
190 size_t count)
191 {
192 ssize_t n;
193
194 do {
195 n = read(fd, buf, count);
196 } while (n < 0 && errno == EINTR);
197
198 return n;
199 }
200
201 gboolean
202 full_write(
203 int fd,
204 const char *buf,
205 size_t len)
206 {
207 size_t total;
208
209 total = 0;
210
211 while (len > 0) {
212 ssize_t n;
213 do {
214 n = write(fd, buf, len);
215 } while (n < 0 && errno == EINTR);
216
217 if (n < 0)
218 break;
219
220 buf += n;
221 total += n;
222 len -= n;
223 }
224
225 return (len == 0);
226 }
227
228 gboolean
229 atomic_write_file(
230 const char *filename,
231 gconstpointer data,
232 size_t len,
233 mode_t mode,
234 gid_t gid)
235 {
236 g_return_val_if_fail(filename != NULL, FALSE);
237 g_return_val_if_fail(len == 0 || data != NULL, FALSE);
238
239 gboolean res = TRUE;
240
241 char *tmp_name = g_strdup_printf ("%s.XXXXXX", filename);
242 int fd = mkstemp(tmp_name);
243 if (fd == -1) {
244 cfs_critical("Failed to create file '%s': %s", tmp_name, g_strerror(errno));
245 res = FALSE;
246 goto ret;
247 }
248
249 if (fchown(fd, 0, gid) == -1) {
250 cfs_critical("Failed to change group of file '%s': %s", tmp_name, g_strerror(errno));
251 close(fd);
252 goto fail;
253 }
254
255 if (fchmod(fd, mode) == -1) {
256 cfs_critical("Failed to change mode of file '%s': %s", tmp_name, g_strerror(errno));
257 close(fd);
258 goto fail;
259 }
260
261 if (len && !full_write(fd, data, len)) {
262 cfs_critical("Failed to write file '%s': %s", tmp_name, g_strerror(errno));
263 close(fd);
264 goto fail;
265 }
266
267 if (close(fd) == -1) {
268 cfs_critical("Failed to close file '%s': %s", tmp_name, g_strerror(errno));
269 goto fail;
270 }
271
272 if (rename(tmp_name, filename) == -1) {
273 cfs_critical("Failed to rename file from '%s' to '%s': %s",
274 tmp_name, filename, g_strerror(errno));
275 goto fail;
276 }
277 ret:
278 g_free (tmp_name);
279
280 return res;
281
282 fail:
283 unlink(tmp_name);
284
285 res = FALSE;
286
287 goto ret;
288 }
289
290 gboolean
291 path_is_private(const char *path)
292 {
293 while (*path == '/') path++;
294
295 if ((strncmp(path, "priv", 4) == 0) && (path[4] == 0 || path[4] == '/')) {
296 return TRUE;
297 } else {
298 if (strncmp(path, "nodes/", 6) == 0) {
299 const char *tmp = path + 6;
300 while(*tmp && *tmp != '/') tmp++;
301 if (*tmp == '/' &&
302 (strncmp(tmp, "/priv", 5) == 0) &&
303 (tmp[5] == 0 || tmp[5] == '/')) {
304 return TRUE;
305 }
306 }
307 }
308 return FALSE;
309 }
310
311 gboolean
312 path_is_lxc_conf(const char *path)
313 {
314 while (*path == '/') path++;
315
316 if (strncmp(path, "nodes/", 6) == 0) {
317 const char *tmp = path + 6;
318 while(*tmp && *tmp != '/') tmp++;
319 if (*tmp == '/' &&
320 (strncmp(tmp, "/lxc", 4) == 0) &&
321 (tmp[4] == 0 || tmp[4] == '/')) {
322 return TRUE;
323 }
324 }
325
326 return FALSE;
327 }
328
329
330 gboolean
331 path_is_lockdir(const char *path)
332 {
333 while (*path == '/') path++;
334
335 return (strncmp(path, "priv/lock/", 10) == 0) && path[10];
336 }