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