prototype.c revision 4c4e4012db96aee7ef236c0684690842fb6a3e47
1/*
2 * This file is part of ltrace.
3 * Copyright (C) 2012 Petr Machata, Red Hat Inc.
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#include <alloca.h>
22#include <errno.h>
23#include <stdlib.h>
24#include <string.h>
25#include <stdio.h>
26
27#include "callback.h"
28#include "param.h"
29#include "prototype.h"
30#include "type.h"
31#include "options.h"
32#include "read_config_file.h"
33#include "backend.h"
34
35struct protolib_cache g_protocache;
36static struct protolib legacy_typedefs;
37
38void
39prototype_init(struct prototype *proto)
40{
41	VECT_INIT(&proto->params, struct param);
42
43	proto->return_info = NULL;
44	proto->own_return_info = 0;
45}
46
47static void
48param_destroy_cb(struct param *param, void *data)
49{
50	param_destroy(param);
51}
52
53void
54prototype_destroy(struct prototype *proto)
55{
56	if (proto == NULL)
57		return;
58	if (proto->own_return_info) {
59		type_destroy(proto->return_info);
60		free(proto->return_info);
61	}
62
63	VECT_DESTROY(&proto->params, struct param, &param_destroy_cb, NULL);
64}
65
66int
67prototype_push_param(struct prototype *proto, struct param *param)
68{
69	return VECT_PUSHBACK(&proto->params, param);
70}
71
72size_t
73prototype_num_params(struct prototype *proto)
74{
75	return vect_size(&proto->params);
76}
77
78void
79prototype_destroy_nth_param(struct prototype *proto, size_t n)
80{
81	assert(n < prototype_num_params(proto));
82	VECT_ERASE(&proto->params, struct param, n, n+1,
83		   &param_destroy_cb, NULL);
84}
85
86struct param *
87prototype_get_nth_param(struct prototype *proto, size_t n)
88{
89	assert(n < prototype_num_params(proto));
90	return VECT_ELEMENT(&proto->params, struct param, n);
91}
92
93struct each_param_data {
94	struct prototype *proto;
95	enum callback_status (*cb)(struct prototype *, struct param *, void *);
96	void *data;
97};
98
99static enum callback_status
100each_param_cb(struct param *param, void *data)
101{
102	struct each_param_data *cb_data = data;
103	return (cb_data->cb)(cb_data->proto, param, cb_data->data);
104}
105
106struct param *
107prototype_each_param(struct prototype *proto, struct param *start_after,
108		     enum callback_status (*cb)(struct prototype *,
109						struct param *, void *),
110		     void *data)
111{
112	struct each_param_data cb_data = { proto, cb, data };
113	return VECT_EACH(&proto->params, struct param, start_after,
114			 &each_param_cb, &cb_data);
115}
116
117void
118named_type_init(struct named_type *named,
119		struct arg_type_info *info, int own_type)
120{
121	named->info = info;
122	named->own_type = own_type;
123	named->forward = 0;
124}
125
126void
127named_type_destroy(struct named_type *named)
128{
129	if (named->own_type) {
130		type_destroy(named->info);
131		free(named->info);
132	}
133}
134
135void
136protolib_init(struct protolib *plib)
137{
138	DICT_INIT(&plib->prototypes, const char *, struct prototype,
139		  dict_hash_string, dict_eq_string, NULL);
140
141	DICT_INIT(&plib->named_types, const char *, struct named_type,
142		  dict_hash_string, dict_eq_string, NULL);
143
144	VECT_INIT(&plib->imports, struct protolib *);
145}
146
147static void
148destroy_prototype_cb(struct prototype *proto, void *data)
149{
150	prototype_destroy(proto);
151}
152
153static void
154destroy_named_type_cb(struct named_type *named, void *data)
155{
156	named_type_destroy(named);
157}
158
159void
160protolib_destroy(struct protolib *plib)
161{
162	VECT_DESTROY(&plib->imports, struct prototype *, NULL, NULL);
163
164	DICT_DESTROY(&plib->prototypes, const char *, struct prototype,
165		     dict_dtor_string, destroy_prototype_cb, NULL);
166
167	DICT_DESTROY(&plib->named_types, const char *, struct named_type,
168		     dict_dtor_string, destroy_named_type_cb, NULL);
169}
170
171static struct protolib **
172each_import(struct protolib *plib, struct protolib **start_after,
173	    enum callback_status (*cb)(struct protolib **, void *), void *data)
174{
175	assert(plib != NULL);
176	return VECT_EACH(&plib->imports, struct protolib *,
177			 start_after, cb, data);
178}
179
180static enum callback_status
181is_or_imports(struct protolib **plibp, void *data)
182{
183	assert(plibp != NULL);
184	assert(*plibp != NULL);
185	struct protolib *import = data;
186	if (*plibp == import
187	    || each_import(*plibp, NULL, &is_or_imports, import) != NULL)
188		return CBS_STOP;
189	else
190		return CBS_CONT;
191}
192
193int
194protolib_add_import(struct protolib *plib, struct protolib *import)
195{
196	assert(plib != NULL);
197	assert(import != NULL);
198	if (is_or_imports(&import, plib) == CBS_STOP) {
199		fprintf(stderr, "Recursive import rejected.\n");
200		return -2;
201	}
202
203	return VECT_PUSHBACK(&plib->imports, &import) < 0 ? -1 : 0;
204}
205
206static int
207clone_if_not_own(const char **strp, int own)
208{
209	assert(*strp != NULL);
210
211	if (!own) {
212		*strp = strdup(*strp);
213		if (*strp == NULL)
214			return -1;
215	}
216
217	return 0;
218}
219
220static int
221bailout(const char *name, int own)
222{
223	int save_errno = errno;
224	if (own)
225		free((char *)name);
226	errno = save_errno;
227	return -1;
228}
229
230int
231protolib_add_prototype(struct protolib *plib, const char *name, int own_name,
232		       struct prototype *proto)
233{
234	assert(plib != NULL);
235	if (clone_if_not_own(&name, own_name) < 0)
236		return -1;
237	if (DICT_INSERT(&plib->prototypes, &name, proto) < 0)
238		return bailout(name, own_name);
239	return 0;
240}
241
242int
243protolib_add_named_type(struct protolib *plib, const char *name, int own_name,
244			struct named_type *named)
245{
246	assert(plib != NULL);
247	if (clone_if_not_own(&name, own_name) < 0)
248		return -1;
249	if (DICT_INSERT(&plib->named_types, &name, named) < 0)
250		return bailout(name, own_name);
251	return 0;
252}
253
254struct lookup {
255	const char *name;
256	void *result;
257	struct dict *(*getter)(struct protolib *plib);
258};
259
260static struct dict *
261get_prototypes(struct protolib *plib)
262{
263	assert(plib != NULL);
264	return &plib->prototypes;
265}
266
267static struct dict *
268get_named_types(struct protolib *plib)
269{
270	assert(plib != NULL);
271	return &plib->named_types;
272}
273
274static enum callback_status
275protolib_lookup_rec(struct protolib **plibp, void *data)
276{
277	assert(plibp != NULL);
278	assert(*plibp != NULL);
279	struct lookup *lookup = data;
280	struct dict *dict = (*lookup->getter)(*plibp);
281
282	lookup->result = dict_find(dict, &lookup->name);
283	if (lookup->result != NULL)
284		return CBS_STOP;
285
286	if (each_import(*plibp, NULL, &protolib_lookup_rec, lookup) != NULL) {
287		assert(lookup->result != NULL);
288		return CBS_STOP;
289	}
290
291	return CBS_CONT;
292}
293
294static void *
295protolib_lookup(struct protolib *plib, const char *name,
296		struct dict *(*getter)(struct protolib *))
297{
298	assert(plib != NULL);
299	struct lookup lookup = { name, NULL, getter };
300	if (protolib_lookup_rec(&plib, &lookup) == CBS_STOP)
301		assert(lookup.result != NULL);
302	else
303		assert(lookup.result == NULL);
304	return lookup.result;
305}
306
307struct prototype *
308protolib_lookup_prototype(struct protolib *plib, const char *name)
309{
310	assert(plib != NULL);
311	return protolib_lookup(plib, name, &get_prototypes);
312}
313
314struct named_type *
315protolib_lookup_type(struct protolib *plib, const char *name)
316{
317	assert(plib != NULL);
318	return protolib_lookup(plib, name, &get_named_types);
319}
320
321static void
322destroy_protolib_cb(struct protolib **plibp, void *data)
323{
324	assert(plibp != NULL);
325	assert(*plibp != NULL);
326	protolib_destroy(*plibp);
327}
328
329void
330protolib_cache_destroy(struct protolib_cache *cache)
331{
332	DICT_DESTROY(&cache->protolibs, const char *, struct protolib *,
333		     dict_dtor_string, destroy_protolib_cb, NULL);
334}
335
336struct load_config_data {
337	struct protolib_cache *self;
338	const char *key;
339	struct protolib *result;
340};
341
342static struct protolib *
343consider_config_dir(struct protolib_cache *cache,
344		    const char *path, const char *key)
345{
346	size_t len = sizeof ".conf";
347	char slash[2] = {'/'};
348	char *buf = alloca(strlen(path) + 1 + strlen(key) + len);
349	strcpy(stpcpy(stpcpy(stpcpy(buf, path), slash), key), ".conf");
350
351	return protolib_cache_file(cache, buf, 0);
352}
353
354static enum callback_status
355consider_confdir_cb(struct opt_F_t *entry, void *d)
356{
357	if (opt_F_get_kind(entry) != OPT_F_DIR)
358		return CBS_CONT;
359	struct load_config_data *data = d;
360
361	data->result = consider_config_dir(data->self,
362					   entry->pathname, data->key);
363	return data->result != NULL ? CBS_STOP : CBS_CONT;
364}
365
366static int
367load_dash_F_dirs(struct protolib_cache *cache,
368		 const char *key, struct protolib **retp)
369{
370	struct load_config_data data = {cache, key};
371
372	if (VECT_EACH(&opt_F, struct opt_F_t, NULL,
373		      consider_confdir_cb, &data) == NULL)
374		/* Not found.  That's fine.  */
375		return 0;
376
377	if (data.result == NULL)
378		/* There were errors.  */
379		return -1;
380
381	*retp = data.result;
382	return 0;
383}
384
385static int
386load_config(struct protolib_cache *cache,
387	    const char *key, int private, struct protolib **retp)
388{
389	const char **dirs = NULL;
390	if (os_get_config_dirs(private, &dirs) < 0
391	    || dirs == NULL)
392		return -1;
393
394	for (; *dirs != NULL; ++dirs) {
395		struct protolib *plib = consider_config_dir(cache, *dirs, key);
396		if (plib != NULL) {
397			*retp = plib;
398			break;
399		}
400	}
401
402	return 0;
403}
404
405static int
406add_ltrace_conf(struct protolib_cache *cache)
407{
408	/* Look into private config directories for .ltrace.conf and
409	 * into system config directories for ltrace.conf.  If it's
410	 * found, add it to implicit import module.  */
411	struct protolib *plib = NULL;
412	const char *home = NULL;
413	if (os_get_ltrace_conf_filename(&home) < 0
414	    || (home != NULL
415		&& (plib = consider_config_dir(cache, home, ".ltrace")) != NULL
416		&& protolib_add_import(&cache->imports, plib) < 0)
417	    || (plib == NULL && load_config(cache, "ltrace", 0, &plib) < 0)
418	    || (plib != NULL && protolib_add_import(&cache->imports, plib) < 0))
419		/* N.B. If plib is non-NULL, it has been already
420		 * cached.  We don't therefore destroy it on
421		 * failures.  */
422		return -1;
423
424	/* Never mind whether we've found anything.  It's fine if the
425	 * config is absent.  */
426	return 0;
427}
428
429static enum callback_status
430add_imports_cb(struct opt_F_t *entry, void *data)
431{
432	struct protolib_cache *self = data;
433	if (opt_F_get_kind(entry) != OPT_F_FILE)
434		return CBS_CONT;
435
436	struct protolib *new_import
437		= protolib_cache_file(self, entry->pathname, 0);
438
439	if (new_import == NULL
440	    || protolib_add_import(&self->imports, new_import) < 0)
441		/* N.B. If new_import is non-NULL, it has been already
442		 * cached.  We don't therefore destroy it on
443		 * failures.  */
444		return CBS_STOP;
445
446	return CBS_CONT;
447}
448
449int
450protolib_cache_init(struct protolib_cache *cache, struct protolib *import)
451{
452	DICT_INIT(&cache->protolibs, const char *, struct protolib *,
453		  dict_hash_string, dict_eq_string, NULL);
454	protolib_init(&cache->imports);
455
456	/* At this point the cache is consistent.  This is important,
457	 * because next we will use it to cache files that we load
458	 * due to -F.
459	 *
460	 * But we are about to construct the implicit import module,
461	 * which means this module can't be itself imported to the
462	 * files that we load now.  So remember that we are still
463	 * bootstrapping.  */
464	cache->bootstrap = 1;
465
466	if (protolib_add_import(&cache->imports, &legacy_typedefs) < 0
467	    || (import != NULL
468		&& protolib_add_import(&cache->imports, import) < 0)
469	    || add_ltrace_conf(cache) < 0
470	    || VECT_EACH(&opt_F, struct opt_F_t, NULL,
471			 add_imports_cb, cache) != NULL) {
472		protolib_cache_destroy(cache);
473		return -1;
474	}
475
476	cache->bootstrap = 0;
477	return 0;
478}
479
480static enum callback_status
481add_import_cb(struct protolib **importp, void *data)
482{
483	struct protolib *plib = data;
484	if (protolib_add_import(plib, *importp) < 0)
485		return CBS_STOP;
486	else
487		return CBS_CONT;
488}
489
490static struct protolib *
491build_default_config(struct protolib_cache *cache, const char *key)
492{
493	struct protolib *new_plib = malloc(sizeof(*new_plib));
494	if (new_plib == NULL) {
495		fprintf(stderr, "Couldn't create config module %s: %s\n",
496			key, strerror(errno));
497		return NULL;
498	}
499
500	protolib_init(new_plib);
501
502	/* If bootstrapping, copy over imports from implicit import
503	 * module to new_plib.  We can't reference the implicit
504	 * import module itself, because new_plib will become part of
505	 * this same implicit import module itself.  */
506	if ((cache->bootstrap && each_import(&cache->imports, NULL,
507					     add_import_cb, new_plib) != NULL)
508	    || (!cache->bootstrap
509		&& protolib_add_import(new_plib, &cache->imports) < 0)) {
510
511		fprintf(stderr,
512			"Couldn't add imports to config module %s: %s\n",
513			key, strerror(errno));
514		protolib_destroy(new_plib);
515		free(new_plib);
516		return NULL;
517	}
518
519	return new_plib;
520}
521
522static void
523attempt_to_cache(struct protolib_cache *cache,
524		 const char *key, struct protolib *plib)
525{
526	if (protolib_cache_protolib(cache, key, 1, plib) == 0
527	    || plib == NULL)
528		/* Never mind failing to store a NULL.  */
529		return;
530
531	/* Returning a protolib that hasn't been cached would leak
532	 * that protolib, but perhaps it's less bad then giving up
533	 * outright.  At least print an error message.  */
534	fprintf(stderr, "Couldn't cache prototype library for %s\n", key);
535}
536
537struct protolib *
538protolib_cache_search(struct protolib_cache *cache,
539		      const char *key, int own_key, int allow_private)
540{
541	struct protolib *plib = NULL;
542	if (DICT_FIND_VAL(&cache->protolibs, &key, &plib) == 0)
543		return plib;
544
545	if (clone_if_not_own(&key, own_key) < 0) {
546		fprintf(stderr, "Couldn't cache %s: %s\n",
547			key, strerror(errno));
548		return NULL;
549	}
550
551	/* The order is: -F directories, private directories, system
552	 * directories.  If the config file is not found anywhere,
553	 * build a default one.  */
554	if (load_dash_F_dirs(cache, key, &plib) < 0
555	    || (plib == NULL && allow_private
556		&& load_config(cache, key, 1, &plib) < 0)
557	    || (plib == NULL
558		&& load_config(cache, key, 0, &plib) < 0)
559	    || (plib == NULL
560		&& (plib = build_default_config(cache, key)) == NULL))
561		fprintf(stderr,
562			"Error occurred when attempting to load a prototype "
563			"library for %s.\n", key);
564
565	/* Whatever came out of this (even NULL), store it in the
566	 * cache.  */
567	attempt_to_cache(cache, key, plib);
568	return plib;
569}
570
571struct protolib *
572protolib_cache_file(struct protolib_cache *cache,
573		    const char *filename, int own_filename)
574{
575	{
576		struct protolib *plib;
577		if (DICT_FIND_VAL(&cache->protolibs, &filename, &plib) == 0)
578			return plib;
579	}
580
581	FILE *stream = fopen(filename, "r");
582	if (stream == NULL)
583		return NULL;
584
585	if (clone_if_not_own(&filename, own_filename) < 0) {
586		fprintf(stderr, "Couldn't cache %s: %s\n",
587			filename, strerror(errno));
588		fclose(stream);
589		return NULL;
590	}
591
592	struct protolib *new_plib = build_default_config(cache, filename);
593	if (new_plib == NULL
594	    || read_config_file(stream, filename, new_plib) < 0) {
595		fclose(stream);
596		if (own_filename)
597			free((char *)filename);
598		if (new_plib != NULL) {
599			protolib_destroy(new_plib);
600			free(new_plib);
601		}
602		return NULL;
603	}
604
605	attempt_to_cache(cache, filename, new_plib);
606	fclose(stream);
607	return new_plib;
608}
609
610int
611protolib_cache_protolib(struct protolib_cache *cache,
612			const char *filename, int own_filename,
613			struct protolib *plib)
614{
615	if (clone_if_not_own(&filename, own_filename) < 0) {
616		fprintf(stderr, "Couldn't cache %s: %s\n",
617			filename, strerror(errno));
618		return -1;
619	}
620
621	int rc = DICT_INSERT(&cache->protolibs, &filename, &plib);
622	if (rc < 0 && own_filename)
623		free((char *)filename);
624	return rc;
625}
626
627void
628init_global_config(void)
629{
630	protolib_init(&legacy_typedefs);
631	struct arg_type_info *void_info = type_get_simple(ARGTYPE_VOID);
632	static struct arg_type_info ptr_info;
633	type_init_pointer(&ptr_info, void_info, 0);
634
635	static struct named_type voidptr_type;
636	named_type_init(&voidptr_type, &ptr_info, 0);
637
638	if (protolib_add_named_type(&legacy_typedefs, "addr", 0,
639				    &voidptr_type) < 0
640	    || protolib_add_named_type(&legacy_typedefs, "file", 0,
641				       &voidptr_type) < 0) {
642		fprintf(stderr,
643			"Couldn't initialize aliases `addr' and `file'.\n");
644		exit(1);
645	}
646
647	if (protolib_cache_init(&g_protocache, NULL) < 0) {
648		fprintf(stderr, "Couldn't init prototype cache\n");
649		abort();
650	}
651}
652
653void
654destroy_global_config(void)
655{
656	protolib_cache_destroy(&g_protocache);
657	protolib_destroy(&legacy_typedefs);
658}
659