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