/* * This file is part of ltrace. * Copyright (C) 2012, 2013 Petr Machata * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA */ #define _POSIX_C_SOURCE 200809L #include <sys/types.h> #include <alloca.h> #include <errno.h> #include <pwd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "backend.h" #include "dict.h" #include "options.h" #include "sysdep.h" #include "vect.h" static char * append(const char *str1, const char *str2) { char *ret = malloc(strlen(str1) + strlen(str2) + 2); if (ret == NULL) return ret; strcpy(stpcpy(ret, str1), str2); return ret; } static void add_dir(struct vect *dirs, const char *str1, const char *str2) { char *dir = append(str1, str2); if (dir != NULL && VECT_PUSHBACK(dirs, &dir) < 0) fprintf(stderr, "Couldn't store candidate config directory %s%s: %s.\n", str1, str2, strerror(errno)); } static enum callback_status add_dir_component_cb(struct opt_F_t *entry, void *data) { struct vect *dirs = data; if (opt_F_get_kind(entry) == OPT_F_DIR) add_dir(dirs, entry->pathname, "/ltrace"); return CBS_CONT; } static void destroy_opt_F_cb(struct opt_F_t *entry, void *data) { opt_F_destroy(entry); } static char *g_home_dir = NULL; int os_get_config_dirs(int private, const char ***retp) { /* Vector of char *. Contains first pointers to local paths, * then NULL, then pointers to system paths, then another * NULL. SYS_START points to the beginning of the second * part. */ static struct vect dirs; static ssize_t sys_start = 0; again: if (sys_start != 0) { if (sys_start == -1) return -1; if (retp != NULL) { if (private) *retp = VECT_ELEMENT(&dirs, const char *, 0); else *retp = VECT_ELEMENT(&dirs, const char *, (size_t)sys_start); } return 0; } VECT_INIT(&dirs, char *); char *home = getenv("HOME"); if (home == NULL) { struct passwd *pwd = getpwuid(getuid()); if (pwd != NULL) home = pwd->pw_dir; } size_t home_len = home != NULL ? strlen(home) : 0; /* The values coming from getenv and getpwuid may not be * persistent. */ if (home != NULL) { free(g_home_dir); g_home_dir = strdup(home); if (g_home_dir != NULL) { home = g_home_dir; } else { char *tmp = alloca(home_len + 1); strcpy(tmp, home); home = tmp; } } char *xdg_home = getenv("XDG_CONFIG_HOME"); if (xdg_home == NULL && home != NULL) { xdg_home = alloca(home_len + sizeof "/.config"); sprintf(xdg_home, "%s/.config", home); } if (xdg_home != NULL) add_dir(&dirs, xdg_home, "/ltrace"); if (home != NULL) add_dir(&dirs, home, "/.ltrace"); char *delim = NULL; if (VECT_PUSHBACK(&dirs, &delim) < 0) { fail: /* This can't work :( */ fprintf(stderr, "Couldn't initialize list of config directories: %s.\n", strerror(errno)); VECT_DESTROY(&dirs, const char *, dict_dtor_string, NULL); sys_start = -1; return -1; } sys_start = vect_size(&dirs); /* """preference-ordered set of base directories to search for * configuration files in addition to the $XDG_CONFIG_HOME * base directory. The directories in $XDG_CONFIG_DIRS should * be seperated with a colon ':'.""" */ char *xdg_sys = getenv("XDG_CONFIG_DIRS"); if (xdg_sys != NULL) { struct vect v; VECT_INIT(&v, struct opt_F_t); if (parse_colon_separated_list(xdg_sys, &v) < 0 || VECT_EACH(&v, struct opt_F_t, NULL, add_dir_component_cb, &dirs) != NULL) fprintf(stderr, "Error processing $XDG_CONFIG_DIRS '%s': %s\n", xdg_sys, strerror(errno)); VECT_DESTROY(&v, struct opt_F_t, destroy_opt_F_cb, NULL); } /* PKGDATADIR is passed via -D when compiling. */ const char *pkgdatadir = PKGDATADIR; if (pkgdatadir != NULL) add_dir(&dirs, pkgdatadir, ""); if (VECT_PUSHBACK(&dirs, &delim) < 0) goto fail; goto again; } int os_get_ltrace_conf_filenames(struct vect *retp) { char *homepath = NULL; char *syspath = NULL; #define FN ".ltrace.conf" if (g_home_dir == NULL) os_get_config_dirs(0, NULL); if (g_home_dir != NULL) { homepath = malloc(strlen(g_home_dir) + 1 + sizeof FN); if (homepath == NULL || sprintf(homepath, "%s/%s", g_home_dir, FN) < 0) { fail: free(syspath); free(homepath); return -1; } } /* SYSCONFDIR is passed via -D when compiling. */ const char *sysconfdir = SYSCONFDIR; if (sysconfdir != NULL && *sysconfdir != '\0') { /* No +1, we skip the initial period. */ syspath = malloc(strlen(sysconfdir) + sizeof FN); if (syspath == NULL || sprintf(syspath, "%s/%s", sysconfdir, FN + 1) < 0) goto fail; } if (VECT_PUSHBACK(retp, &homepath) < 0 || VECT_PUSHBACK(retp, &syspath) < 0) goto fail; return 0; }