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