]> git.proxmox.com Git - pve-cluster.git/blob - data/src/cfs-utils.c
use qblog instead of syslog
[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 char *ltxt;
121
122 switch (log_level & G_LOG_LEVEL_MASK) {
123 case G_LOG_LEVEL_ERROR:
124 level=LOG_ERR;
125 ltxt = "error";
126 break;
127 case G_LOG_LEVEL_CRITICAL:
128 level=LOG_CRIT;
129 ltxt = "critical";
130 break;
131 case G_LOG_LEVEL_WARNING:
132 level=LOG_WARNING;
133 ltxt = "warning";
134 break;
135 case G_LOG_LEVEL_MESSAGE:
136 level=LOG_NOTICE;
137 ltxt = "notice";
138 break;
139 case G_LOG_LEVEL_INFO:
140 level=LOG_INFO;
141 ltxt = "info";
142 break;
143 case G_LOG_LEVEL_DEBUG:
144 level=LOG_DEBUG;
145 ltxt = "debug";
146 if (!cfs.debug)
147 return;
148 break;
149 default:
150 level=LOG_INFO;
151 ltxt = "info";
152 }
153
154 va_list args;
155
156 va_start (args, format);
157 char *orgmsg = g_strdup_vprintf (format, args);
158 va_end (args);
159
160 char msg[8192];
161 utf8_to_ascii(msg, sizeof(msg), orgmsg, FALSE);
162
163 uint32_t tag = g_quark_from_string(log_domain);
164
165 qb_log_from_external_source(func, file, "%s", level, line, tag, msg);
166
167 g_free(orgmsg);
168 }
169
170 // xml parser for cluster.conf - just good enough to extract version
171
172 typedef struct
173 {
174 guint64 version;
175 } PVEClusterConfig;
176
177 static void
178 parser_start_element (
179 GMarkupParseContext *context,
180 const gchar *element_name,
181 const gchar **attribute_names,
182 const gchar **attribute_values,
183 gpointer user_data,
184 GError **error)
185 {
186 PVEClusterConfig *data = user_data;
187
188 if (!data->version && !strcmp(element_name, "cluster")) {
189 const char **n = attribute_names;
190 const char **v = attribute_values;
191
192 while (n && v && *n) {
193 if (!strcmp(*n, "config_version")) {
194 char *e = NULL;
195 guint64 ver = strtoull(*v, &e, 10);
196 if (e)
197 data->version = ver;
198 }
199 ++n;
200 ++v;
201 }
202 }
203 }
204
205 static GMarkupParser cluster_conf_parser = {
206 .start_element = parser_start_element
207 };
208
209 guint64
210 cluster_config_version(
211 const gpointer config_data,
212 gsize config_length)
213 {
214 GMarkupParseContext *ctx;
215
216 GError *err = NULL;
217
218 PVEClusterConfig cfg = { .version = 0 };
219 if (!(ctx = g_markup_parse_context_new(&cluster_conf_parser, 0, &cfg, NULL))) {
220 cfs_critical("g_markup_parse_context_new failed");
221 return 0;
222 }
223
224 if (!g_markup_parse_context_parse(ctx, config_data, config_length, &err)) {
225 cfs_critical("unable to parse cluster config - %s", err->message);
226 g_error_free (err);
227 g_markup_parse_context_free(ctx);
228 return cfg.version;
229 }
230
231 if (!g_markup_parse_context_end_parse(ctx, &err)) {
232 cfs_critical("unable to parse cluster config - %s", err->message);
233 g_error_free (err);
234 g_markup_parse_context_free(ctx);
235 return cfg.version;
236 }
237
238 g_markup_parse_context_free(ctx);
239 return cfg.version;
240 }
241
242 ssize_t
243 safe_read(
244 int fd,
245 char *buf,
246 size_t count)
247 {
248 ssize_t n;
249
250 do {
251 n = read(fd, buf, count);
252 } while (n < 0 && errno == EINTR);
253
254 return n;
255 }
256
257 gboolean
258 full_write(
259 int fd,
260 const char *buf,
261 size_t len)
262 {
263 size_t total;
264
265 total = 0;
266
267 while (len > 0) {
268 ssize_t n;
269 do {
270 n = write(fd, buf, len);
271 } while (n < 0 && errno == EINTR);
272
273 if (n < 0)
274 break;
275
276 buf += n;
277 total += n;
278 len -= n;
279 }
280
281 return (len == 0);
282 }
283
284 gboolean
285 atomic_write_file(
286 const char *filename,
287 gconstpointer data,
288 size_t len,
289 mode_t mode,
290 gid_t gid)
291 {
292 g_return_val_if_fail(filename != NULL, FALSE);
293 g_return_val_if_fail(len == 0 || data != NULL, FALSE);
294
295 gboolean res = TRUE;
296
297 char *tmp_name = g_strdup_printf ("%s.XXXXXX", filename);
298 int fd = mkstemp(tmp_name);
299 if (fd == -1) {
300 cfs_critical("Failed to create file '%s': %s", tmp_name, g_strerror(errno));
301 res = FALSE;
302 goto ret;
303 }
304
305 if (fchown(fd, 0, gid) == -1) {
306 cfs_critical("Failed to change group of file '%s': %s", tmp_name, g_strerror(errno));
307 close(fd);
308 goto fail;
309 }
310
311 if (fchmod(fd, mode) == -1) {
312 cfs_critical("Failed to change mode of file '%s': %s", tmp_name, g_strerror(errno));
313 close(fd);
314 goto fail;
315 }
316
317 if (len && !full_write(fd, data, len)) {
318 cfs_critical("Failed to write file '%s': %s", tmp_name, g_strerror(errno));
319 close(fd);
320 goto fail;
321 }
322
323 if (close(fd) == -1) {
324 cfs_critical("Failed to close file '%s': %s", tmp_name, g_strerror(errno));
325 goto fail;
326 }
327
328 if (rename(tmp_name, filename) == -1) {
329 cfs_critical("Failed to rename file from '%s' to '%s': %s",
330 tmp_name, filename, g_strerror(errno));
331 goto fail;
332 }
333 ret:
334 g_free (tmp_name);
335
336 return res;
337
338 fail:
339 unlink(tmp_name);
340
341 res = FALSE;
342
343 goto ret;
344 }
345
346 gboolean
347 path_is_private(const char *path)
348 {
349 while (*path == '/') path++;
350
351 if ((strncmp(path, "priv", 4) == 0) && (path[4] == 0 || path[4] == '/')) {
352 return TRUE;
353 } else {
354 if (strncmp(path, "nodes/", 6) == 0) {
355 const char *tmp = path + 6;
356 while(*tmp && *tmp != '/') tmp++;
357 if (*tmp == '/' &&
358 (strncmp(tmp, "/priv", 5) == 0) &&
359 (tmp[5] == 0 || tmp[5] == '/')) {
360 return TRUE;
361 }
362 }
363 }
364 return FALSE;
365 }
366
367 gboolean
368 path_is_lockdir(const char *path)
369 {
370 while (*path == '/') path++;
371
372 return (strncmp(path, "priv/lock/", 10) == 0) && (strlen(path) > 10);
373 }