Fix: remove use of stat()
[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>
81b86775
DG
30
31#include <common/common.h>
fe4477ee 32#include <common/runas.h>
81b86775
DG
33
34#include "utils.h"
35
36/*
37 * Return the realpath(3) of the path even if the last directory token does not
38 * exist. For example, with /tmp/test1/test2, if test2/ does not exist but the
39 * /tmp/test1 does, the real path is returned. In normal time, realpath(3)
40 * fails if the end point directory does not exist.
41 */
90e535ef 42LTTNG_HIDDEN
81b86775
DG
43char *utils_expand_path(const char *path)
44{
45 const char *end_path = path;
46 char *next, *cut_path = NULL, *expanded_path = NULL;
47
48 /* Safety net */
49 if (path == NULL) {
50 goto error;
51 }
52
53 /* Find last token delimited by '/' */
54 while ((next = strpbrk(end_path + 1, "/"))) {
55 end_path = next;
56 }
57
58 /* Cut last token from original path */
59 cut_path = strndup(path, end_path - path);
60
61 expanded_path = zmalloc(PATH_MAX);
62 if (expanded_path == NULL) {
63 PERROR("zmalloc expand path");
64 goto error;
65 }
66
67 expanded_path = realpath((char *)cut_path, expanded_path);
68 if (expanded_path == NULL) {
69 switch (errno) {
70 case ENOENT:
71 ERR("%s: No such file or directory", cut_path);
72 break;
73 default:
74 PERROR("realpath utils expand path");
75 break;
76 }
77 goto error;
78 }
79
80 /* Add end part to expanded path */
c30ce0b3 81 strncat(expanded_path, end_path, PATH_MAX - strlen(expanded_path) - 1);
81b86775
DG
82
83 free(cut_path);
84 return expanded_path;
85
86error:
87 free(expanded_path);
88 free(cut_path);
89 return NULL;
90}
91
92/*
93 * Create a pipe in dst.
94 */
90e535ef 95LTTNG_HIDDEN
81b86775
DG
96int utils_create_pipe(int *dst)
97{
98 int ret;
99
100 if (dst == NULL) {
101 return -1;
102 }
103
104 ret = pipe(dst);
105 if (ret < 0) {
106 PERROR("create pipe");
107 }
108
109 return ret;
110}
111
112/*
113 * Create pipe and set CLOEXEC flag to both fd.
114 *
115 * Make sure the pipe opened by this function are closed at some point. Use
116 * utils_close_pipe().
117 */
90e535ef 118LTTNG_HIDDEN
81b86775
DG
119int utils_create_pipe_cloexec(int *dst)
120{
121 int ret, i;
122
123 if (dst == NULL) {
124 return -1;
125 }
126
127 ret = utils_create_pipe(dst);
128 if (ret < 0) {
129 goto error;
130 }
131
132 for (i = 0; i < 2; i++) {
133 ret = fcntl(dst[i], F_SETFD, FD_CLOEXEC);
134 if (ret < 0) {
135 PERROR("fcntl pipe cloexec");
136 goto error;
137 }
138 }
139
140error:
141 return ret;
142}
143
144/*
145 * Close both read and write side of the pipe.
146 */
90e535ef 147LTTNG_HIDDEN
81b86775
DG
148void utils_close_pipe(int *src)
149{
150 int i, ret;
151
152 if (src == NULL) {
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 }
166 }
167}
a4b92340
DG
168
169/*
170 * Create a new string using two strings range.
171 */
90e535ef 172LTTNG_HIDDEN
a4b92340
DG
173char *utils_strdupdelim(const char *begin, const char *end)
174{
175 char *str;
176
177 str = zmalloc(end - begin + 1);
178 if (str == NULL) {
179 PERROR("zmalloc strdupdelim");
180 goto error;
181 }
182
183 memcpy(str, begin, end - begin);
184 str[end - begin] = '\0';
185
186error:
187 return str;
188}
b662582b
DG
189
190/*
191 * Set CLOEXEC flag to the give file descriptor.
192 */
90e535ef 193LTTNG_HIDDEN
b662582b
DG
194int utils_set_fd_cloexec(int fd)
195{
196 int ret;
197
198 if (fd < 0) {
199 ret = -EINVAL;
200 goto end;
201 }
202
203 ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
204 if (ret < 0) {
205 PERROR("fcntl cloexec");
206 ret = -errno;
207 }
208
209end:
210 return ret;
211}
35f90c40
DG
212
213/*
214 * Create pid file to the given path and filename.
215 */
90e535ef 216LTTNG_HIDDEN
35f90c40
DG
217int utils_create_pid_file(pid_t pid, const char *filepath)
218{
219 int ret;
220 FILE *fp;
221
222 assert(filepath);
223
224 fp = fopen(filepath, "w");
225 if (fp == NULL) {
226 PERROR("open pid file %s", filepath);
227 ret = -1;
228 goto error;
229 }
230
231 ret = fprintf(fp, "%d\n", pid);
232 if (ret < 0) {
233 PERROR("fprintf pid file");
234 }
235
236 fclose(fp);
237 DBG("Pid %d written in file %s", pid, filepath);
238error:
239 return ret;
240}
2d851108
DG
241
242/*
243 * Recursively create directory using the given path and mode.
244 *
245 * On success, return 0 else a negative error code.
246 */
90e535ef 247LTTNG_HIDDEN
2d851108
DG
248int utils_mkdir_recursive(const char *path, mode_t mode)
249{
250 char *p, tmp[PATH_MAX];
2d851108
DG
251 size_t len;
252 int ret;
253
254 assert(path);
255
256 ret = snprintf(tmp, sizeof(tmp), "%s", path);
257 if (ret < 0) {
258 PERROR("snprintf mkdir");
259 goto error;
260 }
261
262 len = ret;
263 if (tmp[len - 1] == '/') {
264 tmp[len - 1] = 0;
265 }
266
267 for (p = tmp + 1; *p; p++) {
268 if (*p == '/') {
269 *p = 0;
270 if (tmp[strlen(tmp) - 1] == '.' &&
271 tmp[strlen(tmp) - 2] == '.' &&
272 tmp[strlen(tmp) - 3] == '/') {
273 ERR("Using '/../' is not permitted in the trace path (%s)",
274 tmp);
275 ret = -1;
276 goto error;
277 }
0c7bcad5 278 ret = mkdir(tmp, mode);
2d851108 279 if (ret < 0) {
0c7bcad5
MD
280 if (errno != EEXIST) {
281 PERROR("mkdir recursive");
282 ret = -errno;
283 goto error;
2d851108
DG
284 }
285 }
286 *p = '/';
287 }
288 }
289
290 ret = mkdir(tmp, mode);
291 if (ret < 0) {
292 if (errno != EEXIST) {
293 PERROR("mkdir recursive last piece");
294 ret = -errno;
295 } else {
296 ret = 0;
297 }
298 }
299
300error:
301 return ret;
302}
fe4477ee
JD
303
304/*
305 * Create the stream tracefile on disk.
306 *
307 * Return 0 on success or else a negative value.
308 */
bc182241 309LTTNG_HIDDEN
fe4477ee
JD
310int utils_create_stream_file(char *path_name, char *file_name, uint64_t size,
311 uint64_t count, int uid, int gid)
312{
be96a7d1 313 int ret, out_fd, flags, mode;
fe4477ee
JD
314 char full_path[PATH_MAX], *path_name_id = NULL, *path;
315
316 assert(path_name);
317 assert(file_name);
318
319 ret = snprintf(full_path, sizeof(full_path), "%s/%s",
320 path_name, file_name);
321 if (ret < 0) {
322 PERROR("snprintf create output file");
323 goto error;
324 }
325
326 /*
327 * If we split the trace in multiple files, we have to add the count at the
328 * end of the tracefile name
329 */
330 if (size > 0) {
331 ret = asprintf(&path_name_id, "%s_%" PRIu64, full_path, count);
332 if (ret < 0) {
333 PERROR("Allocating path name ID");
334 goto error;
335 }
336 path = path_name_id;
337 } else {
338 path = full_path;
339 }
340
be96a7d1 341 flags = O_WRONLY | O_CREAT | O_TRUNC;
0f907de1 342 /* Open with 660 mode */
be96a7d1
DG
343 mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
344
345 if (uid < 0 || gid < 0) {
346 out_fd = open(path, flags, mode);
347 } else {
348 out_fd = run_as_open(path, flags, mode, uid, gid);
349 }
fe4477ee
JD
350 if (out_fd < 0) {
351 PERROR("open stream path %s", path);
352 goto error_open;
353 }
354 ret = out_fd;
355
356error_open:
357 free(path_name_id);
358error:
359 return ret;
360}
361
362/*
363 * Change the output tracefile according to the given size and count The
364 * new_count pointer is set during this operation.
365 *
366 * From the consumer, the stream lock MUST be held before calling this function
367 * because we are modifying the stream status.
368 *
369 * Return 0 on success or else a negative value.
370 */
bc182241 371LTTNG_HIDDEN
fe4477ee
JD
372int utils_rotate_stream_file(char *path_name, char *file_name, uint64_t size,
373 uint64_t count, int uid, int gid, int out_fd, uint64_t *new_count)
374{
375 int ret;
376
377 ret = close(out_fd);
378 if (ret < 0) {
379 PERROR("Closing tracefile");
380 goto error;
381 }
382
383 if (count > 0) {
384 *new_count = (*new_count + 1) % count;
385 } else {
386 (*new_count)++;
387 }
388
389 return utils_create_stream_file(path_name, file_name, size, *new_count,
390 uid, gid);
391error:
392 return ret;
393}
70d0b120
SM
394
395/**
396 * Prints the error message corresponding to a regex error code.
397 *
398 * @param errcode The error code.
399 * @param regex The regex object that produced the error code.
400 */
401static void regex_print_error(int errcode, regex_t *regex)
402{
403 /* Get length of error message and allocate accordingly */
404 size_t length;
405 char *buffer;
406
407 assert(regex != NULL);
408
409 length = regerror(errcode, regex, NULL, 0);
410 if (length == 0) {
411 ERR("regerror returned a length of 0");
412 return;
413 }
414
415 buffer = zmalloc(length);
416 if (!buffer) {
417 ERR("regex_print_error: zmalloc failed");
418 return;
419 }
420
421 /* Get and print error message */
422 regerror(errcode, regex, buffer, length);
423 ERR("regex error: %s\n", buffer);
424 free(buffer);
425
426}
427
428/**
429 * Parse a string that represents a size in human readable format. It
430 * supports decimal integers suffixed by 'k', 'M' or 'G'.
431 *
432 * The suffix multiply the integer by:
433 * 'k': 1024
434 * 'M': 1024^2
435 * 'G': 1024^3
436 *
437 * @param str The string to parse.
438 * @param size Pointer to a size_t that will be filled with the
cfa9a5a2 439 * resulting size.
70d0b120
SM
440 *
441 * @return 0 on success, -1 on failure.
442 */
443int utils_parse_size_suffix(char *str, uint64_t *size)
444{
445 regex_t regex;
446 int ret;
447 const int nmatch = 3;
448 regmatch_t suffix_match, matches[nmatch];
449 unsigned long long base_size;
450 long shift = 0;
451
452 if (!str) {
453 return 0;
454 }
455
456 /* Compile regex */
457 ret = regcomp(&regex, "^\\(0x\\)\\{0,1\\}[0-9][0-9]*\\([kKMG]\\{0,1\\}\\)$", 0);
458 if (ret != 0) {
459 regex_print_error(ret, &regex);
460 ret = -1;
461 goto end;
462 }
463
464 /* Match regex */
465 ret = regexec(&regex, str, nmatch, matches, 0);
466 if (ret != 0) {
467 ret = -1;
468 goto free;
469 }
470
471 /* There is a match ! */
472 errno = 0;
473 base_size = strtoull(str, NULL, 0);
474 if (errno != 0) {
475 PERROR("strtoull");
476 ret = -1;
477 goto free;
478 }
479
480 /* Check if there is a suffix */
481 suffix_match = matches[2];
482 if (suffix_match.rm_eo - suffix_match.rm_so == 1) {
483 switch (*(str + suffix_match.rm_so)) {
484 case 'K':
485 case 'k':
486 shift = KIBI_LOG2;
487 break;
488 case 'M':
489 shift = MEBI_LOG2;
490 break;
491 case 'G':
492 shift = GIBI_LOG2;
493 break;
494 default:
495 ERR("parse_human_size: invalid suffix");
496 ret = -1;
497 goto free;
498 }
499 }
500
501 *size = base_size << shift;
502
503 /* Check for overflow */
504 if ((*size >> shift) != base_size) {
505 ERR("parse_size_suffix: oops, overflow detected.");
506 ret = -1;
507 goto free;
508 }
509
510 ret = 0;
511
512free:
513 regfree(&regex);
514end:
515 return ret;
516}
cfa9a5a2
DG
517
518/*
519 * fls: returns the position of the most significant bit.
520 * Returns 0 if no bit is set, else returns the position of the most
521 * significant bit (from 1 to 32 on 32-bit, from 1 to 64 on 64-bit).
522 */
523#if defined(__i386) || defined(__x86_64)
524static inline unsigned int fls_u32(uint32_t x)
525{
526 int r;
527
528 asm("bsrl %1,%0\n\t"
529 "jnz 1f\n\t"
530 "movl $-1,%0\n\t"
531 "1:\n\t"
532 : "=r" (r) : "rm" (x));
533 return r + 1;
534}
535#define HAS_FLS_U32
536#endif
537
538#ifndef HAS_FLS_U32
539static __attribute__((unused)) unsigned int fls_u32(uint32_t x)
540{
541 unsigned int r = 32;
542
543 if (!x) {
544 return 0;
545 }
546 if (!(x & 0xFFFF0000U)) {
547 x <<= 16;
548 r -= 16;
549 }
550 if (!(x & 0xFF000000U)) {
551 x <<= 8;
552 r -= 8;
553 }
554 if (!(x & 0xF0000000U)) {
555 x <<= 4;
556 r -= 4;
557 }
558 if (!(x & 0xC0000000U)) {
559 x <<= 2;
560 r -= 2;
561 }
562 if (!(x & 0x80000000U)) {
563 x <<= 1;
564 r -= 1;
565 }
566 return r;
567}
568#endif
569
570/*
571 * Return the minimum order for which x <= (1UL << order).
572 * Return -1 if x is 0.
573 */
574LTTNG_HIDDEN
575int utils_get_count_order_u32(uint32_t x)
576{
577 if (!x) {
578 return -1;
579 }
580
581 return fls_u32(x - 1);
582}
This page took 0.065983 seconds and 5 git commands to generate.