Tests: Add test_utils_resolve_relative to unit tests
[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 25#include <sys/stat.h>
0c7bcad5 26#include <sys/types.h>
2d851108 27#include <unistd.h>
fe4477ee 28#include <inttypes.h>
70d0b120 29#include <regex.h>
6c71277b 30#include <grp.h>
81b86775
DG
31
32#include <common/common.h>
fe4477ee 33#include <common/runas.h>
81b86775
DG
34
35#include "utils.h"
feb0f3e5 36#include "defaults.h"
81b86775 37
a2e78ff0
RB
38/*
39 * Resolve the './' and '../' strings in the middle of a path using
40 * our very own way to do it, so that it works even if the directory
41 * does not exist
42 */
43LTTNG_HIDDEN
44char *utils_resolve_relative(const char *path)
45{
46 char *next, *previous, *slash, *start_path, *absolute_path = NULL;
47
48 /* Safety net */
49 if (path == NULL) {
50 goto error;
51 }
52
53 /* Allocate memory for the absolute path */
54 absolute_path = zmalloc(PATH_MAX);
55 if (absolute_path == NULL) {
56 PERROR("zmalloc expand path");
57 goto error;
58 }
59
60 /* Copy the path in the absolute path */
61 strncpy(absolute_path, path, PATH_MAX);
62
63 /* As long as we find '/./' in the path string */
64 while ((next = strstr(absolute_path, "/./"))) {
65
66 /* We prepare the start_path not containing it */
67 start_path = strndup(absolute_path, next - absolute_path);
68
69 /* And we concatenate it with the part after this string */
70 snprintf(absolute_path, PATH_MAX, "%s%s", start_path, next + 2);
71
72 free(start_path);
73 }
74
75 /* As long as we find '/../' in the path string */
76 while ((next = strstr(absolute_path, "/../"))) {
77 /* If the path starts with '/../', there's a problem */
78 if (next == absolute_path) {
79 ERR("%s: Path cannot be resolved", path);
80 goto error;
81 }
82
83 /* We find the last level of directory */
84 previous = absolute_path;
85 while ((slash = strpbrk(previous + 1, "/")) && slash != next) {
86 previous = slash;
87 }
88
89 /* Then we prepare the start_path not containing it */
90 start_path = strndup(absolute_path, previous - absolute_path);
91
92 /* And we concatenate it with the part after the '/../' */
93 snprintf(absolute_path, PATH_MAX, "%s%s", start_path, next + 3);
94
95 free(start_path);
96 }
97
98 return absolute_path;
99
100error:
101 free(absolute_path);
102 return NULL;
103}
104
105
81b86775
DG
106/*
107 * Return the realpath(3) of the path even if the last directory token does not
108 * exist. For example, with /tmp/test1/test2, if test2/ does not exist but the
109 * /tmp/test1 does, the real path is returned. In normal time, realpath(3)
110 * fails if the end point directory does not exist.
111 */
90e535ef 112LTTNG_HIDDEN
81b86775
DG
113char *utils_expand_path(const char *path)
114{
115 const char *end_path = path;
116 char *next, *cut_path = NULL, *expanded_path = NULL;
117
118 /* Safety net */
119 if (path == NULL) {
120 goto error;
121 }
122
123 /* Find last token delimited by '/' */
124 while ((next = strpbrk(end_path + 1, "/"))) {
125 end_path = next;
126 }
127
128 /* Cut last token from original path */
129 cut_path = strndup(path, end_path - path);
130
131 expanded_path = zmalloc(PATH_MAX);
132 if (expanded_path == NULL) {
133 PERROR("zmalloc expand path");
134 goto error;
135 }
136
137 expanded_path = realpath((char *)cut_path, expanded_path);
138 if (expanded_path == NULL) {
139 switch (errno) {
140 case ENOENT:
141 ERR("%s: No such file or directory", cut_path);
142 break;
143 default:
144 PERROR("realpath utils expand path");
145 break;
146 }
147 goto error;
148 }
149
150 /* Add end part to expanded path */
c30ce0b3 151 strncat(expanded_path, end_path, PATH_MAX - strlen(expanded_path) - 1);
81b86775
DG
152
153 free(cut_path);
154 return expanded_path;
155
156error:
157 free(expanded_path);
158 free(cut_path);
159 return NULL;
160}
161
162/*
163 * Create a pipe in dst.
164 */
90e535ef 165LTTNG_HIDDEN
81b86775
DG
166int utils_create_pipe(int *dst)
167{
168 int ret;
169
170 if (dst == NULL) {
171 return -1;
172 }
173
174 ret = pipe(dst);
175 if (ret < 0) {
176 PERROR("create pipe");
177 }
178
179 return ret;
180}
181
182/*
183 * Create pipe and set CLOEXEC flag to both fd.
184 *
185 * Make sure the pipe opened by this function are closed at some point. Use
186 * utils_close_pipe().
187 */
90e535ef 188LTTNG_HIDDEN
81b86775
DG
189int utils_create_pipe_cloexec(int *dst)
190{
191 int ret, i;
192
193 if (dst == NULL) {
194 return -1;
195 }
196
197 ret = utils_create_pipe(dst);
198 if (ret < 0) {
199 goto error;
200 }
201
202 for (i = 0; i < 2; i++) {
203 ret = fcntl(dst[i], F_SETFD, FD_CLOEXEC);
204 if (ret < 0) {
205 PERROR("fcntl pipe cloexec");
206 goto error;
207 }
208 }
209
210error:
211 return ret;
212}
213
094f381c
MD
214/*
215 * Create pipe and set fd flags to FD_CLOEXEC and O_NONBLOCK.
216 *
217 * Make sure the pipe opened by this function are closed at some point. Use
218 * utils_close_pipe(). Using pipe() and fcntl rather than pipe2() to
219 * support OSes other than Linux 2.6.23+.
220 */
221LTTNG_HIDDEN
222int utils_create_pipe_cloexec_nonblock(int *dst)
223{
224 int ret, i;
225
226 if (dst == NULL) {
227 return -1;
228 }
229
230 ret = utils_create_pipe(dst);
231 if (ret < 0) {
232 goto error;
233 }
234
235 for (i = 0; i < 2; i++) {
236 ret = fcntl(dst[i], F_SETFD, FD_CLOEXEC);
237 if (ret < 0) {
238 PERROR("fcntl pipe cloexec");
239 goto error;
240 }
241 /*
242 * Note: we override any flag that could have been
243 * previously set on the fd.
244 */
245 ret = fcntl(dst[i], F_SETFL, O_NONBLOCK);
246 if (ret < 0) {
247 PERROR("fcntl pipe nonblock");
248 goto error;
249 }
250 }
251
252error:
253 return ret;
254}
255
81b86775
DG
256/*
257 * Close both read and write side of the pipe.
258 */
90e535ef 259LTTNG_HIDDEN
81b86775
DG
260void utils_close_pipe(int *src)
261{
262 int i, ret;
263
264 if (src == NULL) {
265 return;
266 }
267
268 for (i = 0; i < 2; i++) {
269 /* Safety check */
270 if (src[i] < 0) {
271 continue;
272 }
273
274 ret = close(src[i]);
275 if (ret) {
276 PERROR("close pipe");
277 }
278 }
279}
a4b92340
DG
280
281/*
282 * Create a new string using two strings range.
283 */
90e535ef 284LTTNG_HIDDEN
a4b92340
DG
285char *utils_strdupdelim(const char *begin, const char *end)
286{
287 char *str;
288
289 str = zmalloc(end - begin + 1);
290 if (str == NULL) {
291 PERROR("zmalloc strdupdelim");
292 goto error;
293 }
294
295 memcpy(str, begin, end - begin);
296 str[end - begin] = '\0';
297
298error:
299 return str;
300}
b662582b
DG
301
302/*
303 * Set CLOEXEC flag to the give file descriptor.
304 */
90e535ef 305LTTNG_HIDDEN
b662582b
DG
306int utils_set_fd_cloexec(int fd)
307{
308 int ret;
309
310 if (fd < 0) {
311 ret = -EINVAL;
312 goto end;
313 }
314
315 ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
316 if (ret < 0) {
317 PERROR("fcntl cloexec");
318 ret = -errno;
319 }
320
321end:
322 return ret;
323}
35f90c40
DG
324
325/*
326 * Create pid file to the given path and filename.
327 */
90e535ef 328LTTNG_HIDDEN
35f90c40
DG
329int utils_create_pid_file(pid_t pid, const char *filepath)
330{
331 int ret;
332 FILE *fp;
333
334 assert(filepath);
335
336 fp = fopen(filepath, "w");
337 if (fp == NULL) {
338 PERROR("open pid file %s", filepath);
339 ret = -1;
340 goto error;
341 }
342
343 ret = fprintf(fp, "%d\n", pid);
344 if (ret < 0) {
345 PERROR("fprintf pid file");
346 }
347
348 fclose(fp);
349 DBG("Pid %d written in file %s", pid, filepath);
350error:
351 return ret;
352}
2d851108
DG
353
354/*
355 * Recursively create directory using the given path and mode.
356 *
357 * On success, return 0 else a negative error code.
358 */
90e535ef 359LTTNG_HIDDEN
2d851108
DG
360int utils_mkdir_recursive(const char *path, mode_t mode)
361{
362 char *p, tmp[PATH_MAX];
2d851108
DG
363 size_t len;
364 int ret;
365
366 assert(path);
367
368 ret = snprintf(tmp, sizeof(tmp), "%s", path);
369 if (ret < 0) {
370 PERROR("snprintf mkdir");
371 goto error;
372 }
373
374 len = ret;
375 if (tmp[len - 1] == '/') {
376 tmp[len - 1] = 0;
377 }
378
379 for (p = tmp + 1; *p; p++) {
380 if (*p == '/') {
381 *p = 0;
382 if (tmp[strlen(tmp) - 1] == '.' &&
383 tmp[strlen(tmp) - 2] == '.' &&
384 tmp[strlen(tmp) - 3] == '/') {
385 ERR("Using '/../' is not permitted in the trace path (%s)",
386 tmp);
387 ret = -1;
388 goto error;
389 }
0c7bcad5 390 ret = mkdir(tmp, mode);
2d851108 391 if (ret < 0) {
0c7bcad5
MD
392 if (errno != EEXIST) {
393 PERROR("mkdir recursive");
394 ret = -errno;
395 goto error;
2d851108
DG
396 }
397 }
398 *p = '/';
399 }
400 }
401
402 ret = mkdir(tmp, mode);
403 if (ret < 0) {
404 if (errno != EEXIST) {
405 PERROR("mkdir recursive last piece");
406 ret = -errno;
407 } else {
408 ret = 0;
409 }
410 }
411
412error:
413 return ret;
414}
fe4477ee
JD
415
416/*
417 * Create the stream tracefile on disk.
418 *
419 * Return 0 on success or else a negative value.
420 */
bc182241 421LTTNG_HIDDEN
07b86b52 422int utils_create_stream_file(const char *path_name, char *file_name, uint64_t size,
309167d2 423 uint64_t count, int uid, int gid, char *suffix)
fe4477ee 424{
be96a7d1 425 int ret, out_fd, flags, mode;
309167d2
JD
426 char full_path[PATH_MAX], *path_name_suffix = NULL, *path;
427 char *extra = NULL;
fe4477ee
JD
428
429 assert(path_name);
430 assert(file_name);
431
432 ret = snprintf(full_path, sizeof(full_path), "%s/%s",
433 path_name, file_name);
434 if (ret < 0) {
435 PERROR("snprintf create output file");
436 goto error;
437 }
438
309167d2
JD
439 /* Setup extra string if suffix or/and a count is needed. */
440 if (size > 0 && suffix) {
441 ret = asprintf(&extra, "_%" PRIu64 "%s", count, suffix);
442 } else if (size > 0) {
443 ret = asprintf(&extra, "_%" PRIu64, count);
444 } else if (suffix) {
445 ret = asprintf(&extra, "%s", suffix);
446 }
447 if (ret < 0) {
448 PERROR("Allocating extra string to name");
449 goto error;
450 }
451
fe4477ee
JD
452 /*
453 * If we split the trace in multiple files, we have to add the count at the
454 * end of the tracefile name
455 */
309167d2
JD
456 if (extra) {
457 ret = asprintf(&path_name_suffix, "%s%s", full_path, extra);
fe4477ee 458 if (ret < 0) {
309167d2
JD
459 PERROR("Allocating path name with extra string");
460 goto error_free_suffix;
fe4477ee 461 }
309167d2 462 path = path_name_suffix;
fe4477ee
JD
463 } else {
464 path = full_path;
465 }
466
be96a7d1 467 flags = O_WRONLY | O_CREAT | O_TRUNC;
0f907de1 468 /* Open with 660 mode */
be96a7d1
DG
469 mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
470
471 if (uid < 0 || gid < 0) {
472 out_fd = open(path, flags, mode);
473 } else {
474 out_fd = run_as_open(path, flags, mode, uid, gid);
475 }
fe4477ee
JD
476 if (out_fd < 0) {
477 PERROR("open stream path %s", path);
478 goto error_open;
479 }
480 ret = out_fd;
481
482error_open:
309167d2
JD
483 free(path_name_suffix);
484error_free_suffix:
485 free(extra);
fe4477ee
JD
486error:
487 return ret;
488}
489
490/*
491 * Change the output tracefile according to the given size and count The
492 * new_count pointer is set during this operation.
493 *
494 * From the consumer, the stream lock MUST be held before calling this function
495 * because we are modifying the stream status.
496 *
497 * Return 0 on success or else a negative value.
498 */
bc182241 499LTTNG_HIDDEN
fe4477ee 500int utils_rotate_stream_file(char *path_name, char *file_name, uint64_t size,
309167d2
JD
501 uint64_t count, int uid, int gid, int out_fd, uint64_t *new_count,
502 int *stream_fd)
fe4477ee
JD
503{
504 int ret;
505
309167d2
JD
506 assert(new_count);
507 assert(stream_fd);
508
fe4477ee
JD
509 ret = close(out_fd);
510 if (ret < 0) {
511 PERROR("Closing tracefile");
512 goto error;
513 }
514
515 if (count > 0) {
516 *new_count = (*new_count + 1) % count;
517 } else {
518 (*new_count)++;
519 }
520
309167d2
JD
521 ret = utils_create_stream_file(path_name, file_name, size, *new_count,
522 uid, gid, 0);
523 if (ret < 0) {
524 goto error;
525 }
526 *stream_fd = ret;
527
528 /* Success. */
529 ret = 0;
530
fe4477ee
JD
531error:
532 return ret;
533}
70d0b120
SM
534
535/**
536 * Prints the error message corresponding to a regex error code.
537 *
538 * @param errcode The error code.
539 * @param regex The regex object that produced the error code.
540 */
541static void regex_print_error(int errcode, regex_t *regex)
542{
543 /* Get length of error message and allocate accordingly */
544 size_t length;
545 char *buffer;
546
547 assert(regex != NULL);
548
549 length = regerror(errcode, regex, NULL, 0);
550 if (length == 0) {
551 ERR("regerror returned a length of 0");
552 return;
553 }
554
555 buffer = zmalloc(length);
556 if (!buffer) {
557 ERR("regex_print_error: zmalloc failed");
558 return;
559 }
560
561 /* Get and print error message */
562 regerror(errcode, regex, buffer, length);
563 ERR("regex error: %s\n", buffer);
564 free(buffer);
565
566}
567
568/**
569 * Parse a string that represents a size in human readable format. It
570 * supports decimal integers suffixed by 'k', 'M' or 'G'.
571 *
572 * The suffix multiply the integer by:
573 * 'k': 1024
574 * 'M': 1024^2
575 * 'G': 1024^3
576 *
577 * @param str The string to parse.
578 * @param size Pointer to a size_t that will be filled with the
cfa9a5a2 579 * resulting size.
70d0b120
SM
580 *
581 * @return 0 on success, -1 on failure.
582 */
00a52467 583LTTNG_HIDDEN
70d0b120
SM
584int utils_parse_size_suffix(char *str, uint64_t *size)
585{
586 regex_t regex;
587 int ret;
588 const int nmatch = 3;
589 regmatch_t suffix_match, matches[nmatch];
590 unsigned long long base_size;
591 long shift = 0;
592
593 if (!str) {
594 return 0;
595 }
596
597 /* Compile regex */
598 ret = regcomp(&regex, "^\\(0x\\)\\{0,1\\}[0-9][0-9]*\\([kKMG]\\{0,1\\}\\)$", 0);
599 if (ret != 0) {
600 regex_print_error(ret, &regex);
601 ret = -1;
602 goto end;
603 }
604
605 /* Match regex */
606 ret = regexec(&regex, str, nmatch, matches, 0);
607 if (ret != 0) {
608 ret = -1;
609 goto free;
610 }
611
612 /* There is a match ! */
613 errno = 0;
614 base_size = strtoull(str, NULL, 0);
615 if (errno != 0) {
616 PERROR("strtoull");
617 ret = -1;
618 goto free;
619 }
620
621 /* Check if there is a suffix */
622 suffix_match = matches[2];
623 if (suffix_match.rm_eo - suffix_match.rm_so == 1) {
624 switch (*(str + suffix_match.rm_so)) {
625 case 'K':
626 case 'k':
627 shift = KIBI_LOG2;
628 break;
629 case 'M':
630 shift = MEBI_LOG2;
631 break;
632 case 'G':
633 shift = GIBI_LOG2;
634 break;
635 default:
636 ERR("parse_human_size: invalid suffix");
637 ret = -1;
638 goto free;
639 }
640 }
641
642 *size = base_size << shift;
643
644 /* Check for overflow */
645 if ((*size >> shift) != base_size) {
646 ERR("parse_size_suffix: oops, overflow detected.");
647 ret = -1;
648 goto free;
649 }
650
651 ret = 0;
652
653free:
654 regfree(&regex);
655end:
656 return ret;
657}
cfa9a5a2
DG
658
659/*
660 * fls: returns the position of the most significant bit.
661 * Returns 0 if no bit is set, else returns the position of the most
662 * significant bit (from 1 to 32 on 32-bit, from 1 to 64 on 64-bit).
663 */
664#if defined(__i386) || defined(__x86_64)
665static inline unsigned int fls_u32(uint32_t x)
666{
667 int r;
668
669 asm("bsrl %1,%0\n\t"
670 "jnz 1f\n\t"
671 "movl $-1,%0\n\t"
672 "1:\n\t"
673 : "=r" (r) : "rm" (x));
674 return r + 1;
675}
676#define HAS_FLS_U32
677#endif
678
679#ifndef HAS_FLS_U32
680static __attribute__((unused)) unsigned int fls_u32(uint32_t x)
681{
682 unsigned int r = 32;
683
684 if (!x) {
685 return 0;
686 }
687 if (!(x & 0xFFFF0000U)) {
688 x <<= 16;
689 r -= 16;
690 }
691 if (!(x & 0xFF000000U)) {
692 x <<= 8;
693 r -= 8;
694 }
695 if (!(x & 0xF0000000U)) {
696 x <<= 4;
697 r -= 4;
698 }
699 if (!(x & 0xC0000000U)) {
700 x <<= 2;
701 r -= 2;
702 }
703 if (!(x & 0x80000000U)) {
704 x <<= 1;
705 r -= 1;
706 }
707 return r;
708}
709#endif
710
711/*
712 * Return the minimum order for which x <= (1UL << order).
713 * Return -1 if x is 0.
714 */
715LTTNG_HIDDEN
716int utils_get_count_order_u32(uint32_t x)
717{
718 if (!x) {
719 return -1;
720 }
721
722 return fls_u32(x - 1);
723}
feb0f3e5
AM
724
725/**
726 * Obtain the value of LTTNG_HOME environment variable, if exists.
727 * Otherwise returns the value of HOME.
728 */
00a52467 729LTTNG_HIDDEN
feb0f3e5
AM
730char *utils_get_home_dir(void)
731{
732 char *val = NULL;
733 val = getenv(DEFAULT_LTTNG_HOME_ENV_VAR);
734 if (val != NULL) {
735 return val;
736 }
737 return getenv(DEFAULT_LTTNG_FALLBACK_HOME_ENV_VAR);
738}
26fe5938
DG
739
740/*
741 * With the given format, fill dst with the time of len maximum siz.
742 *
743 * Return amount of bytes set in the buffer or else 0 on error.
744 */
745LTTNG_HIDDEN
746size_t utils_get_current_time_str(const char *format, char *dst, size_t len)
747{
748 size_t ret;
749 time_t rawtime;
750 struct tm *timeinfo;
751
752 assert(format);
753 assert(dst);
754
755 /* Get date and time for session path */
756 time(&rawtime);
757 timeinfo = localtime(&rawtime);
758 ret = strftime(dst, len, format, timeinfo);
759 if (ret == 0) {
760 ERR("Unable to strftime with format %s at dst %p of len %lu", format,
761 dst, len);
762 }
763
764 return ret;
765}
6c71277b
MD
766
767/*
768 * Return the group ID matching name, else 0 if it cannot be found.
769 */
770LTTNG_HIDDEN
771gid_t utils_get_group_id(const char *name)
772{
773 struct group *grp;
774
775 grp = getgrnam(name);
776 if (!grp) {
777 static volatile int warn_once;
778
779 if (!warn_once) {
780 WARN("No tracing group detected");
781 warn_once = 1;
782 }
783 return 0;
784 }
785 return grp->gr_gid;
786}
This page took 0.096789 seconds and 4 git commands to generate.