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 *
append(const char * str1,const char * str2)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
add_dir(struct vect * dirs,const char * str1,const char * str2)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
add_dir_component_cb(struct opt_F_t * entry,void * data)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
destroy_opt_F_cb(struct opt_F_t * entry,void * data)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
os_get_config_dirs(int private,const char *** retp)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
os_get_ltrace_conf_filenames(struct vect * retp)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