Line data Source code
1 : /* dirinfo.c - Get directory information
2 : * Copyright (C) 2009, 2013 g10 Code GmbH
3 : *
4 : * This file is part of GPGME.
5 : *
6 : * GPGME is free software; you can redistribute it and/or modify it
7 : * under the terms of the GNU Lesser General Public License as
8 : * published by the Free Software Foundation; either version 2.1 of
9 : * the License, or (at your option) any later version.
10 : *
11 : * GPGME is distributed in the hope that it will be useful, but
12 : * WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : * Lesser General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU Lesser General Public
17 : * License along with this program; if not, see <https://www.gnu.org/licenses/>.
18 : */
19 :
20 : #if HAVE_CONFIG_H
21 : #include <config.h>
22 : #endif
23 :
24 : #include <stdlib.h>
25 : #include <string.h>
26 :
27 : #include "gpgme.h"
28 : #include "util.h"
29 : #include "priv-io.h"
30 : #include "debug.h"
31 : #include "sema.h"
32 : #include "sys-util.h"
33 :
34 : DEFINE_STATIC_LOCK (dirinfo_lock);
35 :
36 : /* Constants used internally to select the data. */
37 : enum
38 : {
39 : WANT_HOMEDIR,
40 : WANT_SYSCONFDIR,
41 : WANT_BINDIR,
42 : WANT_LIBEXECDIR,
43 : WANT_LIBDIR,
44 : WANT_DATADIR,
45 : WANT_LOCALEDIR,
46 : WANT_AGENT_SOCKET,
47 : WANT_AGENT_SSH_SOCKET,
48 : WANT_DIRMNGR_SOCKET,
49 : WANT_UISRV_SOCKET,
50 : WANT_GPGCONF_NAME,
51 : WANT_GPG_NAME,
52 : WANT_GPGSM_NAME,
53 : WANT_G13_NAME,
54 : WANT_GPG_ONE_MODE
55 : };
56 :
57 : /* Values retrieved via gpgconf and cached here. */
58 : static struct {
59 : int valid; /* Cached information is valid. */
60 : int disable_gpgconf;
61 : char *homedir;
62 : char *sysconfdir;
63 : char *bindir;
64 : char *libexecdir;
65 : char *libdir;
66 : char *datadir;
67 : char *localedir;
68 : char *agent_socket;
69 : char *agent_ssh_socket;
70 : char *dirmngr_socket;
71 : char *uisrv_socket;
72 : char *gpgconf_name;
73 : char *gpg_name;
74 : char *gpgsm_name;
75 : char *g13_name;
76 : int gpg_one_mode; /* System is in gpg1 mode. */
77 : } dirinfo;
78 :
79 :
80 :
81 : /* Helper function to be used only by gpgme_set_global_flag. */
82 : void
83 0 : _gpgme_dirinfo_disable_gpgconf (void)
84 : {
85 0 : dirinfo.disable_gpgconf = 1;
86 0 : }
87 :
88 :
89 : /* Return the length of the directory part including the trailing
90 : * slash of NAME. */
91 : static size_t
92 79 : dirname_len (const char *name)
93 : {
94 79 : return _gpgme_get_basename (name) - name;
95 : }
96 :
97 :
98 : /* Parse the output of "gpgconf --list-dirs". This function expects
99 : that DIRINFO_LOCK is held by the caller. If COMPONENTS is set, the
100 : output of --list-components is expected. */
101 : static void
102 1501 : parse_output (char *line, int components)
103 : {
104 : char *value, *p;
105 : size_t n;
106 :
107 1501 : value = strchr (line, ':');
108 1501 : if (!value)
109 0 : return;
110 1501 : *value++ = 0;
111 1501 : if (components)
112 : {
113 : /* Skip the second field. */
114 474 : value = strchr (value, ':');
115 474 : if (!value)
116 0 : return;
117 474 : *value++ = 0;
118 : }
119 1501 : p = strchr (value, ':');
120 1501 : if (p)
121 0 : *p = 0;
122 1501 : if (_gpgme_decode_percent_string (value, &value, strlen (value)+1, 0))
123 0 : return;
124 1501 : if (!*value)
125 0 : return;
126 :
127 1501 : if (components)
128 : {
129 474 : if (!strcmp (line, "gpg") && !dirinfo.gpg_name)
130 79 : dirinfo.gpg_name = strdup (value);
131 395 : else if (!strcmp (line, "gpgsm") && !dirinfo.gpgsm_name)
132 79 : dirinfo.gpgsm_name = strdup (value);
133 316 : else if (!strcmp (line, "g13") && !dirinfo.g13_name)
134 0 : dirinfo.g13_name = strdup (value);
135 : }
136 : else
137 : {
138 1027 : if (!strcmp (line, "homedir") && !dirinfo.homedir)
139 79 : dirinfo.homedir = strdup (value);
140 948 : else if (!strcmp (line, "sysconfdir") && !dirinfo.sysconfdir)
141 79 : dirinfo.sysconfdir = strdup (value);
142 869 : else if (!strcmp (line, "bindir") && !dirinfo.bindir)
143 79 : dirinfo.bindir = strdup (value);
144 790 : else if (!strcmp (line, "libexecdir") && !dirinfo.libexecdir)
145 79 : dirinfo.libexecdir = strdup (value);
146 711 : else if (!strcmp (line, "libdir") && !dirinfo.libdir)
147 79 : dirinfo.libdir = strdup (value);
148 632 : else if (!strcmp (line, "datadir") && !dirinfo.datadir)
149 79 : dirinfo.datadir = strdup (value);
150 553 : else if (!strcmp (line, "localedir") && !dirinfo.localedir)
151 79 : dirinfo.localedir = strdup (value);
152 474 : else if (!strcmp (line, "agent-socket") && !dirinfo.agent_socket)
153 79 : {
154 79 : const char name[] = "S.uiserver";
155 : char *buffer;
156 :
157 79 : dirinfo.agent_socket = strdup (value);
158 79 : if (dirinfo.agent_socket)
159 : {
160 79 : n = dirname_len (dirinfo.agent_socket);
161 79 : buffer = malloc (n + strlen (name) + 1);
162 79 : if (buffer)
163 : {
164 79 : strncpy (buffer, dirinfo.agent_socket, n);
165 79 : strcpy (buffer + n, name);
166 79 : dirinfo.uisrv_socket = buffer;
167 : }
168 : }
169 : }
170 395 : else if (!strcmp (line, "dirmngr-socket") && !dirinfo.dirmngr_socket)
171 79 : dirinfo.dirmngr_socket = strdup (value);
172 316 : else if (!strcmp (line, "agent-ssh-socket") && !dirinfo.agent_ssh_socket)
173 79 : dirinfo.agent_ssh_socket = strdup (value);
174 : }
175 : }
176 :
177 :
178 : /* Read the directory information from gpgconf. This function expects
179 : that DIRINFO_LOCK is held by the caller. PGNAME is the name of the
180 : gpgconf binary. If COMPONENTS is set, not the directories bit the
181 : name of the componeNts are read. */
182 : static void
183 158 : read_gpgconf_dirs (const char *pgmname, int components)
184 : {
185 158 : char linebuf[1024] = {0};
186 158 : int linelen = 0;
187 : char * argv[3];
188 : int rp[2];
189 158 : struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0},
190 : {-1, -1} };
191 : int status;
192 : int nread;
193 158 : char *mark = NULL;
194 :
195 158 : argv[0] = (char *)pgmname;
196 158 : argv[1] = (char*)(components? "--list-components" : "--list-dirs");
197 158 : argv[2] = NULL;
198 :
199 158 : if (_gpgme_io_pipe (rp, 1) < 0)
200 0 : return;
201 :
202 158 : cfd[0].fd = rp[1];
203 :
204 158 : status = _gpgme_io_spawn (pgmname, argv, IOSPAWN_FLAG_DETACHED,
205 : cfd, NULL, NULL, NULL);
206 158 : if (status < 0)
207 : {
208 0 : _gpgme_io_close (rp[0]);
209 0 : _gpgme_io_close (rp[1]);
210 0 : return;
211 : }
212 :
213 : do
214 : {
215 316 : nread = _gpgme_io_read (rp[0],
216 : linebuf + linelen,
217 : sizeof linebuf - linelen - 1);
218 316 : if (nread > 0)
219 : {
220 : char *line;
221 158 : const char *lastmark = NULL;
222 : size_t nused;
223 :
224 158 : linelen += nread;
225 158 : linebuf[linelen] = '\0';
226 :
227 1659 : for (line=linebuf; (mark = strchr (line, '\n')); line = mark+1 )
228 : {
229 1501 : lastmark = mark;
230 1501 : if (mark > line && mark[-1] == '\r')
231 0 : mark[-1] = '\0';
232 : else
233 1501 : mark[0] = '\0';
234 :
235 1501 : parse_output (line, components);
236 : }
237 :
238 158 : nused = lastmark? (lastmark + 1 - linebuf) : 0;
239 158 : memmove (linebuf, linebuf + nused, linelen - nused);
240 158 : linelen -= nused;
241 : }
242 : }
243 316 : while (nread > 0 && linelen < sizeof linebuf - 1);
244 :
245 158 : _gpgme_io_close (rp[0]);
246 : }
247 :
248 :
249 : static const char *
250 1256 : get_gpgconf_item (int what)
251 : {
252 1256 : const char *result = NULL;
253 :
254 1256 : LOCK (dirinfo_lock);
255 1258 : if (!dirinfo.valid)
256 : {
257 : char *pgmname;
258 :
259 79 : pgmname = dirinfo.disable_gpgconf? NULL : _gpgme_get_gpgconf_path ();
260 79 : if (pgmname && access (pgmname, F_OK))
261 : {
262 0 : _gpgme_debug (DEBUG_INIT,
263 : "gpgme-dinfo: gpgconf='%s' [not installed]\n", pgmname);
264 0 : free (pgmname);
265 0 : pgmname = NULL; /* Not available. */
266 : }
267 : else
268 79 : _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: gpgconf='%s'\n",
269 : pgmname? pgmname : "[null]");
270 79 : if (!pgmname)
271 : {
272 : /* Probably gpgconf is not installed. Assume we are using
273 : GnuPG-1. */
274 0 : dirinfo.gpg_one_mode = 1;
275 0 : pgmname = _gpgme_get_gpg_path ();
276 0 : if (pgmname)
277 0 : dirinfo.gpg_name = pgmname;
278 : }
279 : else
280 : {
281 79 : dirinfo.gpg_one_mode = 0;
282 79 : read_gpgconf_dirs (pgmname, 0);
283 79 : read_gpgconf_dirs (pgmname, 1);
284 79 : dirinfo.gpgconf_name = pgmname;
285 : }
286 : /* Even if the reading of the directories failed (e.g. due to an
287 : too old version gpgconf or no gpgconf at all), we need to
288 : mark the entries as valid so that we won't try over and over
289 : to read them. Note further that we are not able to change
290 : the read values later because they are practically statically
291 : allocated. */
292 79 : dirinfo.valid = 1;
293 79 : if (dirinfo.gpg_name)
294 79 : _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: gpg='%s'\n",
295 : dirinfo.gpg_name);
296 79 : if (dirinfo.g13_name)
297 0 : _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: g13='%s'\n",
298 : dirinfo.g13_name);
299 79 : if (dirinfo.gpgsm_name)
300 79 : _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: gpgsm='%s'\n",
301 : dirinfo.gpgsm_name);
302 79 : if (dirinfo.homedir)
303 79 : _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: homedir='%s'\n",
304 : dirinfo.homedir);
305 79 : if (dirinfo.agent_socket)
306 79 : _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: agent='%s'\n",
307 : dirinfo.agent_socket);
308 79 : if (dirinfo.agent_ssh_socket)
309 79 : _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: ssh='%s'\n",
310 : dirinfo.agent_ssh_socket);
311 79 : if (dirinfo.dirmngr_socket)
312 79 : _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: dirmngr='%s'\n",
313 : dirinfo.dirmngr_socket);
314 79 : if (dirinfo.uisrv_socket)
315 79 : _gpgme_debug (DEBUG_INIT, "gpgme-dinfo: uisrv='%s'\n",
316 : dirinfo.uisrv_socket);
317 : }
318 1258 : switch (what)
319 : {
320 1 : case WANT_HOMEDIR: result = dirinfo.homedir; break;
321 1 : case WANT_SYSCONFDIR: result = dirinfo.sysconfdir; break;
322 1 : case WANT_BINDIR: result = dirinfo.bindir; break;
323 1 : case WANT_LIBEXECDIR: result = dirinfo.libexecdir; break;
324 1 : case WANT_LIBDIR: result = dirinfo.libdir; break;
325 1 : case WANT_DATADIR: result = dirinfo.datadir; break;
326 1 : case WANT_LOCALEDIR: result = dirinfo.localedir; break;
327 80 : case WANT_AGENT_SOCKET: result = dirinfo.agent_socket; break;
328 1 : case WANT_AGENT_SSH_SOCKET: result = dirinfo.agent_ssh_socket; break;
329 1 : case WANT_DIRMNGR_SOCKET: result = dirinfo.dirmngr_socket; break;
330 159 : case WANT_GPGCONF_NAME: result = dirinfo.gpgconf_name; break;
331 159 : case WANT_GPG_NAME: result = dirinfo.gpg_name; break;
332 159 : case WANT_GPGSM_NAME: result = dirinfo.gpgsm_name; break;
333 159 : case WANT_G13_NAME: result = dirinfo.g13_name; break;
334 80 : case WANT_UISRV_SOCKET: result = dirinfo.uisrv_socket; break;
335 453 : case WANT_GPG_ONE_MODE: result = dirinfo.gpg_one_mode? "1":NULL; break;
336 : }
337 1258 : UNLOCK (dirinfo_lock);
338 1258 : return result;
339 : }
340 :
341 :
342 : /* Return the default home directory. Returns NULL if not known. */
343 : const char *
344 0 : _gpgme_get_default_homedir (void)
345 : {
346 0 : return get_gpgconf_item (WANT_HOMEDIR);
347 : }
348 :
349 : /* Return the default gpg-agent socket name. Returns NULL if not known. */
350 : const char *
351 79 : _gpgme_get_default_agent_socket (void)
352 : {
353 79 : return get_gpgconf_item (WANT_AGENT_SOCKET);
354 : }
355 :
356 : /* Return the default gpg file name. Returns NULL if not known. */
357 : const char *
358 158 : _gpgme_get_default_gpg_name (void)
359 : {
360 158 : return get_gpgconf_item (WANT_GPG_NAME);
361 : }
362 :
363 : /* Return the default gpgsm file name. Returns NULL if not known. */
364 : const char *
365 158 : _gpgme_get_default_gpgsm_name (void)
366 : {
367 158 : return get_gpgconf_item (WANT_GPGSM_NAME);
368 : }
369 :
370 : /* Return the default g13 file name. Returns NULL if not known. */
371 : const char *
372 158 : _gpgme_get_default_g13_name (void)
373 : {
374 158 : return get_gpgconf_item (WANT_G13_NAME);
375 : }
376 :
377 : /* Return the default gpgconf file name. Returns NULL if not known. */
378 : const char *
379 158 : _gpgme_get_default_gpgconf_name (void)
380 : {
381 158 : return get_gpgconf_item (WANT_GPGCONF_NAME);
382 : }
383 :
384 : /* Return the default UI-server socket name. Returns NULL if not
385 : known. */
386 : const char *
387 79 : _gpgme_get_default_uisrv_socket (void)
388 : {
389 79 : return get_gpgconf_item (WANT_UISRV_SOCKET);
390 : }
391 :
392 : /* Return true if we are in GnuPG-1 mode - ie. no gpgconf and agent
393 : being optional. */
394 : int
395 451 : _gpgme_in_gpg_one_mode (void)
396 : {
397 451 : return !!get_gpgconf_item (WANT_GPG_ONE_MODE);
398 : }
399 :
400 :
401 :
402 : /* Helper function to return the basename of the passed filename. */
403 : const char *
404 540 : _gpgme_get_basename (const char *name)
405 : {
406 : const char *s;
407 :
408 540 : if (!name || !*name)
409 0 : return name;
410 3260 : for (s = name + strlen (name) -1; s >= name; s--)
411 3260 : if (*s == '/'
412 : #ifdef HAVE_W32_SYSTEM
413 : || *s == '\\' || *s == ':'
414 : #endif
415 : )
416 540 : return s+1;
417 0 : return name;
418 : }
419 :
420 :
421 : /* Return default values for various directories and file names. */
422 : const char *
423 15 : gpgme_get_dirinfo (const char *what)
424 : {
425 15 : if (!what)
426 0 : return NULL;
427 15 : else if (!strcmp (what, "homedir"))
428 1 : return get_gpgconf_item (WANT_HOMEDIR);
429 14 : else if (!strcmp (what, "agent-socket"))
430 1 : return get_gpgconf_item (WANT_AGENT_SOCKET);
431 13 : else if (!strcmp (what, "uiserver-socket"))
432 1 : return get_gpgconf_item (WANT_UISRV_SOCKET);
433 12 : else if (!strcmp (what, "gpgconf-name"))
434 1 : return get_gpgconf_item (WANT_GPGCONF_NAME);
435 11 : else if (!strcmp (what, "gpg-name"))
436 1 : return get_gpgconf_item (WANT_GPG_NAME);
437 10 : else if (!strcmp (what, "gpgsm-name"))
438 1 : return get_gpgconf_item (WANT_GPGSM_NAME);
439 9 : else if (!strcmp (what, "g13-name"))
440 1 : return get_gpgconf_item (WANT_G13_NAME);
441 8 : else if (!strcmp (what, "agent-ssh-socket"))
442 1 : return get_gpgconf_item (WANT_AGENT_SSH_SOCKET);
443 7 : else if (!strcmp (what, "dirmngr-socket"))
444 1 : return get_gpgconf_item (WANT_DIRMNGR_SOCKET);
445 6 : else if (!strcmp (what, "sysconfdir"))
446 1 : return get_gpgconf_item (WANT_SYSCONFDIR);
447 5 : else if (!strcmp (what, "bindir"))
448 1 : return get_gpgconf_item (WANT_BINDIR);
449 4 : else if (!strcmp (what, "libexecdir"))
450 1 : return get_gpgconf_item (WANT_LIBEXECDIR);
451 3 : else if (!strcmp (what, "libdir"))
452 1 : return get_gpgconf_item (WANT_LIBDIR);
453 2 : else if (!strcmp (what, "datadir"))
454 1 : return get_gpgconf_item (WANT_DATADIR);
455 1 : else if (!strcmp (what, "localedir"))
456 1 : return get_gpgconf_item (WANT_LOCALEDIR);
457 : else
458 0 : return NULL;
459 : }
|