1 /* 2 * This file is part of ltrace. 3 * Copyright (C) 2012, 2013 Petr Machata 4 * 5 * This program is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU General Public License as 7 * published by the Free Software Foundation; either version 2 of the 8 * License, or (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, but 11 * WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 18 * 02110-1301 USA 19 */ 20 21 #define _POSIX_C_SOURCE 200809L 22 #include <sys/types.h> 23 #include <alloca.h> 24 #include <errno.h> 25 #include <pwd.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <unistd.h> 30 31 #include "backend.h" 32 #include "dict.h" 33 #include "options.h" 34 #include "sysdep.h" 35 #include "vect.h" 36 37 static char * 38 append(const char *str1, const char *str2) 39 { 40 char *ret = malloc(strlen(str1) + strlen(str2) + 2); 41 if (ret == NULL) 42 return ret; 43 strcpy(stpcpy(ret, str1), str2); 44 return ret; 45 } 46 47 static void 48 add_dir(struct vect *dirs, const char *str1, const char *str2) 49 { 50 char *dir = append(str1, str2); 51 if (dir != NULL 52 && VECT_PUSHBACK(dirs, &dir) < 0) 53 fprintf(stderr, 54 "Couldn't store candidate config directory %s%s: %s.\n", 55 str1, str2, strerror(errno)); 56 } 57 58 static enum callback_status 59 add_dir_component_cb(struct opt_F_t *entry, void *data) 60 { 61 struct vect *dirs = data; 62 if (opt_F_get_kind(entry) == OPT_F_DIR) 63 add_dir(dirs, entry->pathname, "/ltrace"); 64 return CBS_CONT; 65 } 66 67 static void 68 destroy_opt_F_cb(struct opt_F_t *entry, void *data) 69 { 70 opt_F_destroy(entry); 71 } 72 73 static char *g_home_dir = NULL; 74 75 int 76 os_get_config_dirs(int private, const char ***retp) 77 { 78 /* Vector of char *. Contains first pointers to local paths, 79 * then NULL, then pointers to system paths, then another 80 * NULL. SYS_START points to the beginning of the second 81 * part. */ 82 static struct vect dirs; 83 static ssize_t sys_start = 0; 84 85 again: 86 if (sys_start != 0) { 87 if (sys_start == -1) 88 return -1; 89 90 if (retp != NULL) { 91 if (private) 92 *retp = VECT_ELEMENT(&dirs, const char *, 0); 93 else 94 *retp = VECT_ELEMENT(&dirs, const char *, 95 (size_t)sys_start); 96 } 97 98 return 0; 99 } 100 101 VECT_INIT(&dirs, char *); 102 103 char *home = getenv("HOME"); 104 if (home == NULL) { 105 struct passwd *pwd = getpwuid(getuid()); 106 if (pwd != NULL) 107 home = pwd->pw_dir; 108 } 109 110 size_t home_len = home != NULL ? strlen(home) : 0; 111 112 /* The values coming from getenv and getpwuid may not be 113 * persistent. */ 114 if (home != NULL) { 115 free(g_home_dir); 116 g_home_dir = strdup(home); 117 if (g_home_dir != NULL) { 118 home = g_home_dir; 119 } else { 120 char *tmp = alloca(home_len + 1); 121 strcpy(tmp, home); 122 home = tmp; 123 } 124 } 125 126 char *xdg_home = getenv("XDG_CONFIG_HOME"); 127 if (xdg_home == NULL && home != NULL) { 128 xdg_home = alloca(home_len + sizeof "/.config"); 129 sprintf(xdg_home, "%s/.config", home); 130 } 131 if (xdg_home != NULL) 132 add_dir(&dirs, xdg_home, "/ltrace"); 133 if (home != NULL) 134 add_dir(&dirs, home, "/.ltrace"); 135 136 char *delim = NULL; 137 if (VECT_PUSHBACK(&dirs, &delim) < 0) { 138 fail: 139 /* This can't work :( */ 140 fprintf(stderr, 141 "Couldn't initialize list of config directories: %s.\n", 142 strerror(errno)); 143 VECT_DESTROY(&dirs, const char *, dict_dtor_string, NULL); 144 sys_start = -1; 145 return -1; 146 } 147 sys_start = vect_size(&dirs); 148 149 /* """preference-ordered set of base directories to search for 150 * configuration files in addition to the $XDG_CONFIG_HOME 151 * base directory. The directories in $XDG_CONFIG_DIRS should 152 * be seperated with a colon ':'.""" */ 153 char *xdg_sys = getenv("XDG_CONFIG_DIRS"); 154 if (xdg_sys != NULL) { 155 struct vect v; 156 VECT_INIT(&v, struct opt_F_t); 157 if (parse_colon_separated_list(xdg_sys, &v) < 0 158 || VECT_EACH(&v, struct opt_F_t, NULL, 159 add_dir_component_cb, &dirs) != NULL) 160 fprintf(stderr, 161 "Error processing $XDG_CONFIG_DIRS '%s': %s\n", 162 xdg_sys, strerror(errno)); 163 VECT_DESTROY(&v, struct opt_F_t, destroy_opt_F_cb, NULL); 164 } 165 166 /* PKGDATADIR is passed via -D when compiling. */ 167 const char *pkgdatadir = PKGDATADIR; 168 if (pkgdatadir != NULL) 169 add_dir(&dirs, pkgdatadir, ""); 170 171 if (VECT_PUSHBACK(&dirs, &delim) < 0) 172 goto fail; 173 174 goto again; 175 } 176 177 int 178 os_get_ltrace_conf_filenames(struct vect *retp) 179 { 180 char *homepath = NULL; 181 char *syspath = NULL; 182 183 #define FN ".ltrace.conf" 184 if (g_home_dir == NULL) 185 os_get_config_dirs(0, NULL); 186 187 if (g_home_dir != NULL) { 188 homepath = malloc(strlen(g_home_dir) + 1 + sizeof FN); 189 if (homepath == NULL 190 || sprintf(homepath, "%s/%s", g_home_dir, FN) < 0) { 191 fail: 192 free(syspath); 193 free(homepath); 194 return -1; 195 } 196 } 197 198 /* SYSCONFDIR is passed via -D when compiling. */ 199 const char *sysconfdir = SYSCONFDIR; 200 if (sysconfdir != NULL && *sysconfdir != '\0') { 201 /* No +1, we skip the initial period. */ 202 syspath = malloc(strlen(sysconfdir) + sizeof FN); 203 if (syspath == NULL 204 || sprintf(syspath, "%s/%s", sysconfdir, FN + 1) < 0) 205 goto fail; 206 } 207 208 if (VECT_PUSHBACK(retp, &homepath) < 0 209 || VECT_PUSHBACK(retp, &syspath) < 0) 210 goto fail; 211 212 return 0; 213 } 214