Home | History | Annotate | Download | only in linux-gnu
      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