prototype.c revision 8ab151c2a1912819e77f83c154c18bc894bc5785
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);
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 enum callback_status
406add_imports_cb(struct opt_F_t *entry, void *data)
407{
408	struct protolib_cache *self = data;
409	if (opt_F_get_kind(entry) != OPT_F_FILE)
410		return CBS_CONT;
411
412	struct protolib *new_import
413		= protolib_cache_file(self, entry->pathname);
414
415	if (new_import == NULL
416	    || protolib_add_import(&self->imports, new_import) < 0)
417		/* N.B. If new_import is non-NULL, it has been already
418		 * cached.  We don't therefore destroy it on
419		 * failures.  */
420		return CBS_STOP;
421
422	return CBS_CONT;
423}
424
425int
426protolib_cache_init(struct protolib_cache *cache, struct protolib *import)
427{
428	DICT_INIT(&cache->protolibs, const char *, struct protolib *,
429		  dict_hash_string, dict_eq_string, NULL);
430	protolib_init(&cache->imports);
431
432	/* At this point the cache is consistent.  This is important,
433	 * because next we will use it to cache files that we load
434	 * due to -F.
435	 *
436	 * But we are about to construct the implicit import module,
437	 * which means this module can't be itself imported to the
438	 * files that we load now.  So remember that we are still
439	 * bootstrapping.  */
440	cache->bootstrap = 1;
441
442	if (protolib_add_import(&cache->imports, &legacy_typedefs) < 0
443	    || (import != NULL
444		&& protolib_add_import(&cache->imports, import) < 0)
445	    || VECT_EACH(&opt_F, struct opt_F_t, NULL,
446			 add_imports_cb, cache) != NULL) {
447		protolib_cache_destroy(cache);
448		return -1;
449	}
450
451	fprintf(stderr, "XXX Not looking for local and system config yet.\n");
452
453	cache->bootstrap = 0;
454	return 0;
455}
456
457static enum callback_status
458add_import_cb(struct protolib **importp, void *data)
459{
460	struct protolib *plib = data;
461	if (protolib_add_import(plib, *importp) < 0)
462		return CBS_STOP;
463	else
464		return CBS_CONT;
465}
466
467static struct protolib *
468build_default_config(struct protolib_cache *cache, const char *key)
469{
470	struct protolib *new_plib = malloc(sizeof(*new_plib));
471	if (new_plib == NULL) {
472		fprintf(stderr, "Couldn't create config module %s: %s\n",
473			key, strerror(errno));
474		return NULL;
475	}
476
477	protolib_init(new_plib);
478
479	/* If bootstrapping, copy over imports from implicit import
480	 * module to new_plib.  We can't reference the implicit
481	 * import module itself, because new_plib will become part of
482	 * this same implicit import module itself.  */
483	if ((cache->bootstrap && each_import(&cache->imports, NULL,
484					     add_import_cb, new_plib) != NULL)
485	    || (!cache->bootstrap
486		&& protolib_add_import(new_plib, &cache->imports) < 0)) {
487
488		fprintf(stderr,
489			"Couldn't add imports to config module %s: %s\n",
490			key, strerror(errno));
491		protolib_destroy(new_plib);
492		free(new_plib);
493		return NULL;
494	}
495
496	return new_plib;
497}
498
499static void
500attempt_to_cache(struct protolib_cache *cache,
501		 const char *key, struct protolib *plib)
502{
503	if (protolib_cache_protolib(cache, key, plib) == 0
504	    || plib == NULL)
505		/* Never mind failing to store a NULL.  */
506		return;
507
508	/* Returning a protolib that hasn't been cached would leak
509	 * that protolib, but perhaps it's less bad then giving up
510	 * outright.  At least print an error message.  */
511	fprintf(stderr, "Couldn't cache prototype library for %s\n", key);
512}
513
514struct protolib *
515protolib_cache_search(struct protolib_cache *cache,
516		      const char *key, int allow_private)
517{
518	struct protolib *plib = NULL;
519	if (DICT_FIND_VAL(&cache->protolibs, &key, &plib) == 0)
520		return plib;
521
522	/* The order is: -F directories, private directories, system
523	 * directories.  If the config file is not found anywhere,
524	 * build a default one.  */
525	if (load_dash_F_dirs(cache, key, &plib) < 0
526	    || (plib == NULL && allow_private
527		&& load_config(cache, key, 1, &plib) < 0)
528	    || (plib == NULL
529		&& load_config(cache, key, 0, &plib) < 0)
530	    || (plib == NULL
531		&& (plib = build_default_config(cache, key)) == NULL))
532		fprintf(stderr,
533			"Error occurred when attempting to load a prototype "
534			"library for %s.\n", key);
535
536	/* Whatever came out of this (even NULL), store it in the
537	 * cache.  */
538	attempt_to_cache(cache, key, plib);
539	return plib;
540}
541
542struct protolib *
543protolib_cache_file(struct protolib_cache *cache, const char *filename)
544{
545	{
546		struct protolib *plib;
547		if (DICT_FIND_VAL(&cache->protolibs, &filename, &plib) == 0)
548			return plib;
549	}
550
551	FILE *stream = fopen(filename, "r");
552	if (stream == NULL)
553		return NULL;
554
555	struct protolib *new_plib = build_default_config(cache, filename);
556	if (new_plib == NULL) {
557		fclose(stream);
558		return NULL;
559	}
560
561	if (read_config_file(stream, filename, new_plib) < 0) {
562		protolib_destroy(new_plib);
563		free(new_plib);
564		fclose(stream);
565		return NULL;
566	}
567
568	attempt_to_cache(cache, filename, new_plib);
569	fclose(stream);
570	return new_plib;
571}
572
573int
574protolib_cache_protolib(struct protolib_cache *cache,
575			const char *filename, struct protolib *plib)
576{
577	return DICT_INSERT(&cache->protolibs, &filename, &plib);
578}
579
580void
581init_global_config(void)
582{
583	protolib_init(&legacy_typedefs);
584	struct arg_type_info *void_info = type_get_simple(ARGTYPE_VOID);
585	static struct arg_type_info ptr_info;
586	type_init_pointer(&ptr_info, void_info, 0);
587
588	static struct named_type voidptr_type;
589	named_type_init(&voidptr_type, &ptr_info, 0);
590
591	if (protolib_add_named_type(&legacy_typedefs, "addr", 0,
592				    &voidptr_type) < 0
593	    || protolib_add_named_type(&legacy_typedefs, "file", 0,
594				       &voidptr_type) < 0) {
595		fprintf(stderr,
596			"Couldn't initialize aliases `addr' and `file'.\n");
597		exit(1);
598	}
599
600	if (protolib_cache_init(&g_protocache, NULL) < 0) {
601		fprintf(stderr, "Couldn't init prototype cache\n");
602		abort();
603	}
604}
605
606void
607destroy_global_config(void)
608{
609	protolib_cache_destroy(&g_protocache);
610	protolib_destroy(&legacy_typedefs);
611}
612