| /* |
| * 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; |
| } |