+ val = lttng_secure_getenv(DEFAULT_LTTNG_FALLBACK_HOME_ENV_VAR);
+ if (val != NULL) {
+ goto end;
+ }
+
+ /* Fallback on the password file entry. */
+ pwd = getpwuid(getuid());
+ if (!pwd) {
+ goto end;
+ }
+ val = pwd->pw_dir;
+
+ DBG3("Home directory is '%s'", val);
+
+end:
+ return val;
+}
+
+/**
+ * Get user's home directory. Dynamically allocated, must be freed
+ * by the caller.
+ */
+LTTNG_HIDDEN
+char *utils_get_user_home_dir(uid_t uid)
+{
+ struct passwd pwd;
+ struct passwd *result;
+ char *home_dir = NULL;
+ char *buf = NULL;
+ long buflen;
+ int ret;
+
+ buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (buflen == -1) {
+ goto end;
+ }
+retry:
+ buf = zmalloc(buflen);
+ if (!buf) {
+ goto end;
+ }
+
+ ret = getpwuid_r(uid, &pwd, buf, buflen, &result);
+ if (ret || !result) {
+ if (ret == ERANGE) {
+ free(buf);
+ buflen *= 2;
+ goto retry;
+ }
+ goto end;
+ }
+
+ home_dir = strdup(pwd.pw_dir);
+end:
+ free(buf);
+ return home_dir;
+}
+
+/*
+ * 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 %zu", format,
+ dst, len);
+ }
+
+ return ret;
+}
+
+/*
+ * Return the group ID matching name, else 0 if it cannot be found.
+ */
+LTTNG_HIDDEN
+gid_t utils_get_group_id(const char *name)
+{
+ struct group *grp;
+
+ grp = getgrnam(name);
+ if (!grp) {
+ static volatile int warn_once;
+
+ if (!warn_once) {
+ WARN("No tracing group detected");
+ warn_once = 1;
+ }
+ return 0;
+ }
+ return grp->gr_gid;
+}
+
+/*
+ * Return a newly allocated option string. This string is to be used as the
+ * optstring argument of getopt_long(), see GETOPT(3). opt_count is the number
+ * of elements in the long_options array. Returns NULL if the string's
+ * allocation fails.
+ */
+LTTNG_HIDDEN
+char *utils_generate_optstring(const struct option *long_options,
+ size_t opt_count)
+{
+ int i;
+ size_t string_len = opt_count, str_pos = 0;
+ char *optstring;
+
+ /*
+ * Compute the necessary string length. One letter per option, two when an
+ * argument is necessary, and a trailing NULL.
+ */
+ for (i = 0; i < opt_count; i++) {
+ string_len += long_options[i].has_arg ? 1 : 0;
+ }
+
+ optstring = zmalloc(string_len);
+ if (!optstring) {
+ goto end;
+ }
+
+ for (i = 0; i < opt_count; i++) {
+ if (!long_options[i].name) {
+ /* Got to the trailing NULL element */
+ break;
+ }
+
+ if (long_options[i].val != '\0') {
+ optstring[str_pos++] = (char) long_options[i].val;
+ if (long_options[i].has_arg) {
+ optstring[str_pos++] = ':';
+ }
+ }
+ }
+
+end:
+ return optstring;
+}
+
+/*
+ * Try to remove a hierarchy of empty directories, recursively. Don't unlink
+ * any file. Try to rmdir any empty directory within the hierarchy.
+ */
+LTTNG_HIDDEN
+int utils_recursive_rmdir(const char *path)
+{
+ DIR *dir;
+ size_t path_len;
+ int dir_fd, ret = 0, closeret, is_empty = 1;
+ struct dirent *entry;
+
+ /* Open directory */
+ dir = opendir(path);
+ if (!dir) {
+ PERROR("Cannot open '%s' path", path);
+ return -1;
+ }
+ dir_fd = lttng_dirfd(dir);
+ if (dir_fd < 0) {
+ PERROR("lttng_dirfd");
+ return -1;
+ }
+
+ path_len = strlen(path);
+ while ((entry = readdir(dir))) {
+ struct stat st;
+ size_t name_len;
+ char filename[PATH_MAX];
+
+ if (!strcmp(entry->d_name, ".")
+ || !strcmp(entry->d_name, "..")) {
+ continue;
+ }
+
+ name_len = strlen(entry->d_name);
+ if (path_len + name_len + 2 > sizeof(filename)) {
+ ERR("Failed to remove file: path name too long (%s/%s)",
+ path, entry->d_name);
+ continue;
+ }
+ if (snprintf(filename, sizeof(filename), "%s/%s",
+ path, entry->d_name) < 0) {
+ ERR("Failed to format path.");
+ continue;
+ }
+
+ if (stat(filename, &st)) {
+ PERROR("stat");
+ continue;
+ }
+
+ if (S_ISDIR(st.st_mode)) {
+ char subpath[PATH_MAX];
+
+ strncpy(subpath, path, PATH_MAX);
+ subpath[PATH_MAX - 1] = '\0';
+ strncat(subpath, "/",
+ PATH_MAX - strlen(subpath) - 1);
+ strncat(subpath, entry->d_name,
+ PATH_MAX - strlen(subpath) - 1);
+ if (utils_recursive_rmdir(subpath)) {
+ is_empty = 0;
+ }
+ } else if (S_ISREG(st.st_mode)) {
+ is_empty = 0;
+ } else {
+ ret = -EINVAL;
+ goto end;
+ }
+ }
+end:
+ closeret = closedir(dir);
+ if (closeret) {
+ PERROR("closedir");
+ }
+ if (is_empty) {
+ DBG3("Attempting rmdir %s", path);
+ ret = rmdir(path);
+ }
+ return ret;
+}
+
+LTTNG_HIDDEN
+int utils_truncate_stream_file(int fd, off_t length)
+{
+ int ret;
+ off_t lseek_ret;
+
+ ret = ftruncate(fd, length);
+ if (ret < 0) {
+ PERROR("ftruncate");
+ goto end;
+ }
+ lseek_ret = lseek(fd, length, SEEK_SET);
+ if (lseek_ret < 0) {
+ PERROR("lseek");
+ ret = -1;
+ goto end;
+ }
+end:
+ return ret;
+}
+
+static const char *get_man_bin_path(void)
+{
+ char *env_man_path = lttng_secure_getenv(DEFAULT_MAN_BIN_PATH_ENV);
+
+ if (env_man_path) {
+ return env_man_path;
+ }
+
+ return DEFAULT_MAN_BIN_PATH;
+}
+
+LTTNG_HIDDEN
+int utils_show_help(int section, const char *page_name,
+ const char *help_msg)
+{
+ char section_string[8];
+ const char *man_bin_path = get_man_bin_path();
+ int ret = 0;
+
+ if (help_msg) {
+ printf("%s", help_msg);
+ goto end;
+ }
+
+ /* Section integer -> section string */
+ ret = sprintf(section_string, "%d", section);
+ assert(ret > 0 && ret < 8);
+
+ /*
+ * Execute man pager.
+ *
+ * We provide -M to man here because LTTng-tools can
+ * be installed outside /usr, in which case its man pages are
+ * not located in the default /usr/share/man directory.
+ */
+ ret = execlp(man_bin_path, "man", "-M", MANPATH,
+ section_string, page_name, NULL);
+
+end:
+ return ret;