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