vscode: Add configurations to run the executables under the debugger
[lttng-tools.git] / src / common / utils.cpp
CommitLineData
81b86775 1/*
ab5be9fa 2 * Copyright (C) 2012 David Goulet <dgoulet@efficios.com>
ab5be9fa 3 * Copyright (C) 2013 Jérémie Galarneau <jeremie.galarneau@efficios.com>
81b86775 4 *
c922647d 5 * SPDX-License-Identifier: LGPL-2.1-only
81b86775 6 *
81b86775
DG
7 */
8
6c1c0768 9#define _LGPL_SOURCE
28ab034a
JG
10#include "defaults.hpp"
11#include "time.hpp"
12#include "utils.hpp"
81b86775 13
c9e313bc
SM
14#include <common/common.hpp>
15#include <common/compat/directory-handle.hpp>
16#include <common/compat/dirent.hpp>
17#include <common/compat/getenv.hpp>
18#include <common/compat/string.hpp>
19#include <common/dynamic-buffer.hpp>
20#include <common/readwrite.hpp>
21#include <common/runas.hpp>
22#include <common/string-utils/format.hpp>
28ab034a 23
d7c23421 24#include <lttng/constant.h>
81b86775 25
28ab034a
JG
26#include <ctype.h>
27#include <fcntl.h>
28#include <grp.h>
29#include <inttypes.h>
30#include <limits.h>
31#include <pwd.h>
32#include <stdlib.h>
33#include <sys/file.h>
34#include <sys/stat.h>
35#include <sys/types.h>
36#include <unistd.h>
81b86775 37
28ab034a
JG
38#define PROC_MEMINFO_PATH "/proc/meminfo"
39#define PROC_MEMINFO_MEMAVAILABLE_LINE "MemAvailable:"
40#define PROC_MEMINFO_MEMTOTAL_LINE "MemTotal:"
09b72f7a 41
cd9adb8b 42/* The lnullptrh of the longest field of `/proc/meminfo`. */
28ab034a 43#define PROC_MEMINFO_FIELD_MAX_NAME_LEN 20
09b72f7a
FD
44
45#if (PROC_MEMINFO_FIELD_MAX_NAME_LEN == 20)
46#define MAX_NAME_LEN_SCANF_IS_A_BROKEN_API "19"
47#else
48#error MAX_NAME_LEN_SCANF_IS_A_BROKEN_API must be updated to match (PROC_MEMINFO_FIELD_MAX_NAME_LEN - 1)
49#endif
50
28ab034a 51#define FALLBACK_USER_BUFLEN 16384
159b042f
JG
52#define FALLBACK_GROUP_BUFLEN 16384
53
81b86775
DG
54/*
55 * Create a pipe in dst.
56 */
57int utils_create_pipe(int *dst)
58{
59 int ret;
60
cd9adb8b 61 if (dst == nullptr) {
81b86775
DG
62 return -1;
63 }
64
65 ret = pipe(dst);
66 if (ret < 0) {
67 PERROR("create pipe");
68 }
69
70 return ret;
71}
72
73/*
74 * Create pipe and set CLOEXEC flag to both fd.
75 *
76 * Make sure the pipe opened by this function are closed at some point. Use
77 * utils_close_pipe().
78 */
79int utils_create_pipe_cloexec(int *dst)
80{
81 int ret, i;
82
cd9adb8b 83 if (dst == nullptr) {
81b86775
DG
84 return -1;
85 }
86
87 ret = utils_create_pipe(dst);
88 if (ret < 0) {
89 goto error;
90 }
91
92 for (i = 0; i < 2; i++) {
93 ret = fcntl(dst[i], F_SETFD, FD_CLOEXEC);
94 if (ret < 0) {
95 PERROR("fcntl pipe cloexec");
96 goto error;
97 }
98 }
99
100error:
101 return ret;
102}
103
094f381c
MD
104/*
105 * Create pipe and set fd flags to FD_CLOEXEC and O_NONBLOCK.
106 *
107 * Make sure the pipe opened by this function are closed at some point. Use
108 * utils_close_pipe(). Using pipe() and fcntl rather than pipe2() to
109 * support OSes other than Linux 2.6.23+.
110 */
094f381c
MD
111int utils_create_pipe_cloexec_nonblock(int *dst)
112{
113 int ret, i;
114
cd9adb8b 115 if (dst == nullptr) {
094f381c
MD
116 return -1;
117 }
118
119 ret = utils_create_pipe(dst);
120 if (ret < 0) {
121 goto error;
122 }
123
124 for (i = 0; i < 2; i++) {
125 ret = fcntl(dst[i], F_SETFD, FD_CLOEXEC);
126 if (ret < 0) {
127 PERROR("fcntl pipe cloexec");
128 goto error;
129 }
130 /*
131 * Note: we override any flag that could have been
132 * previously set on the fd.
133 */
134 ret = fcntl(dst[i], F_SETFL, O_NONBLOCK);
135 if (ret < 0) {
136 PERROR("fcntl pipe nonblock");
137 goto error;
138 }
139 }
140
141error:
142 return ret;
143}
144
81b86775
DG
145/*
146 * Close both read and write side of the pipe.
147 */
148void utils_close_pipe(int *src)
149{
150 int i, ret;
151
cd9adb8b 152 if (src == nullptr) {
81b86775
DG
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 }
11f8d2f7 166 src[i] = -1;
81b86775
DG
167 }
168}
a4b92340
DG
169
170/*
171 * Create a new string using two strings range.
172 */
173char *utils_strdupdelim(const char *begin, const char *end)
174{
64803277 175 char *str = zmalloc<char>(end - begin + 1);
a4b92340 176
cd9adb8b 177 if (str == nullptr) {
a4b92340
DG
178 PERROR("zmalloc strdupdelim");
179 goto error;
180 }
181
182 memcpy(str, begin, end - begin);
183 str[end - begin] = '\0';
184
185error:
186 return str;
187}
b662582b
DG
188
189/*
190 * Set CLOEXEC flag to the give file descriptor.
191 */
b662582b
DG
192int utils_set_fd_cloexec(int fd)
193{
194 int ret;
195
196 if (fd < 0) {
197 ret = -EINVAL;
198 goto end;
199 }
200
201 ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
202 if (ret < 0) {
203 PERROR("fcntl cloexec");
204 ret = -errno;
205 }
206
207end:
208 return ret;
209}
35f90c40
DG
210
211/*
212 * Create pid file to the given path and filename.
213 */
35f90c40
DG
214int utils_create_pid_file(pid_t pid, const char *filepath)
215{
de5abcb0 216 int ret, fd = -1;
cd9adb8b 217 FILE *fp = nullptr;
35f90c40 218
a0377dfe 219 LTTNG_ASSERT(filepath);
35f90c40 220
28ab034a 221 fd = open(filepath, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
de5abcb0
JR
222 if (fd < 0) {
223 PERROR("open file %s", filepath);
224 ret = -1;
225 goto error;
226 }
227
228 fp = fdopen(fd, "w");
cd9adb8b 229 if (fp == nullptr) {
de5abcb0 230 PERROR("fdopen file %s", filepath);
35f90c40 231 ret = -1;
9a30ba1d 232 if (close(fd)) {
28ab034a
JG
233 PERROR("Failed to close `%s` file descriptor while handling fdopen error",
234 filepath);
9a30ba1d
JG
235 }
236
35f90c40
DG
237 goto error;
238 }
239
d1f721c5 240 ret = fprintf(fp, "%d\n", (int) pid);
35f90c40 241 if (ret < 0) {
de5abcb0
JR
242 PERROR("fprintf file %s", filepath);
243 ret = -1;
e205d79b 244 goto error;
35f90c40
DG
245 }
246
de5abcb0 247 DBG("'%d' written in file %s", (int) pid, filepath);
e205d79b 248 ret = 0;
de5abcb0 249
35f90c40 250error:
de5abcb0
JR
251 if (fp && fclose(fp)) {
252 PERROR("fclose file %s", filepath);
253 }
35f90c40
DG
254 return ret;
255}
2d851108
DG
256
257/*
d77dded2 258 * Create directory using the given path and mode.
2d851108
DG
259 *
260 * On success, return 0 else a negative error code.
261 */
d77dded2
JG
262int utils_mkdir(const char *path, mode_t mode, int uid, int gid)
263{
264 int ret;
cbf53d23 265 struct lttng_directory_handle *handle;
69e3a560 266 const struct lttng_credentials creds = {
a6bc4ca9
SM
267 .uid = LTTNG_OPTIONAL_INIT_VALUE((uid_t) uid),
268 .gid = LTTNG_OPTIONAL_INIT_VALUE((gid_t) gid),
18710679
JG
269 };
270
cd9adb8b 271 handle = lttng_directory_handle_create(nullptr);
cbf53d23
JG
272 if (!handle) {
273 ret = -1;
fd774fc6
JG
274 goto end;
275 }
18710679 276 ret = lttng_directory_handle_create_subdirectory_as_user(
cd9adb8b 277 handle, path, mode, (uid >= 0 || gid >= 0) ? &creds : nullptr);
fd774fc6 278end:
cbf53d23 279 lttng_directory_handle_put(handle);
2d851108
DG
280 return ret;
281}
fe4477ee 282
d77dded2
JG
283/*
284 * Recursively create directory using the given path and mode, under the
285 * provided uid and gid.
286 *
287 * On success, return 0 else a negative error code.
288 */
d77dded2
JG
289int utils_mkdir_recursive(const char *path, mode_t mode, int uid, int gid)
290{
291 int ret;
cbf53d23 292 struct lttng_directory_handle *handle;
69e3a560 293 const struct lttng_credentials creds = {
a6bc4ca9
SM
294 .uid = LTTNG_OPTIONAL_INIT_VALUE((uid_t) uid),
295 .gid = LTTNG_OPTIONAL_INIT_VALUE((gid_t) gid),
18710679
JG
296 };
297
cd9adb8b 298 handle = lttng_directory_handle_create(nullptr);
cbf53d23
JG
299 if (!handle) {
300 ret = -1;
fd774fc6
JG
301 goto end;
302 }
18710679 303 ret = lttng_directory_handle_create_subdirectory_recursive_as_user(
cd9adb8b 304 handle, path, mode, (uid >= 0 || gid >= 0) ? &creds : nullptr);
fd774fc6 305end:
cbf53d23 306 lttng_directory_handle_put(handle);
d77dded2
JG
307 return ret;
308}
309
fe4477ee 310/*
40804255 311 * out_stream_path is the output parameter.
fe4477ee
JD
312 *
313 * Return 0 on success or else a negative value.
314 */
28ab034a
JG
315int utils_stream_file_path(const char *path_name,
316 const char *file_name,
317 uint64_t size,
318 uint64_t count,
319 const char *suffix,
320 char *out_stream_path,
321 size_t stream_path_len)
fe4477ee 322{
7591bab1 323 int ret;
d6d89e4c 324 char count_str[MAX_INT_DEC_LEN(count) + 1] = {};
40804255 325 const char *path_separator;
fe4477ee 326
28ab034a 327 if (path_name && (path_name[0] == '\0' || path_name[strlen(path_name) - 1] == '/')) {
40804255
JG
328 path_separator = "";
329 } else {
330 path_separator = "/";
fe4477ee
JD
331 }
332
28ab034a
JG
333 path_name = path_name ?: "";
334 suffix = suffix ?: "";
40804255 335 if (size > 0) {
28ab034a 336 ret = snprintf(count_str, sizeof(count_str), "_%" PRIu64, count);
a0377dfe 337 LTTNG_ASSERT(ret > 0 && ret < sizeof(count_str));
309167d2
JD
338 }
339
28ab034a
JG
340 ret = snprintf(out_stream_path,
341 stream_path_len,
342 "%s%s%s%s%s",
343 path_name,
344 path_separator,
345 file_name,
346 count_str,
347 suffix);
40804255
JG
348 if (ret < 0 || ret >= stream_path_len) {
349 ERR("Truncation occurred while formatting stream path");
350 ret = -1;
fe4477ee 351 } else {
40804255 352 ret = 0;
7591bab1 353 }
7591bab1
MD
354 return ret;
355}
356
70d0b120
SM
357/**
358 * Parse a string that represents a size in human readable format. It
5983a922 359 * supports decimal integers suffixed by 'k', 'K', 'M' or 'G'.
70d0b120
SM
360 *
361 * The suffix multiply the integer by:
362 * 'k': 1024
363 * 'M': 1024^2
364 * 'G': 1024^3
365 *
366 * @param str The string to parse.
5983a922 367 * @param size Pointer to a uint64_t that will be filled with the
cfa9a5a2 368 * resulting size.
70d0b120
SM
369 *
370 * @return 0 on success, -1 on failure.
371 */
28ab034a 372int utils_parse_size_suffix(const char *const str, uint64_t *const size)
70d0b120 373{
70d0b120 374 int ret;
5983a922 375 uint64_t base_size;
70d0b120 376 long shift = 0;
5983a922
SM
377 const char *str_end;
378 char *num_end;
70d0b120
SM
379
380 if (!str) {
5983a922 381 DBG("utils_parse_size_suffix: received a NULL string.");
70d0b120
SM
382 ret = -1;
383 goto end;
384 }
385
5983a922 386 /* strtoull will accept a negative number, but we don't want to. */
cd9adb8b 387 if (strchr(str, '-') != nullptr) {
5983a922 388 DBG("utils_parse_size_suffix: invalid size string, should not contain '-'.");
70d0b120 389 ret = -1;
5983a922 390 goto end;
70d0b120
SM
391 }
392
5983a922
SM
393 /* str_end will point to the \0 */
394 str_end = str + strlen(str);
70d0b120 395 errno = 0;
5983a922 396 base_size = strtoull(str, &num_end, 0);
70d0b120 397 if (errno != 0) {
5983a922 398 PERROR("utils_parse_size_suffix strtoull");
70d0b120 399 ret = -1;
5983a922
SM
400 goto end;
401 }
402
403 if (num_end == str) {
404 /* strtoull parsed nothing, not good. */
405 DBG("utils_parse_size_suffix: strtoull had nothing good to parse.");
406 ret = -1;
407 goto end;
408 }
409
410 /* Check if a prefix is present. */
411 switch (*num_end) {
412 case 'G':
413 shift = GIBI_LOG2;
414 num_end++;
415 break;
416 case 'M': /* */
417 shift = MEBI_LOG2;
418 num_end++;
419 break;
420 case 'K':
421 case 'k':
422 shift = KIBI_LOG2;
423 num_end++;
424 break;
425 case '\0':
426 break;
427 default:
428 DBG("utils_parse_size_suffix: invalid suffix.");
429 ret = -1;
430 goto end;
431 }
432
433 /* Check for garbage after the valid input. */
434 if (num_end != str_end) {
435 DBG("utils_parse_size_suffix: Garbage after size string.");
436 ret = -1;
437 goto end;
70d0b120
SM
438 }
439
440 *size = base_size << shift;
441
442 /* Check for overflow */
443 if ((*size >> shift) != base_size) {
5983a922 444 DBG("utils_parse_size_suffix: oops, overflow detected.");
70d0b120 445 ret = -1;
5983a922 446 goto end;
70d0b120
SM
447 }
448
449 ret = 0;
70d0b120
SM
450end:
451 return ret;
452}
cfa9a5a2 453
7010c033
SM
454/**
455 * Parse a string that represents a time in human readable format. It
81684730
JR
456 * supports decimal integers suffixed by:
457 * "us" for microsecond,
458 * "ms" for millisecond,
459 * "s" for second,
460 * "m" for minute,
461 * "h" for hour
7010c033
SM
462 *
463 * The suffix multiply the integer by:
81684730
JR
464 * "us" : 1
465 * "ms" : 1000
466 * "s" : 1000000
467 * "m" : 60000000
468 * "h" : 3600000000
7010c033
SM
469 *
470 * Note that unit-less numbers are assumed to be microseconds.
471 *
472 * @param str The string to parse, assumed to be NULL-terminated.
473 * @param time_us Pointer to a uint64_t that will be filled with the
474 * resulting time in microseconds.
475 *
476 * @return 0 on success, -1 on failure.
477 */
28ab034a 478int utils_parse_time_suffix(char const *const str, uint64_t *const time_us)
7010c033
SM
479{
480 int ret;
481 uint64_t base_time;
81684730 482 uint64_t multiplier = 1;
7010c033
SM
483 const char *str_end;
484 char *num_end;
485
486 if (!str) {
487 DBG("utils_parse_time_suffix: received a NULL string.");
488 ret = -1;
489 goto end;
490 }
491
492 /* strtoull will accept a negative number, but we don't want to. */
cd9adb8b 493 if (strchr(str, '-') != nullptr) {
7010c033
SM
494 DBG("utils_parse_time_suffix: invalid time string, should not contain '-'.");
495 ret = -1;
496 goto end;
497 }
498
499 /* str_end will point to the \0 */
500 str_end = str + strlen(str);
501 errno = 0;
502 base_time = strtoull(str, &num_end, 10);
503 if (errno != 0) {
504 PERROR("utils_parse_time_suffix strtoull on string \"%s\"", str);
505 ret = -1;
506 goto end;
507 }
508
509 if (num_end == str) {
510 /* strtoull parsed nothing, not good. */
511 DBG("utils_parse_time_suffix: strtoull had nothing good to parse.");
512 ret = -1;
513 goto end;
514 }
515
516 /* Check if a prefix is present. */
517 switch (*num_end) {
518 case 'u':
81684730
JR
519 /*
520 * Microsecond (us)
521 *
522 * Skip the "us" if the string matches the "us" suffix,
523 * otherwise let the check for the end of the string handle
524 * the error reporting.
525 */
526 if (*(num_end + 1) == 's') {
527 num_end += 2;
528 }
7010c033
SM
529 break;
530 case 'm':
81684730
JR
531 if (*(num_end + 1) == 's') {
532 /* Millisecond (ms) */
533 multiplier = USEC_PER_MSEC;
534 /* Skip the 's' */
535 num_end++;
536 } else {
537 /* Minute (m) */
538 multiplier = USEC_PER_MINUTE;
539 }
540 num_end++;
7010c033
SM
541 break;
542 case 's':
81684730
JR
543 /* Second */
544 multiplier = USEC_PER_SEC;
545 num_end++;
546 break;
547 case 'h':
548 /* Hour */
549 multiplier = USEC_PER_HOURS;
7010c033
SM
550 num_end++;
551 break;
552 case '\0':
553 break;
554 default:
555 DBG("utils_parse_time_suffix: invalid suffix.");
556 ret = -1;
557 goto end;
558 }
559
560 /* Check for garbage after the valid input. */
561 if (num_end != str_end) {
562 DBG("utils_parse_time_suffix: Garbage after time string.");
563 ret = -1;
564 goto end;
565 }
566
567 *time_us = base_time * multiplier;
568
569 /* Check for overflow */
570 if ((*time_us / multiplier) != base_time) {
571 DBG("utils_parse_time_suffix: oops, overflow detected.");
572 ret = -1;
573 goto end;
574 }
575
576 ret = 0;
577end:
578 return ret;
579}
580
cfa9a5a2
DG
581/*
582 * fls: returns the position of the most significant bit.
583 * Returns 0 if no bit is set, else returns the position of the most
584 * significant bit (from 1 to 32 on 32-bit, from 1 to 64 on 64-bit).
585 */
586#if defined(__i386) || defined(__x86_64)
587static inline unsigned int fls_u32(uint32_t x)
588{
589 int r;
590
591 asm("bsrl %1,%0\n\t"
28ab034a
JG
592 "jnz 1f\n\t"
593 "movl $-1,%0\n\t"
594 "1:\n\t"
595 : "=r"(r)
596 : "rm"(x));
cfa9a5a2
DG
597 return r + 1;
598}
599#define HAS_FLS_U32
600#endif
601
a1e4ab8b 602#if defined(__x86_64) && defined(__LP64__)
28ab034a 603static inline unsigned int fls_u64(uint64_t x)
db5be0a3
JG
604{
605 long r;
606
607 asm("bsrq %1,%0\n\t"
608 "jnz 1f\n\t"
609 "movq $-1,%0\n\t"
610 "1:\n\t"
28ab034a
JG
611 : "=r"(r)
612 : "rm"(x));
db5be0a3
JG
613 return r + 1;
614}
615#define HAS_FLS_U64
616#endif
617
618#ifndef HAS_FLS_U64
28ab034a 619static __attribute__((unused)) unsigned int fls_u64(uint64_t x)
db5be0a3
JG
620{
621 unsigned int r = 64;
622
623 if (!x)
624 return 0;
625
626 if (!(x & 0xFFFFFFFF00000000ULL)) {
627 x <<= 32;
628 r -= 32;
629 }
630 if (!(x & 0xFFFF000000000000ULL)) {
631 x <<= 16;
632 r -= 16;
633 }
634 if (!(x & 0xFF00000000000000ULL)) {
635 x <<= 8;
636 r -= 8;
637 }
638 if (!(x & 0xF000000000000000ULL)) {
639 x <<= 4;
640 r -= 4;
641 }
642 if (!(x & 0xC000000000000000ULL)) {
643 x <<= 2;
644 r -= 2;
645 }
646 if (!(x & 0x8000000000000000ULL)) {
647 x <<= 1;
648 r -= 1;
649 }
650 return r;
651}
652#endif
653
cfa9a5a2
DG
654#ifndef HAS_FLS_U32
655static __attribute__((unused)) unsigned int fls_u32(uint32_t x)
656{
657 unsigned int r = 32;
658
659 if (!x) {
660 return 0;
661 }
662 if (!(x & 0xFFFF0000U)) {
663 x <<= 16;
664 r -= 16;
665 }
666 if (!(x & 0xFF000000U)) {
667 x <<= 8;
668 r -= 8;
669 }
670 if (!(x & 0xF0000000U)) {
671 x <<= 4;
672 r -= 4;
673 }
674 if (!(x & 0xC0000000U)) {
675 x <<= 2;
676 r -= 2;
677 }
678 if (!(x & 0x80000000U)) {
679 x <<= 1;
680 r -= 1;
681 }
682 return r;
683}
684#endif
685
686/*
687 * Return the minimum order for which x <= (1UL << order).
688 * Return -1 if x is 0.
689 */
cfa9a5a2
DG
690int utils_get_count_order_u32(uint32_t x)
691{
692 if (!x) {
693 return -1;
694 }
695
696 return fls_u32(x - 1);
697}
feb0f3e5 698
db5be0a3
JG
699/*
700 * Return the minimum order for which x <= (1UL << order).
701 * Return -1 if x is 0.
702 */
db5be0a3
JG
703int utils_get_count_order_u64(uint64_t x)
704{
705 if (!x) {
706 return -1;
707 }
708
709 return fls_u64(x - 1);
710}
711
feb0f3e5
AM
712/**
713 * Obtain the value of LTTNG_HOME environment variable, if exists.
714 * Otherwise returns the value of HOME.
715 */
cd9adb8b 716const char *utils_get_home_dir()
feb0f3e5 717{
cd9adb8b 718 char *val = nullptr;
04135dbd
DG
719 struct passwd *pwd;
720
e8fa9fb0 721 val = lttng_secure_getenv(DEFAULT_LTTNG_HOME_ENV_VAR);
cd9adb8b 722 if (val != nullptr) {
04135dbd
DG
723 goto end;
724 }
e8fa9fb0 725 val = lttng_secure_getenv(DEFAULT_LTTNG_FALLBACK_HOME_ENV_VAR);
cd9adb8b 726 if (val != nullptr) {
04135dbd 727 goto end;
feb0f3e5 728 }
04135dbd
DG
729
730 /* Fallback on the password file entry. */
731 pwd = getpwuid(getuid());
732 if (!pwd) {
733 goto end;
734 }
735 val = pwd->pw_dir;
736
737 DBG3("Home directory is '%s'", val);
738
739end:
740 return val;
feb0f3e5 741}
26fe5938 742
fb198a11
JG
743/**
744 * Get user's home directory. Dynamically allocated, must be freed
745 * by the caller.
746 */
fb198a11
JG
747char *utils_get_user_home_dir(uid_t uid)
748{
749 struct passwd pwd;
750 struct passwd *result;
cd9adb8b
JG
751 char *home_dir = nullptr;
752 char *buf = nullptr;
fb198a11
JG
753 long buflen;
754 int ret;
755
756 buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
757 if (buflen == -1) {
758 goto end;
759 }
760retry:
64803277 761 buf = zmalloc<char>(buflen);
fb198a11
JG
762 if (!buf) {
763 goto end;
764 }
765
766 ret = getpwuid_r(uid, &pwd, buf, buflen, &result);
767 if (ret || !result) {
768 if (ret == ERANGE) {
769 free(buf);
770 buflen *= 2;
771 goto retry;
772 }
773 goto end;
774 }
775
776 home_dir = strdup(pwd.pw_dir);
777end:
778 free(buf);
779 return home_dir;
780}
781
26fe5938
DG
782/*
783 * With the given format, fill dst with the time of len maximum siz.
784 *
785 * Return amount of bytes set in the buffer or else 0 on error.
786 */
26fe5938
DG
787size_t utils_get_current_time_str(const char *format, char *dst, size_t len)
788{
789 size_t ret;
790 time_t rawtime;
791 struct tm *timeinfo;
792
a0377dfe
FD
793 LTTNG_ASSERT(format);
794 LTTNG_ASSERT(dst);
26fe5938
DG
795
796 /* Get date and time for session path */
797 time(&rawtime);
798 timeinfo = localtime(&rawtime);
411b3154
SM
799 DIAGNOSTIC_PUSH
800 DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL
26fe5938 801 ret = strftime(dst, len, format, timeinfo);
411b3154 802 DIAGNOSTIC_POP
26fe5938 803 if (ret == 0) {
28ab034a 804 ERR("Unable to strftime with format %s at dst %p of len %zu", format, dst, len);
26fe5938
DG
805 }
806
807 return ret;
808}
6c71277b
MD
809
810/*
28ab59d0
JR
811 * Return 0 on success and set *gid to the group_ID matching the passed name.
812 * Else -1 if it cannot be found or an error occurred.
6c71277b 813 */
28ab59d0 814int utils_get_group_id(const char *name, bool warn, gid_t *gid)
6c71277b 815{
28ab59d0
JR
816 static volatile int warn_once;
817 int ret;
818 long sys_len;
819 size_t len;
820 struct group grp;
821 struct group *result;
822 struct lttng_dynamic_buffer buffer;
823
824 /* Get the system limit, if it exists. */
825 sys_len = sysconf(_SC_GETGR_R_SIZE_MAX);
826 if (sys_len == -1) {
827 len = 1024;
828 } else {
829 len = (size_t) sys_len;
830 }
831
832 lttng_dynamic_buffer_init(&buffer);
833 ret = lttng_dynamic_buffer_set_size(&buffer, len);
834 if (ret) {
835 ERR("Failed to allocate group info buffer");
836 ret = -1;
837 goto error;
838 }
6c71277b 839
28ab59d0
JR
840 while ((ret = getgrnam_r(name, &grp, buffer.data, buffer.size, &result)) == ERANGE) {
841 const size_t new_len = 2 * buffer.size;
6c71277b 842
28ab59d0
JR
843 /* Buffer is not big enough, increase its size. */
844 if (new_len < buffer.size) {
845 ERR("Group info buffer size overflow");
846 ret = -1;
847 goto error;
848 }
849
850 ret = lttng_dynamic_buffer_set_size(&buffer, new_len);
851 if (ret) {
28ab034a 852 ERR("Failed to grow group info buffer to %zu bytes", new_len);
28ab59d0
JR
853 ret = -1;
854 goto error;
6c71277b 855 }
6c71277b 856 }
28ab59d0 857 if (ret) {
9120e619 858 if (ret == ESRCH) {
28ab034a 859 DBG("Could not find group file entry for group name '%s'", name);
9120e619 860 } else {
28ab034a 861 PERROR("Failed to get group file entry for group name '%s'", name);
9120e619
JG
862 }
863
28ab59d0
JR
864 ret = -1;
865 goto error;
866 }
867
868 /* Group not found. */
869 if (!result) {
870 ret = -1;
871 goto error;
872 }
873
874 *gid = result->gr_gid;
875 ret = 0;
876
877error:
878 if (ret && warn && !warn_once) {
879 WARN("No tracing group detected");
880 warn_once = 1;
881 }
882 lttng_dynamic_buffer_reset(&buffer);
883 return ret;
6c71277b 884}
8db0dc00
JG
885
886/*
887 * Return a newly allocated option string. This string is to be used as the
888 * optstring argument of getopt_long(), see GETOPT(3). opt_count is the number
889 * of elements in the long_options array. Returns NULL if the string's
890 * allocation fails.
891 */
28ab034a 892char *utils_generate_optstring(const struct option *long_options, size_t opt_count)
8db0dc00
JG
893{
894 int i;
895 size_t string_len = opt_count, str_pos = 0;
896 char *optstring;
897
898 /*
899 * Compute the necessary string length. One letter per option, two when an
900 * argument is necessary, and a trailing NULL.
901 */
902 for (i = 0; i < opt_count; i++) {
903 string_len += long_options[i].has_arg ? 1 : 0;
904 }
905
64803277 906 optstring = zmalloc<char>(string_len);
8db0dc00
JG
907 if (!optstring) {
908 goto end;
909 }
910
911 for (i = 0; i < opt_count; i++) {
912 if (!long_options[i].name) {
913 /* Got to the trailing NULL element */
914 break;
915 }
916
a596dcb9
JG
917 if (long_options[i].val != '\0') {
918 optstring[str_pos++] = (char) long_options[i].val;
919 if (long_options[i].has_arg) {
920 optstring[str_pos++] = ':';
921 }
8db0dc00
JG
922 }
923 }
924
925end:
926 return optstring;
927}
3d071855
MD
928
929/*
930 * Try to remove a hierarchy of empty directories, recursively. Don't unlink
9529ec1b 931 * any file. Try to rmdir any empty directory within the hierarchy.
3d071855 932 */
3d071855
MD
933int utils_recursive_rmdir(const char *path)
934{
93bed9fe 935 int ret;
cbf53d23 936 struct lttng_directory_handle *handle;
7a946beb 937
cd9adb8b 938 handle = lttng_directory_handle_create(nullptr);
cbf53d23
JG
939 if (!handle) {
940 ret = -1;
93bed9fe 941 goto end;
3d071855 942 }
cbf53d23 943 ret = lttng_directory_handle_remove_subdirectory(handle, path);
3d071855 944end:
cbf53d23 945 lttng_directory_handle_put(handle);
3d071855
MD
946 return ret;
947}
93ec662e 948
93ec662e
JD
949int utils_truncate_stream_file(int fd, off_t length)
950{
951 int ret;
a5df8828 952 off_t lseek_ret;
93ec662e
JD
953
954 ret = ftruncate(fd, length);
955 if (ret < 0) {
956 PERROR("ftruncate");
957 goto end;
958 }
a5df8828
GL
959 lseek_ret = lseek(fd, length, SEEK_SET);
960 if (lseek_ret < 0) {
93ec662e 961 PERROR("lseek");
a5df8828 962 ret = -1;
93ec662e
JD
963 goto end;
964 }
93ec662e
JD
965end:
966 return ret;
967}
4ba92f18 968
cd9adb8b 969static const char *get_man_bin_path()
4ba92f18 970{
b7dce40d 971 char *env_man_path = lttng_secure_getenv(DEFAULT_MAN_BIN_PATH_ENV);
4ba92f18
PP
972
973 if (env_man_path) {
974 return env_man_path;
975 }
976
977 return DEFAULT_MAN_BIN_PATH;
978}
979
ef4a570d
OD
980static const char *get_manpath()
981{
982 char *manpath = lttng_secure_getenv(DEFAULT_MANPATH);
983
984 if (manpath) {
985 return manpath;
986 }
987
988 /* As defined during configuration. */
989 return MANPATH;
990}
991
28ab034a 992int utils_show_help(int section, const char *page_name, const char *help_msg)
4ba92f18
PP
993{
994 char section_string[8];
995 const char *man_bin_path = get_man_bin_path();
ef4a570d 996 const char *manpath = get_manpath();
4fc83d94
PP
997 int ret = 0;
998
999 if (help_msg) {
1000 printf("%s", help_msg);
1001 goto end;
1002 }
4ba92f18
PP
1003
1004 /* Section integer -> section string */
51a7b11e 1005 ret = snprintf(section_string, sizeof(section_string), "%d", section);
a0377dfe 1006 LTTNG_ASSERT(ret > 0 && ret < 8);
4ba92f18
PP
1007
1008 /*
1009 * Execute man pager.
1010 *
b07e7ef0 1011 * We provide -M to man here because LTTng-tools can
4ba92f18
PP
1012 * be installed outside /usr, in which case its man pages are
1013 * not located in the default /usr/share/man directory.
1014 */
ef4a570d 1015 ret = execlp(man_bin_path, "man", "-M", manpath, section_string, page_name, NULL);
4fc83d94
PP
1016
1017end:
4ba92f18
PP
1018 return ret;
1019}
09b72f7a 1020
28ab034a 1021static int read_proc_meminfo_field(const char *field, uint64_t *value)
09b72f7a
FD
1022{
1023 int ret;
1024 FILE *proc_meminfo;
1025 char name[PROC_MEMINFO_FIELD_MAX_NAME_LEN] = {};
1026
1027 proc_meminfo = fopen(PROC_MEMINFO_PATH, "r");
1028 if (!proc_meminfo) {
1029 PERROR("Failed to fopen() " PROC_MEMINFO_PATH);
1030 ret = -1;
1031 goto fopen_error;
28ab034a 1032 }
09b72f7a
FD
1033
1034 /*
1035 * Read the contents of /proc/meminfo line by line to find the right
1036 * field.
1037 */
1038 while (!feof(proc_meminfo)) {
13dd7782 1039 uint64_t value_kb;
09b72f7a
FD
1040
1041 ret = fscanf(proc_meminfo,
28ab034a
JG
1042 "%" MAX_NAME_LEN_SCANF_IS_A_BROKEN_API "s %" SCNu64 " kB\n",
1043 name,
1044 &value_kb);
09b72f7a
FD
1045 if (ret == EOF) {
1046 /*
1047 * fscanf() returning EOF can indicate EOF or an error.
1048 */
1049 if (ferror(proc_meminfo)) {
1050 PERROR("Failed to parse " PROC_MEMINFO_PATH);
1051 }
1052 break;
1053 }
1054
1055 if (ret == 2 && strcmp(name, field) == 0) {
1056 /*
1057 * This number is displayed in kilo-bytes. Return the
1058 * number of bytes.
1059 */
13dd7782
JR
1060 if (value_kb > UINT64_MAX / 1024) {
1061 ERR("Overflow on kb to bytes conversion");
1062 break;
1063 }
1064
1065 *value = value_kb * 1024;
09b72f7a
FD
1066 ret = 0;
1067 goto found;
1068 }
1069 }
1070 /* Reached the end of the file without finding the right field. */
1071 ret = -1;
1072
1073found:
1074 fclose(proc_meminfo);
1075fopen_error:
1076 return ret;
1077}
1078
1079/*
1080 * Returns an estimate of the number of bytes of memory available based on the
1081 * the information in `/proc/meminfo`. The number returned by this function is
1082 * a best guess.
1083 */
13dd7782 1084int utils_get_memory_available(uint64_t *value)
09b72f7a
FD
1085{
1086 return read_proc_meminfo_field(PROC_MEMINFO_MEMAVAILABLE_LINE, value);
1087}
1088
1089/*
1090 * Returns the total size of the memory on the system in bytes based on the
1091 * the information in `/proc/meminfo`.
1092 */
13dd7782 1093int utils_get_memory_total(uint64_t *value)
09b72f7a
FD
1094{
1095 return read_proc_meminfo_field(PROC_MEMINFO_MEMTOTAL_LINE, value);
1096}
ce9ee1fb 1097
ce9ee1fb
JR
1098int utils_change_working_directory(const char *path)
1099{
1100 int ret;
1101
a0377dfe 1102 LTTNG_ASSERT(path);
ce9ee1fb
JR
1103
1104 DBG("Changing working directory to \"%s\"", path);
1105 ret = chdir(path);
1106 if (ret) {
1107 PERROR("Failed to change working directory to \"%s\"", path);
1108 goto end;
1109 }
1110
1111 /* Check for write access */
1112 if (access(path, W_OK)) {
1113 if (errno == EACCES) {
1114 /*
1115 * Do not treat this as an error since the permission
1116 * might change in the lifetime of the process
1117 */
1118 DBG("Working directory \"%s\" is not writable", path);
1119 } else {
28ab034a 1120 PERROR("Failed to check if working directory \"%s\" is writable", path);
ce9ee1fb
JR
1121 }
1122 }
1123
1124end:
1125 return ret;
1126}
159b042f 1127
159b042f
JG
1128enum lttng_error_code utils_user_id_from_name(const char *user_name, uid_t *uid)
1129{
1130 struct passwd p, *pres;
1131 int ret;
1132 enum lttng_error_code ret_val = LTTNG_OK;
cd9adb8b 1133 char *buf = nullptr;
159b042f
JG
1134 ssize_t buflen;
1135
1136 buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
1137 if (buflen < 0) {
1138 buflen = FALLBACK_USER_BUFLEN;
1139 }
1140
64803277 1141 buf = zmalloc<char>(buflen);
159b042f
JG
1142 if (!buf) {
1143 ret_val = LTTNG_ERR_NOMEM;
1144 goto end;
1145 }
1146
1147 for (;;) {
1148 ret = getpwnam_r(user_name, &p, buf, buflen, &pres);
1149 switch (ret) {
1150 case EINTR:
1151 continue;
1152 case ERANGE:
1153 buflen *= 2;
1154 free(buf);
64803277 1155 buf = zmalloc<char>(buflen);
159b042f
JG
1156 if (!buf) {
1157 ret_val = LTTNG_ERR_NOMEM;
1158 goto end;
1159 }
1160 continue;
1161 default:
1162 goto end_loop;
1163 }
1164 }
1165end_loop:
1166
1167 switch (ret) {
1168 case 0:
cd9adb8b 1169 if (pres == nullptr) {
159b042f
JG
1170 ret_val = LTTNG_ERR_USER_NOT_FOUND;
1171 } else {
1172 *uid = p.pw_uid;
1173 DBG("Lookup of tracker UID/VUID: name '%s' maps to uid %" PRId64,
28ab034a
JG
1174 user_name,
1175 (int64_t) *uid);
159b042f
JG
1176 ret_val = LTTNG_OK;
1177 }
1178 break;
1179 case ENOENT:
1180 case ESRCH:
1181 case EBADF:
1182 case EPERM:
1183 ret_val = LTTNG_ERR_USER_NOT_FOUND;
1184 break;
1185 default:
1186 ret_val = LTTNG_ERR_NOMEM;
1187 }
1188end:
1189 free(buf);
1190 return ret_val;
1191}
1192
28ab034a 1193enum lttng_error_code utils_group_id_from_name(const char *group_name, gid_t *gid)
159b042f
JG
1194{
1195 struct group g, *gres;
1196 int ret;
1197 enum lttng_error_code ret_val = LTTNG_OK;
cd9adb8b 1198 char *buf = nullptr;
159b042f
JG
1199 ssize_t buflen;
1200
1201 buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
1202 if (buflen < 0) {
1203 buflen = FALLBACK_GROUP_BUFLEN;
1204 }
1205
64803277 1206 buf = zmalloc<char>(buflen);
159b042f
JG
1207 if (!buf) {
1208 ret_val = LTTNG_ERR_NOMEM;
1209 goto end;
1210 }
1211
1212 for (;;) {
1213 ret = getgrnam_r(group_name, &g, buf, buflen, &gres);
1214 switch (ret) {
1215 case EINTR:
1216 continue;
1217 case ERANGE:
1218 buflen *= 2;
1219 free(buf);
64803277 1220 buf = zmalloc<char>(buflen);
159b042f
JG
1221 if (!buf) {
1222 ret_val = LTTNG_ERR_NOMEM;
1223 goto end;
1224 }
1225 continue;
1226 default:
1227 goto end_loop;
1228 }
1229 }
1230end_loop:
1231
1232 switch (ret) {
1233 case 0:
cd9adb8b 1234 if (gres == nullptr) {
159b042f
JG
1235 ret_val = LTTNG_ERR_GROUP_NOT_FOUND;
1236 } else {
1237 *gid = g.gr_gid;
1238 DBG("Lookup of tracker GID/GUID: name '%s' maps to gid %" PRId64,
28ab034a
JG
1239 group_name,
1240 (int64_t) *gid);
159b042f
JG
1241 ret_val = LTTNG_OK;
1242 }
1243 break;
1244 case ENOENT:
1245 case ESRCH:
1246 case EBADF:
1247 case EPERM:
1248 ret_val = LTTNG_ERR_GROUP_NOT_FOUND;
1249 break;
1250 default:
1251 ret_val = LTTNG_ERR_NOMEM;
1252 }
1253end:
1254 free(buf);
1255 return ret_val;
1256}
240baf2b 1257
28ab034a 1258int utils_parse_unsigned_long_long(const char *str, unsigned long long *value)
240baf2b
JR
1259{
1260 int ret;
1261 char *endptr;
1262
a0377dfe
FD
1263 LTTNG_ASSERT(str);
1264 LTTNG_ASSERT(value);
240baf2b
JR
1265
1266 errno = 0;
1267 *value = strtoull(str, &endptr, 10);
1268
1269 /* Conversion failed. Out of range? */
1270 if (errno != 0) {
1271 /* Don't print an error; allow the caller to log a better error. */
1272 DBG("Failed to parse string as unsigned long long number: string = '%s', errno = %d",
28ab034a
JG
1273 str,
1274 errno);
240baf2b
JR
1275 ret = -1;
1276 goto end;
1277 }
1278
1279 /* Not the end of the string or empty string. */
1280 if (*endptr || endptr == str) {
28ab034a 1281 DBG("Failed to parse string as unsigned long long number: string = '%s'", str);
240baf2b
JR
1282 ret = -1;
1283 goto end;
1284 }
1285
1286 ret = 0;
1287
1288end:
1289 return ret;
1290}
This page took 0.152375 seconds and 4 git commands to generate.