Fix: utils.c: check str*dup OOM
[lttng-tools.git] / src / common / utils.c
1 /*
2 * Copyright (C) 2012 - David Goulet <dgoulet@efficios.com>
3 * Copyright (C) 2013 - Raphaƫl Beamonte <raphael.beamonte@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License, version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc., 51
16 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #define _GNU_SOURCE
20 #include <assert.h>
21 #include <ctype.h>
22 #include <fcntl.h>
23 #include <limits.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29 #include <inttypes.h>
30 #include <grp.h>
31 #include <pwd.h>
32 #include <sys/file.h>
33
34 #include <common/common.h>
35 #include <common/runas.h>
36
37 #include "utils.h"
38 #include "defaults.h"
39
40 /*
41 * Return a partial realpath(3) of the path even if the full path does not
42 * exist. For instance, with /tmp/test1/test2/test3, if test2/ does not exist
43 * but the /tmp/test1 does, the real path for /tmp/test1 is concatened with
44 * /test2/test3 then returned. In normal time, realpath(3) fails if the end
45 * point directory does not exist.
46 * In case resolved_path is NULL, the string returned was allocated in the
47 * function and thus need to be freed by the caller. The size argument allows
48 * to specify the size of the resolved_path argument if given, or the size to
49 * allocate.
50 */
51 LTTNG_HIDDEN
52 char *utils_partial_realpath(const char *path, char *resolved_path, size_t size)
53 {
54 char *cut_path, *try_path = NULL, *try_path_prev = NULL;
55 const char *next, *prev, *end;
56
57 /* Safety net */
58 if (path == NULL) {
59 goto error;
60 }
61
62 /*
63 * Identify the end of the path, we don't want to treat the
64 * last char if it is a '/', we will just keep it on the side
65 * to be added at the end, and return a value coherent with
66 * the path given as argument
67 */
68 end = path + strlen(path);
69 if (*(end-1) == '/') {
70 end--;
71 }
72
73 /* Initiate the values of the pointers before looping */
74 next = path;
75 prev = next;
76 /* Only to ensure try_path is not NULL to enter the while */
77 try_path = (char *)next;
78
79 /* Resolve the canonical path of the first part of the path */
80 while (try_path != NULL && next != end) {
81 /*
82 * If there is not any '/' left, we want to try with
83 * the full path
84 */
85 next = strpbrk(next + 1, "/");
86 if (next == NULL) {
87 next = end;
88 }
89
90 /* Cut the part we will be trying to resolve */
91 cut_path = strndup(path, next - path);
92 if (cut_path == NULL) {
93 PERROR("strndup");
94 goto error;
95 }
96
97 /* Try to resolve this part */
98 try_path = realpath((char *)cut_path, NULL);
99 if (try_path == NULL) {
100 /*
101 * There was an error, we just want to be assured it
102 * is linked to an unexistent directory, if it's another
103 * reason, we spawn an error
104 */
105 switch (errno) {
106 case ENOENT:
107 /* Ignore the error */
108 break;
109 default:
110 PERROR("realpath (partial_realpath)");
111 goto error;
112 break;
113 }
114 } else {
115 /* Save the place we are before trying the next step */
116 free(try_path_prev);
117 try_path_prev = try_path;
118 prev = next;
119 }
120
121 /* Free the allocated memory */
122 free(cut_path);
123 };
124
125 /* Allocate memory for the resolved path if necessary */
126 if (resolved_path == NULL) {
127 resolved_path = zmalloc(size);
128 if (resolved_path == NULL) {
129 PERROR("zmalloc resolved path");
130 goto error;
131 }
132 }
133
134 /*
135 * If we were able to solve at least partially the path, we can concatenate
136 * what worked and what didn't work
137 */
138 if (try_path_prev != NULL) {
139 /* If we risk to concatenate two '/', we remove one of them */
140 if (try_path_prev[strlen(try_path_prev) - 1] == '/' && prev[0] == '/') {
141 try_path_prev[strlen(try_path_prev) - 1] = '\0';
142 }
143
144 /*
145 * Duplicate the memory used by prev in case resolved_path and
146 * path are pointers for the same memory space
147 */
148 cut_path = strdup(prev);
149 if (cut_path == NULL) {
150 PERROR("strdup");
151 goto error;
152 }
153
154 /* Concatenate the strings */
155 snprintf(resolved_path, size, "%s%s", try_path_prev, cut_path);
156
157 /* Free the allocated memory */
158 free(cut_path);
159 free(try_path_prev);
160 /*
161 * Else, we just copy the path in our resolved_path to
162 * return it as is
163 */
164 } else {
165 strncpy(resolved_path, path, size);
166 }
167
168 /* Then we return the 'partially' resolved path */
169 return resolved_path;
170
171 error:
172 free(resolved_path);
173 return NULL;
174 }
175
176 /*
177 * Make a full resolution of the given path even if it doesn't exist.
178 * This function uses the utils_partial_realpath function to resolve
179 * symlinks and relatives paths at the start of the string, and
180 * implements functionnalities to resolve the './' and '../' strings
181 * in the middle of a path. This function is only necessary because
182 * realpath(3) does not accept to resolve unexistent paths.
183 * The returned string was allocated in the function, it is thus of
184 * the responsibility of the caller to free this memory.
185 */
186 LTTNG_HIDDEN
187 char *utils_expand_path(const char *path)
188 {
189 char *next, *previous, *slash, *start_path, *absolute_path = NULL;
190 char *last_token;
191 int is_dot, is_dotdot;
192
193 /* Safety net */
194 if (path == NULL) {
195 goto error;
196 }
197
198 /* Allocate memory for the absolute_path */
199 absolute_path = zmalloc(PATH_MAX);
200 if (absolute_path == NULL) {
201 PERROR("zmalloc expand path");
202 goto error;
203 }
204
205 /*
206 * If the path is not already absolute nor explicitly relative,
207 * consider we're in the current directory
208 */
209 if (*path != '/' && strncmp(path, "./", 2) != 0 &&
210 strncmp(path, "../", 3) != 0) {
211 snprintf(absolute_path, PATH_MAX, "./%s", path);
212 /* Else, we just copy the path */
213 } else {
214 strncpy(absolute_path, path, PATH_MAX);
215 }
216
217 /* Resolve partially our path */
218 absolute_path = utils_partial_realpath(absolute_path,
219 absolute_path, PATH_MAX);
220
221 /* As long as we find '/./' in the working_path string */
222 while ((next = strstr(absolute_path, "/./"))) {
223
224 /* We prepare the start_path not containing it */
225 start_path = strndup(absolute_path, next - absolute_path);
226 if (!start_path) {
227 PERROR("strndup");
228 goto error;
229 }
230 /* And we concatenate it with the part after this string */
231 snprintf(absolute_path, PATH_MAX, "%s%s", start_path, next + 2);
232
233 free(start_path);
234 }
235
236 /* As long as we find '/../' in the working_path string */
237 while ((next = strstr(absolute_path, "/../"))) {
238 /* We find the last level of directory */
239 previous = absolute_path;
240 while ((slash = strpbrk(previous, "/")) && slash != next) {
241 previous = slash + 1;
242 }
243
244 /* Then we prepare the start_path not containing it */
245 start_path = strndup(absolute_path, previous - absolute_path);
246 if (!start_path) {
247 PERROR("strndup");
248 goto error;
249 }
250
251 /* And we concatenate it with the part after the '/../' */
252 snprintf(absolute_path, PATH_MAX, "%s%s", start_path, next + 4);
253
254 /* We can free the memory used for the start path*/
255 free(start_path);
256
257 /* Then we verify for symlinks using partial_realpath */
258 absolute_path = utils_partial_realpath(absolute_path,
259 absolute_path, PATH_MAX);
260 }
261
262 /* Identify the last token */
263 last_token = strrchr(absolute_path, '/');
264
265 /* Verify that this token is not a relative path */
266 is_dotdot = (strcmp(last_token, "/..") == 0);
267 is_dot = (strcmp(last_token, "/.") == 0);
268
269 /* If it is, take action */
270 if (is_dot || is_dotdot) {
271 /* For both, remove this token */
272 *last_token = '\0';
273
274 /* If it was a reference to parent directory, go back one more time */
275 if (is_dotdot) {
276 last_token = strrchr(absolute_path, '/');
277
278 /* If there was only one level left, we keep the first '/' */
279 if (last_token == absolute_path) {
280 last_token++;
281 }
282
283 *last_token = '\0';
284 }
285 }
286
287 return absolute_path;
288
289 error:
290 free(absolute_path);
291 return NULL;
292 }
293
294 /*
295 * Create a pipe in dst.
296 */
297 LTTNG_HIDDEN
298 int utils_create_pipe(int *dst)
299 {
300 int ret;
301
302 if (dst == NULL) {
303 return -1;
304 }
305
306 ret = pipe(dst);
307 if (ret < 0) {
308 PERROR("create pipe");
309 }
310
311 return ret;
312 }
313
314 /*
315 * Create pipe and set CLOEXEC flag to both fd.
316 *
317 * Make sure the pipe opened by this function are closed at some point. Use
318 * utils_close_pipe().
319 */
320 LTTNG_HIDDEN
321 int utils_create_pipe_cloexec(int *dst)
322 {
323 int ret, i;
324
325 if (dst == NULL) {
326 return -1;
327 }
328
329 ret = utils_create_pipe(dst);
330 if (ret < 0) {
331 goto error;
332 }
333
334 for (i = 0; i < 2; i++) {
335 ret = fcntl(dst[i], F_SETFD, FD_CLOEXEC);
336 if (ret < 0) {
337 PERROR("fcntl pipe cloexec");
338 goto error;
339 }
340 }
341
342 error:
343 return ret;
344 }
345
346 /*
347 * Create pipe and set fd flags to FD_CLOEXEC and O_NONBLOCK.
348 *
349 * Make sure the pipe opened by this function are closed at some point. Use
350 * utils_close_pipe(). Using pipe() and fcntl rather than pipe2() to
351 * support OSes other than Linux 2.6.23+.
352 */
353 LTTNG_HIDDEN
354 int utils_create_pipe_cloexec_nonblock(int *dst)
355 {
356 int ret, i;
357
358 if (dst == NULL) {
359 return -1;
360 }
361
362 ret = utils_create_pipe(dst);
363 if (ret < 0) {
364 goto error;
365 }
366
367 for (i = 0; i < 2; i++) {
368 ret = fcntl(dst[i], F_SETFD, FD_CLOEXEC);
369 if (ret < 0) {
370 PERROR("fcntl pipe cloexec");
371 goto error;
372 }
373 /*
374 * Note: we override any flag that could have been
375 * previously set on the fd.
376 */
377 ret = fcntl(dst[i], F_SETFL, O_NONBLOCK);
378 if (ret < 0) {
379 PERROR("fcntl pipe nonblock");
380 goto error;
381 }
382 }
383
384 error:
385 return ret;
386 }
387
388 /*
389 * Close both read and write side of the pipe.
390 */
391 LTTNG_HIDDEN
392 void utils_close_pipe(int *src)
393 {
394 int i, ret;
395
396 if (src == NULL) {
397 return;
398 }
399
400 for (i = 0; i < 2; i++) {
401 /* Safety check */
402 if (src[i] < 0) {
403 continue;
404 }
405
406 ret = close(src[i]);
407 if (ret) {
408 PERROR("close pipe");
409 }
410 }
411 }
412
413 /*
414 * Create a new string using two strings range.
415 */
416 LTTNG_HIDDEN
417 char *utils_strdupdelim(const char *begin, const char *end)
418 {
419 char *str;
420
421 str = zmalloc(end - begin + 1);
422 if (str == NULL) {
423 PERROR("zmalloc strdupdelim");
424 goto error;
425 }
426
427 memcpy(str, begin, end - begin);
428 str[end - begin] = '\0';
429
430 error:
431 return str;
432 }
433
434 /*
435 * Set CLOEXEC flag to the give file descriptor.
436 */
437 LTTNG_HIDDEN
438 int utils_set_fd_cloexec(int fd)
439 {
440 int ret;
441
442 if (fd < 0) {
443 ret = -EINVAL;
444 goto end;
445 }
446
447 ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
448 if (ret < 0) {
449 PERROR("fcntl cloexec");
450 ret = -errno;
451 }
452
453 end:
454 return ret;
455 }
456
457 /*
458 * Create pid file to the given path and filename.
459 */
460 LTTNG_HIDDEN
461 int utils_create_pid_file(pid_t pid, const char *filepath)
462 {
463 int ret;
464 FILE *fp;
465
466 assert(filepath);
467
468 fp = fopen(filepath, "w");
469 if (fp == NULL) {
470 PERROR("open pid file %s", filepath);
471 ret = -1;
472 goto error;
473 }
474
475 ret = fprintf(fp, "%d\n", pid);
476 if (ret < 0) {
477 PERROR("fprintf pid file");
478 }
479
480 fclose(fp);
481 DBG("Pid %d written in file %s", pid, filepath);
482 error:
483 return ret;
484 }
485
486 /*
487 * Create lock file to the given path and filename.
488 * Returns the associated file descriptor, -1 on error.
489 */
490 LTTNG_HIDDEN
491 int utils_create_lock_file(const char *filepath)
492 {
493 int ret;
494 int fd;
495
496 assert(filepath);
497
498 fd = open(filepath, O_CREAT,
499 O_WRONLY | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
500 if (fd < 0) {
501 PERROR("open lock file %s", filepath);
502 ret = -1;
503 goto error;
504 }
505
506 /*
507 * Attempt to lock the file. If this fails, there is
508 * already a process using the same lock file running
509 * and we should exit.
510 */
511 ret = flock(fd, LOCK_EX | LOCK_NB);
512 if (ret) {
513 WARN("Could not get lock file %s, another instance is running.",
514 filepath);
515 close(fd);
516 fd = ret;
517 goto error;
518 }
519
520 error:
521 return fd;
522 }
523
524 /*
525 * Recursively create directory using the given path and mode.
526 *
527 * On success, return 0 else a negative error code.
528 */
529 LTTNG_HIDDEN
530 int utils_mkdir_recursive(const char *path, mode_t mode)
531 {
532 char *p, tmp[PATH_MAX];
533 size_t len;
534 int ret;
535
536 assert(path);
537
538 ret = snprintf(tmp, sizeof(tmp), "%s", path);
539 if (ret < 0) {
540 PERROR("snprintf mkdir");
541 goto error;
542 }
543
544 len = ret;
545 if (tmp[len - 1] == '/') {
546 tmp[len - 1] = 0;
547 }
548
549 for (p = tmp + 1; *p; p++) {
550 if (*p == '/') {
551 *p = 0;
552 if (tmp[strlen(tmp) - 1] == '.' &&
553 tmp[strlen(tmp) - 2] == '.' &&
554 tmp[strlen(tmp) - 3] == '/') {
555 ERR("Using '/../' is not permitted in the trace path (%s)",
556 tmp);
557 ret = -1;
558 goto error;
559 }
560 ret = mkdir(tmp, mode);
561 if (ret < 0) {
562 if (errno != EEXIST) {
563 PERROR("mkdir recursive");
564 ret = -errno;
565 goto error;
566 }
567 }
568 *p = '/';
569 }
570 }
571
572 ret = mkdir(tmp, mode);
573 if (ret < 0) {
574 if (errno != EEXIST) {
575 PERROR("mkdir recursive last piece");
576 ret = -errno;
577 } else {
578 ret = 0;
579 }
580 }
581
582 error:
583 return ret;
584 }
585
586 /*
587 * Create the stream tracefile on disk.
588 *
589 * Return 0 on success or else a negative value.
590 */
591 LTTNG_HIDDEN
592 int utils_create_stream_file(const char *path_name, char *file_name, uint64_t size,
593 uint64_t count, int uid, int gid, char *suffix)
594 {
595 int ret, out_fd, flags, mode;
596 char full_path[PATH_MAX], *path_name_suffix = NULL, *path;
597 char *extra = NULL;
598
599 assert(path_name);
600 assert(file_name);
601
602 ret = snprintf(full_path, sizeof(full_path), "%s/%s",
603 path_name, file_name);
604 if (ret < 0) {
605 PERROR("snprintf create output file");
606 goto error;
607 }
608
609 /* Setup extra string if suffix or/and a count is needed. */
610 if (size > 0 && suffix) {
611 ret = asprintf(&extra, "_%" PRIu64 "%s", count, suffix);
612 } else if (size > 0) {
613 ret = asprintf(&extra, "_%" PRIu64, count);
614 } else if (suffix) {
615 ret = asprintf(&extra, "%s", suffix);
616 }
617 if (ret < 0) {
618 PERROR("Allocating extra string to name");
619 goto error;
620 }
621
622 /*
623 * If we split the trace in multiple files, we have to add the count at the
624 * end of the tracefile name
625 */
626 if (extra) {
627 ret = asprintf(&path_name_suffix, "%s%s", full_path, extra);
628 if (ret < 0) {
629 PERROR("Allocating path name with extra string");
630 goto error_free_suffix;
631 }
632 path = path_name_suffix;
633 } else {
634 path = full_path;
635 }
636
637 flags = O_WRONLY | O_CREAT | O_TRUNC;
638 /* Open with 660 mode */
639 mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
640
641 if (uid < 0 || gid < 0) {
642 out_fd = open(path, flags, mode);
643 } else {
644 out_fd = run_as_open(path, flags, mode, uid, gid);
645 }
646 if (out_fd < 0) {
647 PERROR("open stream path %s", path);
648 goto error_open;
649 }
650 ret = out_fd;
651
652 error_open:
653 free(path_name_suffix);
654 error_free_suffix:
655 free(extra);
656 error:
657 return ret;
658 }
659
660 /*
661 * Change the output tracefile according to the given size and count The
662 * new_count pointer is set during this operation.
663 *
664 * From the consumer, the stream lock MUST be held before calling this function
665 * because we are modifying the stream status.
666 *
667 * Return 0 on success or else a negative value.
668 */
669 LTTNG_HIDDEN
670 int utils_rotate_stream_file(char *path_name, char *file_name, uint64_t size,
671 uint64_t count, int uid, int gid, int out_fd, uint64_t *new_count,
672 int *stream_fd)
673 {
674 int ret;
675
676 assert(new_count);
677 assert(stream_fd);
678
679 ret = close(out_fd);
680 if (ret < 0) {
681 PERROR("Closing tracefile");
682 goto error;
683 }
684
685 if (count > 0) {
686 *new_count = (*new_count + 1) % count;
687 } else {
688 (*new_count)++;
689 }
690
691 ret = utils_create_stream_file(path_name, file_name, size, *new_count,
692 uid, gid, 0);
693 if (ret < 0) {
694 goto error;
695 }
696 *stream_fd = ret;
697
698 /* Success. */
699 ret = 0;
700
701 error:
702 return ret;
703 }
704
705
706 /**
707 * Parse a string that represents a size in human readable format. It
708 * supports decimal integers suffixed by 'k', 'K', 'M' or 'G'.
709 *
710 * The suffix multiply the integer by:
711 * 'k': 1024
712 * 'M': 1024^2
713 * 'G': 1024^3
714 *
715 * @param str The string to parse.
716 * @param size Pointer to a uint64_t that will be filled with the
717 * resulting size.
718 *
719 * @return 0 on success, -1 on failure.
720 */
721 LTTNG_HIDDEN
722 int utils_parse_size_suffix(const char * const str, uint64_t * const size)
723 {
724 int ret;
725 uint64_t base_size;
726 long shift = 0;
727 const char *str_end;
728 char *num_end;
729
730 if (!str) {
731 DBG("utils_parse_size_suffix: received a NULL string.");
732 ret = -1;
733 goto end;
734 }
735
736 /* strtoull will accept a negative number, but we don't want to. */
737 if (strchr(str, '-') != NULL) {
738 DBG("utils_parse_size_suffix: invalid size string, should not contain '-'.");
739 ret = -1;
740 goto end;
741 }
742
743 /* str_end will point to the \0 */
744 str_end = str + strlen(str);
745 errno = 0;
746 base_size = strtoull(str, &num_end, 0);
747 if (errno != 0) {
748 PERROR("utils_parse_size_suffix strtoull");
749 ret = -1;
750 goto end;
751 }
752
753 if (num_end == str) {
754 /* strtoull parsed nothing, not good. */
755 DBG("utils_parse_size_suffix: strtoull had nothing good to parse.");
756 ret = -1;
757 goto end;
758 }
759
760 /* Check if a prefix is present. */
761 switch (*num_end) {
762 case 'G':
763 shift = GIBI_LOG2;
764 num_end++;
765 break;
766 case 'M': /* */
767 shift = MEBI_LOG2;
768 num_end++;
769 break;
770 case 'K':
771 case 'k':
772 shift = KIBI_LOG2;
773 num_end++;
774 break;
775 case '\0':
776 break;
777 default:
778 DBG("utils_parse_size_suffix: invalid suffix.");
779 ret = -1;
780 goto end;
781 }
782
783 /* Check for garbage after the valid input. */
784 if (num_end != str_end) {
785 DBG("utils_parse_size_suffix: Garbage after size string.");
786 ret = -1;
787 goto end;
788 }
789
790 *size = base_size << shift;
791
792 /* Check for overflow */
793 if ((*size >> shift) != base_size) {
794 DBG("utils_parse_size_suffix: oops, overflow detected.");
795 ret = -1;
796 goto end;
797 }
798
799 ret = 0;
800 end:
801 return ret;
802 }
803
804 /*
805 * fls: returns the position of the most significant bit.
806 * Returns 0 if no bit is set, else returns the position of the most
807 * significant bit (from 1 to 32 on 32-bit, from 1 to 64 on 64-bit).
808 */
809 #if defined(__i386) || defined(__x86_64)
810 static inline unsigned int fls_u32(uint32_t x)
811 {
812 int r;
813
814 asm("bsrl %1,%0\n\t"
815 "jnz 1f\n\t"
816 "movl $-1,%0\n\t"
817 "1:\n\t"
818 : "=r" (r) : "rm" (x));
819 return r + 1;
820 }
821 #define HAS_FLS_U32
822 #endif
823
824 #ifndef HAS_FLS_U32
825 static __attribute__((unused)) unsigned int fls_u32(uint32_t x)
826 {
827 unsigned int r = 32;
828
829 if (!x) {
830 return 0;
831 }
832 if (!(x & 0xFFFF0000U)) {
833 x <<= 16;
834 r -= 16;
835 }
836 if (!(x & 0xFF000000U)) {
837 x <<= 8;
838 r -= 8;
839 }
840 if (!(x & 0xF0000000U)) {
841 x <<= 4;
842 r -= 4;
843 }
844 if (!(x & 0xC0000000U)) {
845 x <<= 2;
846 r -= 2;
847 }
848 if (!(x & 0x80000000U)) {
849 x <<= 1;
850 r -= 1;
851 }
852 return r;
853 }
854 #endif
855
856 /*
857 * Return the minimum order for which x <= (1UL << order).
858 * Return -1 if x is 0.
859 */
860 LTTNG_HIDDEN
861 int utils_get_count_order_u32(uint32_t x)
862 {
863 if (!x) {
864 return -1;
865 }
866
867 return fls_u32(x - 1);
868 }
869
870 /**
871 * Obtain the value of LTTNG_HOME environment variable, if exists.
872 * Otherwise returns the value of HOME.
873 */
874 LTTNG_HIDDEN
875 char *utils_get_home_dir(void)
876 {
877 char *val = NULL;
878 struct passwd *pwd;
879
880 val = getenv(DEFAULT_LTTNG_HOME_ENV_VAR);
881 if (val != NULL) {
882 goto end;
883 }
884 val = getenv(DEFAULT_LTTNG_FALLBACK_HOME_ENV_VAR);
885 if (val != NULL) {
886 goto end;
887 }
888
889 /* Fallback on the password file entry. */
890 pwd = getpwuid(getuid());
891 if (!pwd) {
892 goto end;
893 }
894 val = pwd->pw_dir;
895
896 DBG3("Home directory is '%s'", val);
897
898 end:
899 return val;
900 }
901
902 /*
903 * With the given format, fill dst with the time of len maximum siz.
904 *
905 * Return amount of bytes set in the buffer or else 0 on error.
906 */
907 LTTNG_HIDDEN
908 size_t utils_get_current_time_str(const char *format, char *dst, size_t len)
909 {
910 size_t ret;
911 time_t rawtime;
912 struct tm *timeinfo;
913
914 assert(format);
915 assert(dst);
916
917 /* Get date and time for session path */
918 time(&rawtime);
919 timeinfo = localtime(&rawtime);
920 ret = strftime(dst, len, format, timeinfo);
921 if (ret == 0) {
922 ERR("Unable to strftime with format %s at dst %p of len %zu", format,
923 dst, len);
924 }
925
926 return ret;
927 }
928
929 /*
930 * Return the group ID matching name, else 0 if it cannot be found.
931 */
932 LTTNG_HIDDEN
933 gid_t utils_get_group_id(const char *name)
934 {
935 struct group *grp;
936
937 grp = getgrnam(name);
938 if (!grp) {
939 static volatile int warn_once;
940
941 if (!warn_once) {
942 WARN("No tracing group detected");
943 warn_once = 1;
944 }
945 return 0;
946 }
947 return grp->gr_gid;
948 }
This page took 0.047509 seconds and 4 git commands to generate.