Fix: remove nonblock flag of sessiond socket in consumer
[lttng-tools.git] / src / common / utils.c
CommitLineData
81b86775
DG
1/*
2 * Copyright (C) 2012 - David Goulet <dgoulet@efficios.com>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License, version 2 only, as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License along with
14 * this program; if not, write to the Free Software Foundation, Inc., 51
15 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 */
17
18#define _GNU_SOURCE
35f90c40 19#include <assert.h>
81b86775
DG
20#include <ctype.h>
21#include <fcntl.h>
22#include <limits.h>
23#include <stdlib.h>
24#include <string.h>
2d851108
DG
25#include <sys/types.h>
26#include <sys/stat.h>
27#include <unistd.h>
fe4477ee 28#include <inttypes.h>
70d0b120 29#include <regex.h>
81b86775
DG
30
31#include <common/common.h>
fe4477ee 32#include <common/runas.h>
81b86775
DG
33
34#include "utils.h"
35
36/*
37 * Return the realpath(3) of the path even if the last directory token does not
38 * exist. For example, with /tmp/test1/test2, if test2/ does not exist but the
39 * /tmp/test1 does, the real path is returned. In normal time, realpath(3)
40 * fails if the end point directory does not exist.
41 */
90e535ef 42LTTNG_HIDDEN
81b86775
DG
43char *utils_expand_path(const char *path)
44{
45 const char *end_path = path;
46 char *next, *cut_path = NULL, *expanded_path = NULL;
47
48 /* Safety net */
49 if (path == NULL) {
50 goto error;
51 }
52
53 /* Find last token delimited by '/' */
54 while ((next = strpbrk(end_path + 1, "/"))) {
55 end_path = next;
56 }
57
58 /* Cut last token from original path */
59 cut_path = strndup(path, end_path - path);
60
61 expanded_path = zmalloc(PATH_MAX);
62 if (expanded_path == NULL) {
63 PERROR("zmalloc expand path");
64 goto error;
65 }
66
67 expanded_path = realpath((char *)cut_path, expanded_path);
68 if (expanded_path == NULL) {
69 switch (errno) {
70 case ENOENT:
71 ERR("%s: No such file or directory", cut_path);
72 break;
73 default:
74 PERROR("realpath utils expand path");
75 break;
76 }
77 goto error;
78 }
79
80 /* Add end part to expanded path */
c30ce0b3 81 strncat(expanded_path, end_path, PATH_MAX - strlen(expanded_path) - 1);
81b86775
DG
82
83 free(cut_path);
84 return expanded_path;
85
86error:
87 free(expanded_path);
88 free(cut_path);
89 return NULL;
90}
91
92/*
93 * Create a pipe in dst.
94 */
90e535ef 95LTTNG_HIDDEN
81b86775
DG
96int utils_create_pipe(int *dst)
97{
98 int ret;
99
100 if (dst == NULL) {
101 return -1;
102 }
103
104 ret = pipe(dst);
105 if (ret < 0) {
106 PERROR("create pipe");
107 }
108
109 return ret;
110}
111
112/*
113 * Create pipe and set CLOEXEC flag to both fd.
114 *
115 * Make sure the pipe opened by this function are closed at some point. Use
116 * utils_close_pipe().
117 */
90e535ef 118LTTNG_HIDDEN
81b86775
DG
119int utils_create_pipe_cloexec(int *dst)
120{
121 int ret, i;
122
123 if (dst == NULL) {
124 return -1;
125 }
126
127 ret = utils_create_pipe(dst);
128 if (ret < 0) {
129 goto error;
130 }
131
132 for (i = 0; i < 2; i++) {
133 ret = fcntl(dst[i], F_SETFD, FD_CLOEXEC);
134 if (ret < 0) {
135 PERROR("fcntl pipe cloexec");
136 goto error;
137 }
138 }
139
140error:
141 return ret;
142}
143
144/*
145 * Close both read and write side of the pipe.
146 */
90e535ef 147LTTNG_HIDDEN
81b86775
DG
148void utils_close_pipe(int *src)
149{
150 int i, ret;
151
152 if (src == NULL) {
153 return;
154 }
155
156 for (i = 0; i < 2; i++) {
157 /* Safety check */
158 if (src[i] < 0) {
159 continue;
160 }
161
162 ret = close(src[i]);
163 if (ret) {
164 PERROR("close pipe");
165 }
166 }
167}
a4b92340
DG
168
169/*
170 * Create a new string using two strings range.
171 */
90e535ef 172LTTNG_HIDDEN
a4b92340
DG
173char *utils_strdupdelim(const char *begin, const char *end)
174{
175 char *str;
176
177 str = zmalloc(end - begin + 1);
178 if (str == NULL) {
179 PERROR("zmalloc strdupdelim");
180 goto error;
181 }
182
183 memcpy(str, begin, end - begin);
184 str[end - begin] = '\0';
185
186error:
187 return str;
188}
b662582b
DG
189
190/*
191 * Set CLOEXEC flag to the give file descriptor.
192 */
90e535ef 193LTTNG_HIDDEN
b662582b
DG
194int utils_set_fd_cloexec(int fd)
195{
196 int ret;
197
198 if (fd < 0) {
199 ret = -EINVAL;
200 goto end;
201 }
202
203 ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
204 if (ret < 0) {
205 PERROR("fcntl cloexec");
206 ret = -errno;
207 }
208
209end:
210 return ret;
211}
35f90c40
DG
212
213/*
214 * Create pid file to the given path and filename.
215 */
90e535ef 216LTTNG_HIDDEN
35f90c40
DG
217int utils_create_pid_file(pid_t pid, const char *filepath)
218{
219 int ret;
220 FILE *fp;
221
222 assert(filepath);
223
224 fp = fopen(filepath, "w");
225 if (fp == NULL) {
226 PERROR("open pid file %s", filepath);
227 ret = -1;
228 goto error;
229 }
230
231 ret = fprintf(fp, "%d\n", pid);
232 if (ret < 0) {
233 PERROR("fprintf pid file");
234 }
235
236 fclose(fp);
237 DBG("Pid %d written in file %s", pid, filepath);
238error:
239 return ret;
240}
2d851108
DG
241
242/*
243 * Recursively create directory using the given path and mode.
244 *
245 * On success, return 0 else a negative error code.
246 */
90e535ef 247LTTNG_HIDDEN
2d851108
DG
248int utils_mkdir_recursive(const char *path, mode_t mode)
249{
250 char *p, tmp[PATH_MAX];
251 struct stat statbuf;
252 size_t len;
253 int ret;
254
255 assert(path);
256
257 ret = snprintf(tmp, sizeof(tmp), "%s", path);
258 if (ret < 0) {
259 PERROR("snprintf mkdir");
260 goto error;
261 }
262
263 len = ret;
264 if (tmp[len - 1] == '/') {
265 tmp[len - 1] = 0;
266 }
267
268 for (p = tmp + 1; *p; p++) {
269 if (*p == '/') {
270 *p = 0;
271 if (tmp[strlen(tmp) - 1] == '.' &&
272 tmp[strlen(tmp) - 2] == '.' &&
273 tmp[strlen(tmp) - 3] == '/') {
274 ERR("Using '/../' is not permitted in the trace path (%s)",
275 tmp);
276 ret = -1;
277 goto error;
278 }
279 ret = stat(tmp, &statbuf);
280 if (ret < 0) {
281 ret = mkdir(tmp, mode);
282 if (ret < 0) {
283 if (errno != EEXIST) {
284 PERROR("mkdir recursive");
285 ret = -errno;
286 goto error;
287 }
288 }
289 }
290 *p = '/';
291 }
292 }
293
294 ret = mkdir(tmp, mode);
295 if (ret < 0) {
296 if (errno != EEXIST) {
297 PERROR("mkdir recursive last piece");
298 ret = -errno;
299 } else {
300 ret = 0;
301 }
302 }
303
304error:
305 return ret;
306}
fe4477ee
JD
307
308/*
309 * Create the stream tracefile on disk.
310 *
311 * Return 0 on success or else a negative value.
312 */
bc182241 313LTTNG_HIDDEN
fe4477ee
JD
314int utils_create_stream_file(char *path_name, char *file_name, uint64_t size,
315 uint64_t count, int uid, int gid)
316{
be96a7d1 317 int ret, out_fd, flags, mode;
fe4477ee
JD
318 char full_path[PATH_MAX], *path_name_id = NULL, *path;
319
320 assert(path_name);
321 assert(file_name);
322
323 ret = snprintf(full_path, sizeof(full_path), "%s/%s",
324 path_name, file_name);
325 if (ret < 0) {
326 PERROR("snprintf create output file");
327 goto error;
328 }
329
330 /*
331 * If we split the trace in multiple files, we have to add the count at the
332 * end of the tracefile name
333 */
334 if (size > 0) {
335 ret = asprintf(&path_name_id, "%s_%" PRIu64, full_path, count);
336 if (ret < 0) {
337 PERROR("Allocating path name ID");
338 goto error;
339 }
340 path = path_name_id;
341 } else {
342 path = full_path;
343 }
344
be96a7d1 345 flags = O_WRONLY | O_CREAT | O_TRUNC;
0f907de1 346 /* Open with 660 mode */
be96a7d1
DG
347 mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
348
349 if (uid < 0 || gid < 0) {
350 out_fd = open(path, flags, mode);
351 } else {
352 out_fd = run_as_open(path, flags, mode, uid, gid);
353 }
fe4477ee
JD
354 if (out_fd < 0) {
355 PERROR("open stream path %s", path);
356 goto error_open;
357 }
358 ret = out_fd;
359
360error_open:
361 free(path_name_id);
362error:
363 return ret;
364}
365
366/*
367 * Change the output tracefile according to the given size and count The
368 * new_count pointer is set during this operation.
369 *
370 * From the consumer, the stream lock MUST be held before calling this function
371 * because we are modifying the stream status.
372 *
373 * Return 0 on success or else a negative value.
374 */
bc182241 375LTTNG_HIDDEN
fe4477ee
JD
376int utils_rotate_stream_file(char *path_name, char *file_name, uint64_t size,
377 uint64_t count, int uid, int gid, int out_fd, uint64_t *new_count)
378{
379 int ret;
380
381 ret = close(out_fd);
382 if (ret < 0) {
383 PERROR("Closing tracefile");
384 goto error;
385 }
386
387 if (count > 0) {
388 *new_count = (*new_count + 1) % count;
389 } else {
390 (*new_count)++;
391 }
392
393 return utils_create_stream_file(path_name, file_name, size, *new_count,
394 uid, gid);
395error:
396 return ret;
397}
70d0b120
SM
398
399/**
400 * Prints the error message corresponding to a regex error code.
401 *
402 * @param errcode The error code.
403 * @param regex The regex object that produced the error code.
404 */
405static void regex_print_error(int errcode, regex_t *regex)
406{
407 /* Get length of error message and allocate accordingly */
408 size_t length;
409 char *buffer;
410
411 assert(regex != NULL);
412
413 length = regerror(errcode, regex, NULL, 0);
414 if (length == 0) {
415 ERR("regerror returned a length of 0");
416 return;
417 }
418
419 buffer = zmalloc(length);
420 if (!buffer) {
421 ERR("regex_print_error: zmalloc failed");
422 return;
423 }
424
425 /* Get and print error message */
426 regerror(errcode, regex, buffer, length);
427 ERR("regex error: %s\n", buffer);
428 free(buffer);
429
430}
431
432/**
433 * Parse a string that represents a size in human readable format. It
434 * supports decimal integers suffixed by 'k', 'M' or 'G'.
435 *
436 * The suffix multiply the integer by:
437 * 'k': 1024
438 * 'M': 1024^2
439 * 'G': 1024^3
440 *
441 * @param str The string to parse.
442 * @param size Pointer to a size_t that will be filled with the
443 * resulting size.
444 *
445 * @return 0 on success, -1 on failure.
446 */
447int utils_parse_size_suffix(char *str, uint64_t *size)
448{
449 regex_t regex;
450 int ret;
451 const int nmatch = 3;
452 regmatch_t suffix_match, matches[nmatch];
453 unsigned long long base_size;
454 long shift = 0;
455
456 if (!str) {
457 return 0;
458 }
459
460 /* Compile regex */
461 ret = regcomp(&regex, "^\\(0x\\)\\{0,1\\}[0-9][0-9]*\\([kKMG]\\{0,1\\}\\)$", 0);
462 if (ret != 0) {
463 regex_print_error(ret, &regex);
464 ret = -1;
465 goto end;
466 }
467
468 /* Match regex */
469 ret = regexec(&regex, str, nmatch, matches, 0);
470 if (ret != 0) {
471 ret = -1;
472 goto free;
473 }
474
475 /* There is a match ! */
476 errno = 0;
477 base_size = strtoull(str, NULL, 0);
478 if (errno != 0) {
479 PERROR("strtoull");
480 ret = -1;
481 goto free;
482 }
483
484 /* Check if there is a suffix */
485 suffix_match = matches[2];
486 if (suffix_match.rm_eo - suffix_match.rm_so == 1) {
487 switch (*(str + suffix_match.rm_so)) {
488 case 'K':
489 case 'k':
490 shift = KIBI_LOG2;
491 break;
492 case 'M':
493 shift = MEBI_LOG2;
494 break;
495 case 'G':
496 shift = GIBI_LOG2;
497 break;
498 default:
499 ERR("parse_human_size: invalid suffix");
500 ret = -1;
501 goto free;
502 }
503 }
504
505 *size = base_size << shift;
506
507 /* Check for overflow */
508 if ((*size >> shift) != base_size) {
509 ERR("parse_size_suffix: oops, overflow detected.");
510 ret = -1;
511 goto free;
512 }
513
514 ret = 0;
515
516free:
517 regfree(&regex);
518end:
519 return ret;
520}
This page took 0.0729 seconds and 4 git commands to generate.