1 #include "reqs_errors.h"
2 
3 #include <pthread.h>
4 #include <syslog.h>
5 #include <time.h>
6 
7 #include "data_structure/uthash_nonfatal.h"
8 #include "common.h"
9 #include "config.h"
10 #include "log.h"
11 #include "thread_var.h"
12 
13 /*
14  * Only log messages of repositories whose level is less than or equal to this.
15  *
16  * Level 0 is "top level", mostly used by the root CAs (trust anchors) and RIRs
17  * before RPKI servers delegation.
18  *
19  * Eg. The URIs will have this level (data according to current RPKI state):
20  * rsync://repository.lacnic.net/    [level 0]
21  * rsync://rpki-repo.registro.br/    [level 1]
22  *
23  * rsync://rpki.ripe.net/            [level 0]
24  * rsync://ca.rg.net/                [level 1]
25  * rsync://cc.rg.net/                [level 2]
26  */
27 #define LOG_REPO_LEVEL 0
28 
29 struct error_uri {
30 	/* Key */
31 	char *uri;
32 	/* Date when the first attempt was made */
33 	time_t first_attempt;
34 	/* Related URI (points to a key of another element) */
35 	char *uri_related;
36 	/* Refered by (where this.uri == that.uri_related) */
37 	char *ref_by;
38 	/* Log the summary */
39 	bool log_summary;
40 	UT_hash_handle hh;
41 };
42 
43 static struct error_uri *err_uris_db;
44 
45 /* Prepare for multithreading. */
46 static pthread_rwlock_t db_lock;
47 
48 static int
error_uri_create(char const * uri,struct error_uri ** err_uri)49 error_uri_create(char const *uri, struct error_uri **err_uri)
50 {
51 	struct error_uri *tmp;
52 	int error;
53 
54 	tmp = malloc(sizeof(struct error_uri));
55 	if (tmp == NULL)
56 		return pr_enomem();
57 
58 	/* Needed by uthash */
59 	memset(tmp, 0, sizeof(struct error_uri));
60 
61 	tmp->uri = strdup(uri);
62 	if (tmp->uri == NULL) {
63 		error = pr_enomem();
64 		goto release_tmp;
65 	}
66 
67 	error = get_current_time(&tmp->first_attempt);
68 	if (error)
69 		goto release_uri;
70 
71 	tmp->log_summary = false;
72 	tmp->uri_related = NULL;
73 	tmp->ref_by = NULL;
74 
75 	*err_uri = tmp;
76 	return 0;
77 release_uri:
78 	free(tmp->uri);
79 release_tmp:
80 	free(tmp);
81 	return error;
82 }
83 
84 static void
error_uri_destroy(struct error_uri * err_uri)85 error_uri_destroy(struct error_uri *err_uri)
86 {
87 	free(err_uri->uri);
88 	free(err_uri);
89 }
90 
91 int
reqs_errors_init(void)92 reqs_errors_init(void)
93 {
94 	int error;
95 
96 	error = pthread_rwlock_init(&db_lock, NULL);
97 	if (error)
98 		return pr_op_errno(error, "pthread_rwlock_init() errored");
99 
100 	err_uris_db = NULL;
101 
102 	return 0;
103 }
104 
105 void
reqs_errors_cleanup(void)106 reqs_errors_cleanup(void)
107 {
108 	/* Remove all the uris */
109 	struct error_uri *node, *tmp;
110 
111 	HASH_ITER(hh, err_uris_db, node, tmp) {
112 		HASH_DEL(err_uris_db, node);
113 		error_uri_destroy(node);
114 	}
115 
116 	pthread_rwlock_destroy(&db_lock); /* Nothing to do with error code */
117 }
118 
119 static struct error_uri *
find_error_uri(char const * search,bool lock)120 find_error_uri(char const *search, bool lock)
121 {
122 	struct error_uri *found;
123 
124 	if (lock)
125 		rwlock_read_lock(&db_lock);
126 	HASH_FIND_STR(err_uris_db, search, found);
127 	if (lock)
128 		rwlock_unlock(&db_lock);
129 
130 	return found;
131 }
132 
133 static void
set_working_repo(struct error_uri * err_uri)134 set_working_repo(struct error_uri *err_uri)
135 {
136 	struct error_uri *ref;
137 	char const *work_uri;
138 
139 	work_uri = working_repo_peek();
140 	if (work_uri == NULL)
141 		return;
142 
143 	ref = find_error_uri(work_uri, true);
144 	if (ref == NULL)
145 		return;
146 
147 	err_uri->uri_related = ref->uri;
148 	ref->ref_by = err_uri->uri;
149 }
150 
151 int
reqs_errors_add_uri(char const * uri)152 reqs_errors_add_uri(char const *uri)
153 {
154 	struct error_uri *new_uri, *found_uri;
155 	int error;
156 
157 	/* Don't overwrite if it already exists */
158 	found_uri = find_error_uri(uri, true);
159 	if (found_uri != NULL)
160 		return 0;
161 
162 	new_uri = NULL;
163 	error = error_uri_create(uri, &new_uri);
164 	if (error)
165 		return error;
166 
167 	set_working_repo(new_uri);
168 
169 	/*
170 	 * Check if this will always be logged, in that case the summary will
171 	 * be logged also if the level must be logged.
172 	 */
173 	if (config_get_stale_repository_period() == 0)
174 		new_uri->log_summary =
175 		    (working_repo_peek_level() <= LOG_REPO_LEVEL);
176 
177 	rwlock_write_lock(&db_lock);
178 	HASH_ADD_KEYPTR(hh, err_uris_db, new_uri->uri, strlen(new_uri->uri),
179 	    new_uri);
180 	rwlock_unlock(&db_lock);
181 
182 	return 0;
183 }
184 
185 void
reqs_errors_rem_uri(char const * uri)186 reqs_errors_rem_uri(char const *uri)
187 {
188 	struct error_uri *found_uri;
189 	struct error_uri *tmp;
190 	char *ref_uri;
191 
192 	rwlock_write_lock(&db_lock);
193 	found_uri = find_error_uri(uri, false);
194 	if (found_uri == NULL) {
195 		rwlock_unlock(&db_lock);
196 		return;
197 	}
198 
199 	while (found_uri->uri_related != NULL) {
200 		tmp = find_error_uri(found_uri->uri_related, false);
201 		if (tmp == NULL)
202 			break;
203 		found_uri = tmp;
204 	}
205 
206 	do {
207 		ref_uri = found_uri->ref_by;
208 		HASH_DELETE(hh, err_uris_db, found_uri);
209 		error_uri_destroy(found_uri);
210 		if (ref_uri == NULL)
211 			break;
212 		HASH_FIND_STR(err_uris_db, ref_uri, found_uri);
213 		if (found_uri == NULL)
214 			break;
215 	} while (true);
216 	rwlock_unlock(&db_lock);
217 }
218 
219 int
reqs_errors_foreach(reqs_errors_cb cb,void * arg)220 reqs_errors_foreach(reqs_errors_cb cb, void *arg)
221 {
222 	struct error_uri *node, *tmp;
223 	int error;
224 
225 	rwlock_read_lock(&db_lock);
226 	HASH_ITER(hh, err_uris_db, node, tmp) {
227 		error = cb(node->uri, arg);
228 		if (error) {
229 			rwlock_unlock(&db_lock);
230 			return error;
231 		}
232 	}
233 	rwlock_unlock(&db_lock);
234 
235 	return 0;
236 }
237 
238 bool
reqs_errors_log_uri(char const * uri)239 reqs_errors_log_uri(char const *uri)
240 {
241 	struct error_uri *node;
242 	time_t now;
243 	unsigned int config_period;
244 	int error;
245 
246 	if (log_op_enabled(LOG_DEBUG))
247 		return true;
248 
249 	if (working_repo_peek_level() > LOG_REPO_LEVEL)
250 		return false;
251 
252 	/* Log always? Don't care if the URI exists yet or not */
253 	config_period = config_get_stale_repository_period();
254 	if (config_period == 0)
255 		return true;
256 
257 	node = find_error_uri(uri, true);
258 	if (node == NULL)
259 		return false;
260 
261 	now = 0;
262 	error = get_current_time(&now);
263 	if (error)
264 		return false;
265 
266 	node->log_summary = (difftime(now, node->first_attempt) >=
267 	    (double)config_period);
268 
269 	return node->log_summary;
270 }
271 
272 /*
273  * Log a summary of the error'd servers.
274  */
275 void
reqs_errors_log_summary(void)276 reqs_errors_log_summary(void)
277 {
278 	/* Remove all the uris */
279 	struct error_uri *node, *tmp;
280 	time_t now;
281 	char *str_time;
282 	bool first;
283 	int error;
284 
285 	first = true;
286 	now = 0;
287 	error = get_current_time(&now);
288 	if (error)
289 		return;
290 
291 	rwlock_read_lock(&db_lock);
292 	HASH_ITER(hh, err_uris_db, node, tmp) {
293 		if (!node->log_summary)
294 			continue;
295 		if (first) {
296 			pr_op_warn("The following repositories URIs couldn't be fetched (it can be a local issue or a server issue), please review previous log messages related to such URIs/servers:");
297 			first = false;
298 		}
299 		str_time = asctime(localtime(&node->first_attempt));
300 		if (strrchr(str_time, '\n') != NULL) {
301 			*(str_time + strlen(str_time) - 1) = ' ';
302 		}
303 		pr_op_warn("- '%s': can't be downloaded since %s", node->uri,
304 		    str_time);
305 	}
306 
307 	rwlock_unlock(&db_lock);
308 }
309