/* SPDX-License-Identifier: GPL-3.0-only */ #include "tf.h" DEFINE_TF_ERRLIST; int atoi64(const char *str, int64_t *value) { char *endptr; long long tmp; errno = 0; /* To distinguish success/failure after call */ tmp = strtoll(str, &endptr, 10); if (errno == ERANGE && (tmp == LLONG_MAX || tmp == LLONG_MIN)) return -E_ATOI_OVERFLOW; /* * If there were no digits at all, strtoll() stores the original value * of str in *endptr. */ if (endptr == str) return -E_ATOI_NO_DIGITS; /* * The implementation may also set errno and return 0 in case no * conversion was performed. */ if (errno != 0 && tmp == 0) return -E_ATOI_NO_DIGITS; if (*endptr != '\0') /* Further characters after number */ return -E_ATOI_JUNK_AT_END; *value = tmp; return 1; } __attribute__ ((warn_unused_result)) void *xrealloc(void *p, size_t size) { /* * No need to check for NULL pointers: If p is NULL, the call * to realloc is equivalent to malloc(size) */ assert(size); if (!(p = realloc(p, size))) { EMERG_LOG("realloc failed (size = %zu), aborting\n", size); exit(EXIT_FAILURE); } return p; } __attribute__ ((warn_unused_result)) void *xmalloc(size_t size) { return xrealloc(NULL, size); } __attribute__ ((warn_unused_result)) void *xcalloc(size_t size) { void *p = xmalloc(size); memset(p, 0, size); return p; } __attribute__ ((warn_unused_result)) char *xstrdup(const char *str) { char *p = strdup(str); if (p) return p; EMERG_LOG("strdup failed, aborting\n"); exit(EXIT_FAILURE); } /* * Get the home directory of the current user. Returns a dynamically allocated * string that must be freed by the caller. */ __attribute__ ((warn_unused_result)) char *get_homedir(void) { struct passwd *pw = getpwuid(getuid()); if (!pw) { EMERG_LOG("could not get home directory: %s\n", strerror(errno)); exit(EXIT_FAILURE); } return xstrdup(pw->pw_dir); } /* * Print a formated message to a dynamically allocated string. * * This function is similar to vasprintf(), a GNU extension which is not in C * or POSIX. It allocates a string large enough to hold the output including * the terminating null byte. The allocated string is returned via the first * argument and must be freed by the caller. However, unlike vasprintf(), this * function calls exit() if insufficient memory is available, while vasprintf() * returns -1 in this case. * * It returns the number of bytes written, not including the terminating \p * NULL character. */ __attribute__ ((format (printf, 2, 0))) unsigned xvasprintf(char **result, const char *fmt, va_list ap) { int ret; size_t size = 150; va_list aq; *result = xmalloc(size + 1); va_copy(aq, ap); ret = vsnprintf(*result, size, fmt, aq); va_end(aq); assert(ret >= 0); if ((size_t)ret < size) return ret; size = ret + 1; *result = xrealloc(*result, size); va_copy(aq, ap); ret = vsnprintf(*result, size, fmt, aq); va_end(aq); assert(ret >= 0 && (size_t)ret < size); return ret; } __attribute__ ((format (printf, 2, 3))) /* Print to a dynamically allocated string, variable number of arguments. */ unsigned xasprintf(char **result, const char *fmt, ...) { va_list ap; unsigned ret; va_start(ap, fmt); ret = xvasprintf(result, fmt, ap); va_end(ap); return ret; } /* * Compile a regular expression. * * This simple wrapper calls regcomp(3) and logs a message on errors. */ int xregcomp(regex_t *preg, const char *regex, int cflags) { char *buf; size_t size; int ret = regcomp(preg, regex, cflags); if (ret == 0) return 1; size = regerror(ret, preg, NULL, 0); buf = xmalloc(size); regerror(ret, preg, buf, size); ERROR_LOG("%s\n", buf); free(buf); return -E_REGEX; } /* Write input from fd to dynamically allocated buffer. */ int fd2buf(int fd, struct iovec *result) { const size_t max_size = 32 * 1024; size_t loaded = 0; int ret; struct iovec iov; iov.iov_len = 100; /* guess that's sufficient */ iov.iov_base = xmalloc(iov.iov_len); for (;;) { ret = read(fd, iov.iov_base + loaded, iov.iov_len - loaded); if (ret < 0) ret = -ERRNO_TO_TF_ERROR(errno); if (ret <= 0) break; loaded += ret; if (loaded >= iov.iov_len) { iov.iov_len *= 2; ret = -ERRNO_TO_TF_ERROR(EOVERFLOW); if (iov.iov_len >= max_size) break; iov.iov_base = xrealloc(iov.iov_base, iov.iov_len); } }; if (ret < 0) { free(iov.iov_base); result->iov_base = NULL; result->iov_len = 0; return ret; } iov.iov_len = loaded; iov.iov_base = xrealloc(iov.iov_base, loaded + 1); *((char *)iov.iov_base + loaded) = '\0'; *result = iov; return 1; } __attribute__ ((format (printf, 2, 3))) void tf_log(int ll, const char* fmt,...) { va_list argp; if (ll < loglevel_arg_val) return; va_start(argp, fmt); vfprintf(stderr, fmt, argp); va_end(argp); } struct regfile_iter { DIR *dir; int dfd; struct dirent *entry; struct stat stat; }; void regfile_iter_next(struct regfile_iter *iter) { for (;;) { iter->entry = readdir(iter->dir); if (!iter->entry) return; if (!strcmp(iter->entry->d_name, ".")) continue; if (!strcmp(iter->entry->d_name, "..")) continue; if (fstatat(iter->dfd, iter->entry->d_name, &iter->stat, 0) == -1) continue; if (!S_ISREG(iter->stat.st_mode)) continue; if (iter->stat.st_size == 0) /* skip over empty files */ continue; return; } } void regfile_iter_new(const char *dirname, struct regfile_iter **result) { struct regfile_iter *iter; DIR *dir = opendir(dirname); if (!dir) { NOTICE_LOG("opendir %s: %s\n", dirname, strerror(errno)); *result = NULL; return; } iter = xmalloc(sizeof(*iter)); iter->dir = dir; iter->dfd = dirfd(iter->dir); assert(iter->dfd >= 0); regfile_iter_next(iter); *result = iter; } static void *xmmap(size_t sz, int fd, const char *path) { void *result = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, fd, 0); if (result == MAP_FAILED) { EMERG_LOG("mmap %s: %s\n", path, strerror(errno)); exit(EXIT_FAILURE); } return result; } void mmap_file(const char *path, struct iovec *iov) { int fd, ret; ret = open(path, 0); if (ret < 0) { EMERG_LOG("could not open %s: %s\n", path, strerror(errno)); exit(EXIT_FAILURE); } fd = ret; iov->iov_len = lseek(fd, 0, SEEK_END); if (iov->iov_len == (size_t)(off_t)-1) { EMERG_LOG("lseek %s: %s\n", path, strerror(errno)); exit(EXIT_FAILURE); } iov->iov_base = xmmap(iov->iov_len, fd, path); close(fd); } bool regfile_iter_map(const struct regfile_iter *iter, struct iovec *result) { int ret, fd; void *map; const char *path; if (!iter || !iter->entry) return false; path = iter->entry->d_name; ret = openat(iter->dfd, path, O_RDONLY, 0); if (ret < 0) { EMERG_LOG("could not open %s: %s\n", path, strerror(errno)); exit(EXIT_FAILURE); } fd = ret; map = xmmap(iter->stat.st_size, fd, path); close(fd); result->iov_len = iter->stat.st_size; result->iov_base = map; return true; } const char *regfile_iter_basename(const struct regfile_iter *iter) { if (!iter || !iter->entry) return NULL; return iter->entry->d_name; } const struct stat *regfile_iter_stat(const struct regfile_iter *iter) { return iter? &iter->stat : NULL; } void regfile_iter_free(struct regfile_iter *iter) { if (!iter) return; closedir(iter->dir); free(iter); }