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 <stdlib.h>
22 #include <assert.h>
23 #include <stdio.h>
24 #include <string.h>
25 
26 #include "filter.h"
27 #include "library.h"
28 #include "callback.h"
29 
30 void
filter_init(struct filter * filt)31 filter_init(struct filter *filt)
32 {
33 	filt->rules = NULL;
34 	filt->next = NULL;
35 }
36 
37 void
filter_destroy(struct filter * filt)38 filter_destroy(struct filter *filt)
39 {
40 	struct filter_rule *it;
41 	for (it = filt->rules; it != NULL; ) {
42 		struct filter_rule *next = it->next;
43 		filter_rule_destroy(it);
44 		it = next;
45 	}
46 }
47 
48 void
filter_rule_init(struct filter_rule * rule,enum filter_rule_type type,struct filter_lib_matcher * matcher,regex_t symbol_re)49 filter_rule_init(struct filter_rule *rule, enum filter_rule_type type,
50 		 struct filter_lib_matcher *matcher,
51 		 regex_t symbol_re)
52 {
53 	rule->type = type;
54 	rule->lib_matcher = matcher;
55 	rule->symbol_re = symbol_re;
56 	rule->next = NULL;
57 }
58 
59 void
filter_rule_destroy(struct filter_rule * rule)60 filter_rule_destroy(struct filter_rule *rule)
61 {
62 	filter_lib_matcher_destroy(rule->lib_matcher);
63 	regfree(&rule->symbol_re);
64 }
65 
66 void
filter_add_rule(struct filter * filt,struct filter_rule * rule)67 filter_add_rule(struct filter *filt, struct filter_rule *rule)
68 {
69 	struct filter_rule **rulep;
70 	for (rulep = &filt->rules; *rulep != NULL; rulep = &(*rulep)->next)
71 		;
72 	*rulep = rule;
73 }
74 
75 void
filter_lib_matcher_name_init(struct filter_lib_matcher * matcher,enum filter_lib_matcher_type type,regex_t libname_re)76 filter_lib_matcher_name_init(struct filter_lib_matcher *matcher,
77 			     enum filter_lib_matcher_type type,
78 			     regex_t libname_re)
79 {
80 	switch (type) {
81 	case FLM_MAIN:
82 		assert(type != type);
83 		abort();
84 
85 	case FLM_SONAME:
86 	case FLM_PATHNAME:
87 		matcher->type = type;
88 		matcher->libname_re = libname_re;
89 	}
90 }
91 
92 void
filter_lib_matcher_main_init(struct filter_lib_matcher * matcher)93 filter_lib_matcher_main_init(struct filter_lib_matcher *matcher)
94 {
95 	matcher->type = FLM_MAIN;
96 }
97 
98 void
filter_lib_matcher_destroy(struct filter_lib_matcher * matcher)99 filter_lib_matcher_destroy(struct filter_lib_matcher *matcher)
100 {
101 	switch (matcher->type) {
102 	case FLM_SONAME:
103 	case FLM_PATHNAME:
104 		regfree(&matcher->libname_re);
105 		break;
106 	case FLM_MAIN:
107 		break;
108 	}
109 }
110 
111 static int
re_match_or_error(regex_t * re,const char * name,const char * what)112 re_match_or_error(regex_t *re, const char *name, const char *what)
113 {
114 	int status = regexec(re, name, 0, NULL, 0);
115 	if (status == 0)
116 		return 1;
117 	if (status == REG_NOMATCH)
118 		return 0;
119 
120 	char buf[200];
121 	regerror(status, re, buf, sizeof buf);
122 	fprintf(stderr, "Error when matching %s: %s\n", name, buf);
123 
124 	return 0;
125 }
126 
127 static int
matcher_matches_library(struct filter_lib_matcher * matcher,struct library * lib)128 matcher_matches_library(struct filter_lib_matcher *matcher, struct library *lib)
129 {
130 	switch (matcher->type) {
131 	case FLM_SONAME:
132 		return re_match_or_error(&matcher->libname_re, lib->soname,
133 					 "library soname");
134 	case FLM_PATHNAME:
135 		return re_match_or_error(&matcher->libname_re, lib->pathname,
136 					 "library pathname");
137 	case FLM_MAIN:
138 		return lib->type == LT_LIBTYPE_MAIN;
139 	}
140 	assert(matcher->type != matcher->type);
141 	abort();
142 }
143 
144 int
filter_matches_library(struct filter * filt,struct library * lib)145 filter_matches_library(struct filter *filt, struct library *lib)
146 {
147 	if (filt == NULL)
148 		return 0;
149 
150 	for (; filt != NULL; filt = filt->next) {
151 		struct filter_rule *it;
152 		for (it = filt->rules; it != NULL; it = it->next)
153 			switch (it->type) {
154 			case FR_ADD:
155 				if (matcher_matches_library(it->lib_matcher, lib))
156 					return 1;
157 			case FR_SUBTRACT:
158 				continue;
159 			};
160 	}
161 	return 0;
162 }
163 
164 int
filter_matches_symbol(struct filter * filt,const char * sym_name,struct library * lib)165 filter_matches_symbol(struct filter *filt,
166 		      const char *sym_name, struct library *lib)
167 {
168 	for (; filt != NULL; filt = filt->next) {
169 		int matches = 0;
170 		struct filter_rule *it;
171 		for (it = filt->rules; it != NULL; it = it->next) {
172 			switch (it->type) {
173 			case FR_ADD:
174 				if (matches)
175 					continue;
176 				break;
177 			case FR_SUBTRACT:
178 				if (!matches)
179 					continue;
180 			}
181 
182 			if (matcher_matches_library(it->lib_matcher, lib)
183 			    && re_match_or_error(&it->symbol_re, sym_name,
184 						 "symbol name"))
185 				matches = !matches;
186 		}
187 		if (matches)
188 			return 1;
189 	}
190 	return 0;
191 }
192