X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fcommon%2Futils.c;h=dd9967312aca1be20253df8fd881c485858d0e6f;hp=54eed80bde14ad83228a090ce45638b0bba6c082;hb=24bc0841bdd3fb38b76a9244d21f28000d653fcf;hpb=bc1822412cd0b91e16119ae75755ed4cd414d992 diff --git a/src/common/utils.c b/src/common/utils.c index 54eed80bd..dd9967312 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -22,15 +22,17 @@ #include #include #include -#include #include +#include #include #include +#include #include #include #include "utils.h" +#include "defaults.h" /* * Return the realpath(3) of the path even if the last directory token does not @@ -140,6 +142,48 @@ error: return ret; } +/* + * Create pipe and set fd flags to FD_CLOEXEC and O_NONBLOCK. + * + * Make sure the pipe opened by this function are closed at some point. Use + * utils_close_pipe(). Using pipe() and fcntl rather than pipe2() to + * support OSes other than Linux 2.6.23+. + */ +LTTNG_HIDDEN +int utils_create_pipe_cloexec_nonblock(int *dst) +{ + int ret, i; + + if (dst == NULL) { + return -1; + } + + ret = utils_create_pipe(dst); + if (ret < 0) { + goto error; + } + + for (i = 0; i < 2; i++) { + ret = fcntl(dst[i], F_SETFD, FD_CLOEXEC); + if (ret < 0) { + PERROR("fcntl pipe cloexec"); + goto error; + } + /* + * Note: we override any flag that could have been + * previously set on the fd. + */ + ret = fcntl(dst[i], F_SETFL, O_NONBLOCK); + if (ret < 0) { + PERROR("fcntl pipe nonblock"); + goto error; + } + } + +error: + return ret; +} + /* * Close both read and write side of the pipe. */ @@ -247,7 +291,6 @@ LTTNG_HIDDEN int utils_mkdir_recursive(const char *path, mode_t mode) { char *p, tmp[PATH_MAX]; - struct stat statbuf; size_t len; int ret; @@ -275,15 +318,12 @@ int utils_mkdir_recursive(const char *path, mode_t mode) ret = -1; goto error; } - ret = stat(tmp, &statbuf); + ret = mkdir(tmp, mode); if (ret < 0) { - ret = mkdir(tmp, mode); - if (ret < 0) { - if (errno != EEXIST) { - PERROR("mkdir recursive"); - ret = -errno; - goto error; - } + if (errno != EEXIST) { + PERROR("mkdir recursive"); + ret = -errno; + goto error; } } *p = '/'; @@ -310,11 +350,12 @@ error: * Return 0 on success or else a negative value. */ LTTNG_HIDDEN -int utils_create_stream_file(char *path_name, char *file_name, uint64_t size, - uint64_t count, int uid, int gid) +int utils_create_stream_file(const char *path_name, char *file_name, uint64_t size, + uint64_t count, int uid, int gid, char *suffix) { - int ret, out_fd; - char full_path[PATH_MAX], *path_name_id = NULL, *path; + int ret, out_fd, flags, mode; + char full_path[PATH_MAX], *path_name_suffix = NULL, *path; + char *extra = NULL; assert(path_name); assert(file_name); @@ -326,24 +367,43 @@ int utils_create_stream_file(char *path_name, char *file_name, uint64_t size, goto error; } + /* Setup extra string if suffix or/and a count is needed. */ + if (size > 0 && suffix) { + ret = asprintf(&extra, "_%" PRIu64 "%s", count, suffix); + } else if (size > 0) { + ret = asprintf(&extra, "_%" PRIu64, count); + } else if (suffix) { + ret = asprintf(&extra, "%s", suffix); + } + if (ret < 0) { + PERROR("Allocating extra string to name"); + goto error; + } + /* * If we split the trace in multiple files, we have to add the count at the * end of the tracefile name */ - if (size > 0) { - ret = asprintf(&path_name_id, "%s_%" PRIu64, full_path, count); + if (extra) { + ret = asprintf(&path_name_suffix, "%s%s", full_path, extra); if (ret < 0) { - PERROR("Allocating path name ID"); - goto error; + PERROR("Allocating path name with extra string"); + goto error_free_suffix; } - path = path_name_id; + path = path_name_suffix; } else { path = full_path; } + flags = O_WRONLY | O_CREAT | O_TRUNC; /* Open with 660 mode */ - out_fd = run_as_open(path, O_WRONLY | O_CREAT | O_TRUNC, - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, uid, gid); + mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; + + if (uid < 0 || gid < 0) { + out_fd = open(path, flags, mode); + } else { + out_fd = run_as_open(path, flags, mode, uid, gid); + } if (out_fd < 0) { PERROR("open stream path %s", path); goto error_open; @@ -351,7 +411,9 @@ int utils_create_stream_file(char *path_name, char *file_name, uint64_t size, ret = out_fd; error_open: - free(path_name_id); + free(path_name_suffix); +error_free_suffix: + free(extra); error: return ret; } @@ -367,10 +429,14 @@ error: */ LTTNG_HIDDEN int utils_rotate_stream_file(char *path_name, char *file_name, uint64_t size, - uint64_t count, int uid, int gid, int out_fd, uint64_t *new_count) + uint64_t count, int uid, int gid, int out_fd, uint64_t *new_count, + int *stream_fd) { int ret; + assert(new_count); + assert(stream_fd); + ret = close(out_fd); if (ret < 0) { PERROR("Closing tracefile"); @@ -383,8 +449,248 @@ int utils_rotate_stream_file(char *path_name, char *file_name, uint64_t size, (*new_count)++; } - return utils_create_stream_file(path_name, file_name, size, *new_count, - uid, gid); + ret = utils_create_stream_file(path_name, file_name, size, *new_count, + uid, gid, 0); + if (ret < 0) { + goto error; + } + *stream_fd = ret; + + /* Success. */ + ret = 0; + error: return ret; } + +/** + * Prints the error message corresponding to a regex error code. + * + * @param errcode The error code. + * @param regex The regex object that produced the error code. + */ +static void regex_print_error(int errcode, regex_t *regex) +{ + /* Get length of error message and allocate accordingly */ + size_t length; + char *buffer; + + assert(regex != NULL); + + length = regerror(errcode, regex, NULL, 0); + if (length == 0) { + ERR("regerror returned a length of 0"); + return; + } + + buffer = zmalloc(length); + if (!buffer) { + ERR("regex_print_error: zmalloc failed"); + return; + } + + /* Get and print error message */ + regerror(errcode, regex, buffer, length); + ERR("regex error: %s\n", buffer); + free(buffer); + +} + +/** + * Parse a string that represents a size in human readable format. It + * supports decimal integers suffixed by 'k', 'M' or 'G'. + * + * The suffix multiply the integer by: + * 'k': 1024 + * 'M': 1024^2 + * 'G': 1024^3 + * + * @param str The string to parse. + * @param size Pointer to a size_t that will be filled with the + * resulting size. + * + * @return 0 on success, -1 on failure. + */ +LTTNG_HIDDEN +int utils_parse_size_suffix(char *str, uint64_t *size) +{ + regex_t regex; + int ret; + const int nmatch = 3; + regmatch_t suffix_match, matches[nmatch]; + unsigned long long base_size; + long shift = 0; + + if (!str) { + return 0; + } + + /* Compile regex */ + ret = regcomp(®ex, "^\\(0x\\)\\{0,1\\}[0-9][0-9]*\\([kKMG]\\{0,1\\}\\)$", 0); + if (ret != 0) { + regex_print_error(ret, ®ex); + ret = -1; + goto end; + } + + /* Match regex */ + ret = regexec(®ex, str, nmatch, matches, 0); + if (ret != 0) { + ret = -1; + goto free; + } + + /* There is a match ! */ + errno = 0; + base_size = strtoull(str, NULL, 0); + if (errno != 0) { + PERROR("strtoull"); + ret = -1; + goto free; + } + + /* Check if there is a suffix */ + suffix_match = matches[2]; + if (suffix_match.rm_eo - suffix_match.rm_so == 1) { + switch (*(str + suffix_match.rm_so)) { + case 'K': + case 'k': + shift = KIBI_LOG2; + break; + case 'M': + shift = MEBI_LOG2; + break; + case 'G': + shift = GIBI_LOG2; + break; + default: + ERR("parse_human_size: invalid suffix"); + ret = -1; + goto free; + } + } + + *size = base_size << shift; + + /* Check for overflow */ + if ((*size >> shift) != base_size) { + ERR("parse_size_suffix: oops, overflow detected."); + ret = -1; + goto free; + } + + ret = 0; + +free: + regfree(®ex); +end: + return ret; +} + +/* + * fls: returns the position of the most significant bit. + * Returns 0 if no bit is set, else returns the position of the most + * significant bit (from 1 to 32 on 32-bit, from 1 to 64 on 64-bit). + */ +#if defined(__i386) || defined(__x86_64) +static inline unsigned int fls_u32(uint32_t x) +{ + int r; + + asm("bsrl %1,%0\n\t" + "jnz 1f\n\t" + "movl $-1,%0\n\t" + "1:\n\t" + : "=r" (r) : "rm" (x)); + return r + 1; +} +#define HAS_FLS_U32 +#endif + +#ifndef HAS_FLS_U32 +static __attribute__((unused)) unsigned int fls_u32(uint32_t x) +{ + unsigned int r = 32; + + if (!x) { + return 0; + } + if (!(x & 0xFFFF0000U)) { + x <<= 16; + r -= 16; + } + if (!(x & 0xFF000000U)) { + x <<= 8; + r -= 8; + } + if (!(x & 0xF0000000U)) { + x <<= 4; + r -= 4; + } + if (!(x & 0xC0000000U)) { + x <<= 2; + r -= 2; + } + if (!(x & 0x80000000U)) { + x <<= 1; + r -= 1; + } + return r; +} +#endif + +/* + * Return the minimum order for which x <= (1UL << order). + * Return -1 if x is 0. + */ +LTTNG_HIDDEN +int utils_get_count_order_u32(uint32_t x) +{ + if (!x) { + return -1; + } + + return fls_u32(x - 1); +} + +/** + * Obtain the value of LTTNG_HOME environment variable, if exists. + * Otherwise returns the value of HOME. + */ +LTTNG_HIDDEN +char *utils_get_home_dir(void) +{ + char *val = NULL; + val = getenv(DEFAULT_LTTNG_HOME_ENV_VAR); + if (val != NULL) { + return val; + } + return getenv(DEFAULT_LTTNG_FALLBACK_HOME_ENV_VAR); +} + +/* + * With the given format, fill dst with the time of len maximum siz. + * + * Return amount of bytes set in the buffer or else 0 on error. + */ +LTTNG_HIDDEN +size_t utils_get_current_time_str(const char *format, char *dst, size_t len) +{ + size_t ret; + time_t rawtime; + struct tm *timeinfo; + + assert(format); + assert(dst); + + /* Get date and time for session path */ + time(&rawtime); + timeinfo = localtime(&rawtime); + ret = strftime(dst, len, format, timeinfo); + if (ret == 0) { + ERR("Unable to strftime with format %s at dst %p of len %lu", format, + dst, len); + } + + return ret; +}