1 | /***************************************
2 | $Revision: 1.62 $
3 |
4 | Functions to process data stream( file, network socket, etc.)
5 |
6 | Status: NOT REVUED, NOT TESTED
7 |
8 | Author(s): Chris Ottrey, Andrei Robachevsky
9 |
10 | ******************/ /******************
11 | Modification History:
12 | andrei (17/01/2000) Created.
13 | ******************/ /******************
14 | Copyright (c) 2000,2001,2002 RIPE NCC
15 |
16 | All Rights Reserved
17 |
18 | Permission to use, copy, modify, and distribute this software and its
19 | documentation for any purpose and without fee is hereby granted,
20 | provided that the above copyright notice appear in all copies and that
21 | both that copyright notice and this permission notice appear in
22 | supporting documentation, and that the name of the author not be
23 | used in advertising or publicity pertaining to distribution of the
24 | software without specific, written prior permission.
25 |
26 | THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
27 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
28 | AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
29 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
30 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
31 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
32 | ***************************************/
33 |
34 | #include "rip.h"
35 | #include "syntax_api.h"
36 |
37 |
38 | #include <sys/types.h>
39 | #include <sys/socket.h>
40 | #include <netdb.h>
41 | #include <arpa/inet.h>
42 | #include <unistd.h>
43 | #include <sys/stat.h>
44 | #include <fcntl.h>
45 | #include <string.h>
46 |
47 |
48 | typedef enum _Line_Type_t {
49 | LINE_ATTRIBUTE,
50 | LINE_COMMENT,
51 | LINE_EMPTY,
52 | LINE_EOF,
53 | LINE_ADD,
54 | LINE_UPD,
55 | LINE_DEL,
56 | LINE_OVERRIDE_ADD,
57 | LINE_OVERRIDE_UPD,
58 | LINE_OVERRIDE_DEL,
59 | LINE_ACK
60 | } Line_Type_t;
61 |
62 | /* Maximum number of objects(serials) we can consume at a time */
63 | #define SBUNCH 1000
64 |
65 | static int report_transaction(Transaction_t *tr, long transaction_id, Log_t *log, ut_timer_t *psotime, char *reason);
66 | static int process_nrtm(UD_stream_t *ud_stream, Transaction_t *tr, int operation);
67 | static int process_updates(UD_stream_t *ud_stream, Transaction_t *tr, int operation);
68 | static int process_transaction(UD_stream_t *ud_stream, GString *obj_buff, int operation, long transaction_id);
69 |
70 | static GString *escape_apostrophes(GString *text);
71 | static nic_handle_t *ud_replace_autonic(Transaction_t *tr, rpsl_object_t *object);
72 | static void reorder_attr(void *element_data, void *ptr);
73 | static void ud_reorder_attributes(rpsl_object_t *object);
74 |
75 |
76 | /******************************************************************
77 | * ud_replace_autonic() *
78 | * *
79 | * Checks if nic handle is an AUTO one (XX*-SRS) *
80 | * So it tries to assign a nic handle and if successfull *
81 | * substitutes the attribute value *
82 | * *
83 | ******************************************************************/
84 | static nic_handle_t *ud_replace_autonic(Transaction_t *tr, rpsl_object_t *object)
85 | {
86 | nic_handle_t *nh_ptr=NULL;
87 | rpsl_attr_t *nic_hdl, *nic_hdl_old;
88 | GList *nic_hdl_list, *nic_hdl_first;
89 | gint ofs;
90 | char *nic_submitted, *nic_db;
91 |
92 | nic_hdl_list = rpsl_object_get_attr(object, "nic-hdl");
93 | nic_hdl_first =g_list_first(nic_hdl_list);
94 | nic_submitted = rpsl_attr_get_clean_value((rpsl_attr_t *)nic_hdl_first->data);
95 |
96 | /* check if it is an auto NIC handle and replace it with a real one */
97 | if(NH_parse(nic_submitted, &nh_ptr) == 0) {
98 | /* this is an AUTO nic handle */
99 | /* Try to allocate it */
100 | if(NH_check(nh_ptr, tr->sql_connection)>0){
101 | /* Convert nh to the database format */
102 | nic_db = NH_convert(nh_ptr);
103 | /* we need to make a copy as we free the whole list later */
104 | nic_hdl = rpsl_attr_copy((rpsl_attr_t *)nic_hdl_first->data);
105 | rpsl_attr_replace_value(nic_hdl, nic_db);
106 | UT_free(nic_db);
107 | }
108 | /* replace this attribute in the object */
109 | ofs = rpsl_attr_get_ofs(nic_hdl);
110 | /* remove attribute from object and free it */
111 | nic_hdl_old = rpsl_object_remove_attr(object, ofs, NULL);
112 | if(nic_hdl_old) rpsl_attr_delete(nic_hdl_old);
113 | else die;
114 | rpsl_object_add_attr(object, nic_hdl, ofs, NULL);
115 | }/* if this was an AUTO nic handle */
116 |
117 |
118 | UT_free(nic_submitted);
119 |
120 | rpsl_attr_delete_list(nic_hdl_list);
121 | return(nh_ptr);
122 | }
123 |
124 | static void reorder_attr(void *element_data, void *ptr)
125 | {
126 | rpsl_attr_t *attr = (rpsl_attr_t *)element_data;
127 | rpsl_object_t *object = (rpsl_object_t *)ptr;
128 | int ofs;
129 |
130 | /* move the attribute to the beginning
131 | so that all mnt-by are at the beginning of the object */
132 | ofs = rpsl_attr_get_ofs(attr);
133 | attr = rpsl_object_remove_attr(object, ofs, NULL);
134 | if (attr) rpsl_object_add_attr(object, attr, 1, NULL);
135 | else die;
136 | }
137 |
138 | /******************************************************************
139 | * reorder attributes *
140 | * . mnt-by should go before member-of to allow correct *
141 | * membership autorization (still done in RIPupd) *
142 | * . nic-hdl should go before any admin-c, tech-c to prevent *
143 | * errrors in self referencing role objects *
144 | ******************************************************************/
145 | static void ud_reorder_attributes(rpsl_object_t *object)
146 | {
147 | GList *mnt_by_list, *nic_hdl_list;
148 | /* get the list of mnt-by attributes */
149 | mnt_by_list = rpsl_object_get_attr(object, "mnt-by");
150 | /* reorder so that all mnt-by are at the beginning of the object */
151 | if(mnt_by_list != NULL) {
152 | g_list_foreach(mnt_by_list, reorder_attr, object);
153 |
154 | /* free GList only, not members as they are stored in the object */
155 | rpsl_attr_delete_list(mnt_by_list);
156 | }
157 |
158 | /* move nic-hdl to the beginning */
159 | nic_hdl_list = rpsl_object_get_attr(object, "nic-hdl");
160 | if(nic_hdl_list != NULL) {
161 | g_list_foreach(nic_hdl_list, reorder_attr, object);
162 |
163 | /* free GList only, not members as they are stored in the object */
164 | rpsl_attr_delete_list(nic_hdl_list);
165 | }
166 |
167 |
168 | }
169 |
170 | /******************************************************************
171 | * split the role or person attribute value into multiple names *
172 | * *
173 | * destroys the original object and builds a new one (only for *
174 | * C_RO or C_PN *
175 | ******************************************************************/
176 |
177 | static rpsl_object_t *ud_split_names(rpsl_object_t *object)
178 | {
179 | const rpsl_attr_t *attr;
180 | gchar **names;
181 | const GList *old_attrs;
182 | GList *p;
183 | GString *new_obj;
184 | rpsl_object_t *ret_val;
185 | C_Type_t class_type;
186 | int i;
187 |
188 | class_type = rpsl_get_class_id(rpsl_object_get_class(object));
189 | if ((class_type != C_PN) && (class_type != C_RO)) return object;
190 |
191 | /* get the list of person or role attribute */
192 | attr = rpsl_object_get_attr_by_ofs(object, 0);
193 |
194 | /* split the names into words */
195 | names = g_strsplit(rpsl_attr_get_value(attr), " ", 0);
196 | new_obj = g_string_new("");
197 |
198 | /* replace the value of the first attribute */
199 | for (i=0; names[i] != NULL; i++) {
200 | g_string_sprintfa(new_obj, "%s:%s\n", rpsl_attr_get_name(attr), names[i]);
201 | }
202 |
203 | g_strfreev(names);
204 |
205 | /* copy all other attributes */
206 | old_attrs = rpsl_object_get_all_attr(object);
207 | for (p=g_list_next(old_attrs); p != NULL; p = g_list_next(p)) {
208 | g_string_sprintfa(new_obj, "%s:%s\n", rpsl_attr_get_name(p->data), rpsl_attr_get_value(p->data));
209 | }
210 |
211 | ret_val = rpsl_object_init(new_obj->str);
212 | g_string_free(new_obj, TRUE);
213 | rpsl_object_delete(object);
214 | return ret_val;
215 |
216 |
217 | }
218 |
219 |
220 | /******************************************************************
221 | * GString *escape_apostrophes() *
222 | * Escapes apostrophes in the text so they do not confuse printf *
223 | * functions and don't corrupt SQL queries *
224 | * *
225 | * *****************************************************************/
226 | static GString *escape_apostrophes(GString *text) {
227 | int i;
228 | for (i=0; i < text->len; i++) {
229 | if ((text->str[i] == '\'') || (text->str[i] == '\\')) {
230 | text = g_string_insert_c(text, i, '\\');
231 | i++;
232 | }
233 | }
234 | return(text);
235 | } /* escape_apostrophes() */
236 |
237 |
238 | /******************************************************************
239 | * Line_Type_t line_type(e) *
240 | * Determines the line type analysing the first letters *
241 | * *
242 | * ****************************************************************/
243 | static Line_Type_t line_type(const char *line, long *transaction_id) {
244 |
245 | if (strncmp(line, "# EOF", 4) == 0) return(LINE_EOF);
246 | if (strncmp(line, "#", 1) == 0) return(LINE_COMMENT);
247 | if (strcmp(line, "\n") == 0) return(LINE_EMPTY);
248 |
249 | if (strncmp(line, "ACK", 3) == 0) {
250 | *transaction_id = atol(line+3);
251 | return(LINE_ACK);
252 | }
253 | if (strncmp(line, "ADD_OVERRIDE", 12) == 0) {
254 | *transaction_id = atol(line+12);
255 | return(LINE_OVERRIDE_ADD);
256 | }
257 | if (strncmp(line, "UPD_OVERRIDE", 12) == 0) {
258 | *transaction_id = atol(line+12);
259 | return(LINE_OVERRIDE_UPD);
260 | }
261 | if (strncmp(line, "DEL_OVERRIDE", 12) == 0) {
262 | *transaction_id = atol(line+12);
263 | return(LINE_OVERRIDE_DEL);
264 | }
265 |
266 | if (strncmp(line, "ADD", 3) == 0) {
267 | *transaction_id = atol(line+3);
268 | return(LINE_ADD);
269 | }
270 | if (strncmp(line, "UPD", 3) == 0) {
271 | *transaction_id = atol(line+3);
272 | return(LINE_UPD);
273 | }
274 | if (strncmp(line, "DEL", 3) == 0) {
275 | *transaction_id = atol(line+3);
276 | return(LINE_DEL);
277 | }
278 |
279 | /* Otherwise this is an attribute */
280 | return(LINE_ATTRIBUTE);
281 |
282 | } /* line_type() */
283 |
284 |
285 | /******************************************************************
286 | * report_transaction() *
287 | * *
288 | * Prints error report to the log *
289 | * *
290 | * reason - additional message that will be included *
291 | * *
292 | * *****************************************************************/
293 | static int report_transaction(Transaction_t *tr, long transaction_id, Log_t *log, ut_timer_t *psotime, char *reason)
294 | {
295 | int result=0;
296 | ut_timer_t fotime;
297 | float timediff;
298 | const char *class_name = DF_class_type2name(tr->class_type);
299 | char *primary_key = tr->K->str;
300 |
301 |
302 | /* calculate statistics */
303 | UT_timeget(&fotime);
304 | timediff = UT_timediff(psotime, &fotime);
305 |
306 | if(tr->succeeded==0) {
307 | result=tr->error;
308 | log->num_failed++;
309 | ER_inf_va(FAC_UD, ASP_UD_UPDLOG, "[%ld] %.2fs FAILED [%s:%s][%s]", transaction_id, timediff, class_name, primary_key, reason);
310 | if(result & ERROR_U_OBJ) ER_dbg_va(FAC_UD, ASP_UD_OBJ,"[%ld] object: referential integrity error", transaction_id);
311 | if(result & ERROR_U_AUT) ER_dbg_va(FAC_UD, ASP_UD_OBJ,"[%ld] object: authentication error", transaction_id);
312 | if(result & ERROR_U_BADOP) ER_dbg_va(FAC_UD, ASP_UD_OBJ,"[%ld] object: unsupported operation", transaction_id);
313 | if(result & ERROR_U_COP) ER_dbg_va(FAC_UD, ASP_UD_OBJ,"[%ld] object: conflicting operation", transaction_id);
314 | if(result & ERROR_U_NSUP) ER_dbg_va(FAC_UD, ASP_UD_OBJ,"[%ld] object: this type is not supported", transaction_id);
315 | ER_dbg_va(FAC_UD, ASP_UD_OBJ, "[%ld] object: details [%s]", transaction_id, (tr->error_script)->str);
316 | result=1; /* # of failures */
317 | }
318 | else {
319 | result=0;
320 | log->num_ok++;
321 | ER_inf_va(FAC_UD, ASP_UD_OBJ, "[%ld] %.2fs OK [%s:%s][%s]", transaction_id, timediff, class_name, primary_key, reason);
322 | ER_dbg_va(FAC_UD, ASP_UD_OBJ, "[%ld] object: details [%s]", transaction_id, (tr->error_script)->str);
323 | }
324 |
325 | return(result);
326 | }/* report_transaction() */
327 |
328 |
329 |
330 | /************************************************************
331 | * process_nrtm() *
332 | * *
333 | * Process object in NRTM client mode *
334 | * *
335 | * nrtm - pointer to _nrtm structure *
336 | * log - pointer to Log_t structure *
337 | * object_name - name of the object *
338 | * operation - operation code (OP_ADD/OP_DEL) *
339 | * *
340 | * Returns: *
341 | * 1 - okay *
342 | * <0 - error *
343 | * *
344 | ************************************************************/
345 |
346 | static int process_nrtm(UD_stream_t *ud_stream, Transaction_t *tr, int operation)
347 | {
348 | int result=0;
349 | int dummy=0;
350 | struct _nrtm *nrtm = ud_stream->nrtm;
351 | long serial_id;
352 | Log_t *log_ptr= &(ud_stream->log);
353 | ut_timer_t sotime;
354 | int ta_upd_nhr;
355 |
356 | /* Start timer for statistics */
357 | UT_timeget(&sotime);
358 |
359 | /* We allow NRTM updates for some inconsistent objects */
360 | /* One of the examples is reference by name which looks like nic-handle */
361 | /* For this purpose we allow dummy creation when updating an object */
362 | /* We also check for dummy allowance when deleting an object */
363 | /* this is done to allow deletion of person objects referenced by name */
364 |
365 | tr->mode|=B_DUMMY;
366 | if(IS_NO_NHR(tr->mode))ta_upd_nhr=0; else ta_upd_nhr = TA_UPD_NHR;
367 |
368 | switch (operation) {
369 |
370 | case OP_ADD:
371 | if(nrtm->tr){ /* DEL ADD => saved*/
372 | if(tr->object_id==0) {
373 | /* object does not exist in the DB */
374 | /* delete the previous(saved) object*/
375 | object_process(nrtm->tr);
376 | /* create DEL serial */
377 | UD_lock_serial(nrtm->tr);
378 | serial_id = UD_create_serial(nrtm->tr);
379 | CP_CREATE_S_PASSED(nrtm->tr->action); TR_update_status(nrtm->tr);
380 | UD_commit_serial(nrtm->tr);
381 | UD_unlock_serial(nrtm->tr);
382 | /* Mark TR as clean */
383 | TR_mark_clean(nrtm->tr);
384 | /* log the transaction */
385 | result=report_transaction(nrtm->tr, serial_id, log_ptr, &sotime, "M:DEL");
386 |
387 | transaction_free(nrtm->tr); nrtm->tr=NULL;
388 |
389 | /* Create an object and update NHR */
390 | tr->action=(TA_CREATE | ta_upd_nhr);
391 | /* restart the timer for statistics */
392 | UT_timeget(&sotime);
393 | object_process(tr); /* create a new one*/
394 | /* create ADD serial */
395 | UD_lock_serial(tr);
396 | serial_id = UD_create_serial(tr);
397 | CP_CREATE_S_PASSED(tr->action); TR_update_status(tr);
398 | UD_commit_serial(tr);
399 | UD_unlock_serial(tr);
400 | /* Mark TR as clean */
401 | TR_mark_clean(tr);
402 | /* log the transaction */
403 | result=report_transaction(tr, serial_id, log_ptr, &sotime, "M:ADD");
404 | }
405 | else {
406 | /* object already exists in the DB - update or dummy replacement*/
407 | /*compare the two, may be we may collapse operations*/
408 | if(tr->object_id==nrtm->tr->object_id) {
409 | /* DEL-ADD ->> UPDATE */
410 | transaction_free(nrtm->tr); nrtm->tr=NULL;
411 | tr->action=TA_UPD_CLLPS;
412 | object_process(tr);
413 | /* create DEL+ADD serial records */
414 | UD_lock_serial(tr);
415 | tr->action=TA_DELETE; serial_id = UD_create_serial(tr);
416 | result=report_transaction(tr, serial_id, log_ptr, &sotime, "M:DEL(UPD)");
417 |
418 | /* restart the timer for statistics */
419 | UT_timeget(&sotime);
420 | tr->sequence_id++;
421 | tr->action=TA_CREATE; serial_id = UD_create_serial(tr);
422 | CP_CREATE_S_PASSED(tr->action); TR_update_status(tr);
423 | UD_commit_serial(tr);
424 | UD_unlock_serial(tr);
425 | /* Mark TR as clean */
426 | TR_mark_clean(tr);
427 | result=report_transaction(tr, serial_id, log_ptr, &sotime, "M:ADD(UPD)");
428 | }
429 | else { /* this should be a dummy object in the database(that we are going to replace with the real one */
430 | /* or an interleaved operation*/
431 | object_process(nrtm->tr); /* delete the previous(saved) object*/
432 | /* create a DEL serial record */
433 | UD_lock_serial(nrtm->tr);
434 | serial_id = UD_create_serial(nrtm->tr);
435 | CP_CREATE_S_PASSED(nrtm->tr->action); TR_update_status(nrtm->tr);
436 | UD_commit_serial(nrtm->tr);
437 | UD_unlock_serial(nrtm->tr);
438 | /* Mark TR as clean */
439 | TR_mark_clean(nrtm->tr);
440 | /* log the transaction */
441 | result=report_transaction(nrtm->tr, serial_id, log_ptr, &sotime, "M:DEL");
442 |
443 |
444 | transaction_free(nrtm->tr); nrtm->tr=NULL;
445 |
446 | /* restart the timer for statistics */
447 | UT_timeget(&sotime);
448 |
449 | tr->action=TA_UPDATE;
450 | /* check if we are replacing a dummy object */
451 | dummy=isdummy(tr);
452 | if(dummy==1) tr->action = TA_UPD_DUMMY;
453 | object_process(tr); /* create a new one*/
454 | /* For serials this is CREATE operation. Increase sequence to have it correct in serals */
455 | if(dummy==1) { tr->action=TA_CREATE; tr->sequence_id++; }
456 | /* create ADD serial record */
457 | UD_lock_serial(tr);
458 | serial_id = UD_create_serial(tr);
459 | CP_CREATE_S_PASSED(tr->action); TR_update_status(tr);
460 | UD_commit_serial(tr);
461 | UD_unlock_serial(tr);
462 | /* Mark TR as clean */
463 | TR_mark_clean(tr);
464 | /* log the transaction */
465 | result=report_transaction(tr, serial_id, log_ptr, &sotime, "M:ADD");
466 |
467 | }
468 | }
469 | }
470 | else { /* ADD ADD =>brand new object*/
471 | if(tr->object_id==0) {
472 | /* fprintf(stderr,"CREATE new\n");*/
473 | /* Create an object and update NHR */
474 | tr->action=(TA_CREATE | ta_upd_nhr);
475 | object_process(tr);
476 | /* create ADD serial */
477 | UD_lock_serial(tr);
478 | serial_id = UD_create_serial(tr);
479 | CP_CREATE_S_PASSED(tr->action); TR_update_status(tr);
480 | UD_commit_serial(tr);
481 | UD_unlock_serial(tr);
482 |
483 | /* Mark TR as clean */
484 | TR_mark_clean(tr);
485 | /* log the transaction */
486 | result=report_transaction(tr, serial_id, log_ptr, &sotime, "M:ADD");
487 | }
488 | else { /* object already exists in the database */
489 | /* this may happen because of dummies*/
490 | /* or with some implementations of mirroring protocol that have atomic update */
491 | /* instead of add + del */
492 | tr->action=TA_UPDATE;
493 | dummy=isdummy(tr);
494 | if(dummy==1) tr->action = TA_UPD_DUMMY;
495 | object_process(tr);
496 | /* For serials this is CREATE operation. Increase sequence to have it correct in serals */
497 | if(dummy==1) { tr->action=TA_CREATE; tr->sequence_id++; }
498 | /* create ADD serial record */
499 | UD_lock_serial(tr);
500 | serial_id = UD_create_serial(tr);
501 | CP_CREATE_S_PASSED(tr->action); TR_update_status(tr);
502 | UD_commit_serial(tr);
503 | UD_unlock_serial(tr);
504 | /* Mark TR as clean */
505 | TR_mark_clean(tr);
506 | /* log the transaction */
507 | result=report_transaction(tr, serial_id, log_ptr, &sotime, "M:ADD");
508 |
509 | }
510 | }
511 | break;
512 |
513 | case OP_DEL:
514 | if(nrtm->tr){ /*DEL DEL =>saved */
515 | /* check this is not a deletion of the same object twice */
516 | /* this should not happen but we cannot trust foreign sources */
517 | /* in such case process saved transaction but fail the current one */
518 | if(nrtm->tr->object_id == tr->object_id) tr->object_id=0;
519 |
520 | object_process(nrtm->tr); /* delete the previous(saved) object*/
521 | /* create DEL serial record */
522 | UD_lock_serial(nrtm->tr);
523 | serial_id = UD_create_serial(nrtm->tr);
524 | CP_CREATE_S_PASSED(nrtm->tr->action); TR_update_status(nrtm->tr);
525 | UD_commit_serial(nrtm->tr);
526 | UD_unlock_serial(nrtm->tr);
527 | /* Mark TR as clean */
528 | TR_mark_clean(nrtm->tr);
529 | /* log the transaction */
530 | result=report_transaction(nrtm->tr, serial_id, log_ptr, &sotime, "M:DEL");
531 |
532 | transaction_free(nrtm->tr); nrtm->tr=NULL;
533 | }
534 | /* save the real object (not a dummy one ) */
535 | if(tr->object_id>0 && !isdummy(tr)){
536 | /* save the object*/
537 | tr->action=(TA_DELETE | ta_upd_nhr);
538 | nrtm->tr=tr;
539 | return(0);
540 | }
541 | else { /* this is an error - Trying to DEL non-existing object*/
542 | tr->succeeded=0; tr->error|=ERROR_U_COP;
543 | tr->action=(TA_DELETE | ta_upd_nhr);
544 | /* create and initialize TR record for crash recovery */
545 | TR_create_record(tr);
546 | /* create DEL serial record anyway */
547 | UD_lock_serial(tr);
548 | serial_id = UD_create_serial(tr);
549 | CP_CREATE_S_PASSED(tr->action); TR_update_status(tr);
550 | UD_commit_serial(tr);
551 | UD_unlock_serial(tr);
552 | /* Mark TR as clean */
553 | TR_mark_clean(tr);
554 | /* log the transaction */
555 | result=report_transaction(tr, serial_id, log_ptr, &sotime, "M:DEL: non-existing object");
556 |
557 | }
558 | break;
559 |
560 | default:
561 | tr->succeeded=0; tr->error |=ERROR_U_BADOP;
562 | break;
563 | }
564 |
565 | /* Free resources */
566 | transaction_free(tr);
567 |
568 | return(result);
569 | } /* process_nrtm() */
570 |
571 |
572 |
573 | /************************************************************
574 | * process_updates() *
575 | * *
576 | * Process object in update mode *
577 | * *
578 | * ud_stream - pointer to UD_stream structure *
579 | * object_name - name of the object *
580 | * operation - operation code (OP_ADD/OP_DEL) *
581 | * *
582 | * Note: *
583 | * Frees tr and tr->obj on exit *
584 | * *
585 | * Returns: *
586 | * 0 - okay *
587 | * <0- number of failed objects *
588 | * *
589 | ************************************************************/
590 |
591 | static int process_updates(UD_stream_t *ud_stream, Transaction_t *tr, int operation)
592 | {
593 | int result=0;
594 | Log_t *log_ptr= &(ud_stream->log);
595 | int dummy=0;
596 | ut_timer_t sotime;
597 | int ta_upd_nhr;
598 | char *reason;
599 |
600 | /* Start timer for statistics */
601 | UT_timeget(&sotime);
602 | if(IS_NO_NHR(tr->mode))ta_upd_nhr=0; else ta_upd_nhr = TA_UPD_NHR;
603 |
604 | switch(operation) {
605 | /* Compare operations and report an error if they do not match */
606 | case OP_ADD:
607 | if(tr->object_id!=0) { /* trying to create, but object exists */
608 | tr->succeeded=0; tr->error|=ERROR_U_COP;
609 | reason="U:ADD:object already exists";
610 | g_string_sprintfa(tr->error_script,"E[%d]:NEW requested but object already exists\n" ,ERROR_U_COP);
611 | UD_ack(tr); /* Send a NACK */
612 | } else {
613 | /* Action: create the object and update NHR */
614 | tr->action=(TA_CREATE | ta_upd_nhr);
615 | reason="U:ADD";
616 | object_process(tr);
617 | }
618 | break;
619 | case OP_UPD:
620 | if(tr->object_id==0) { /* trying to update non-existing object*/
621 | tr->succeeded=0; tr->error|=ERROR_U_COP;
622 | reason="U:UPD:non-existing object";
623 | g_string_sprintfa(tr->error_script,"E[%d]:UPD requested but no existing object found\n" ,ERROR_U_COP);
624 | UD_ack(tr); /* Send a NACK */
625 | } else {
626 | tr->action=TA_UPDATE;
627 | reason="U:UPD";
628 | dummy=isdummy(tr);
629 | if(dummy==1) tr->action = TA_UPD_DUMMY;
630 | object_process(tr);
631 | }
632 | break;
633 |
634 | case OP_DEL:
635 | if(tr->object_id==0) { /* trying t delete non-existing object*/
636 | tr->succeeded=0; tr->error|=ERROR_U_COP;
637 | reason="U:DEL:non-existing object";
638 | g_string_sprintfa(tr->error_script,"E[%d]:DEL requested but no existing object found\n" ,ERROR_U_COP);
639 | UD_ack(tr);
640 | } else {
641 | tr->action=(TA_DELETE | ta_upd_nhr);
642 | reason="U:DEL";
643 | object_process(tr);
644 | }
645 | break;
646 |
647 | default:
648 | /* bad operation for this mode if not standalone */
649 | if(IS_STANDALONE(tr->mode)) {
650 | if(tr->object_id==0){
651 | tr->action=(TA_CREATE | ta_upd_nhr);
652 | reason="U:ADD";
653 | }
654 | else {
655 | tr->action=TA_UPDATE;
656 | reason="U:UPD";
657 | }
658 | object_process(tr);
659 | }
660 | else {
661 | tr->succeeded=0;
662 | tr->error|=ERROR_U_BADOP;
663 | g_string_sprintfa(tr->error_script,"E[%d]:Unknown operation requested\n" ,ERROR_U_BADOP);
664 | reason="U:bad operation";
665 | UD_ack(tr); /* Send a NACK */
666 | }
667 | break;
668 | }
669 | /* If not in standalone mode create serial and copy error transcript */
670 | if(!IS_STANDALONE(tr->mode)) {
671 | if(tr->succeeded){
672 | /* we don't want to generate DEL serial for dummy replacement*/
673 | if(dummy==1) { tr->action=TA_CREATE; tr->sequence_id++; }
674 | UD_lock_serial(tr);
675 | UD_create_serial(tr);
676 | CP_CREATE_S_PASSED(tr->action); TR_update_status(tr);
677 | UD_commit_serial(tr);
678 | UD_unlock_serial(tr);
679 | /* Mark the TR as clean */
680 | TR_mark_clean(tr);
681 | }
682 | }
683 |
684 | /* Make a report. U stands for update stream. No reason */
685 | result=report_transaction(tr, tr->transaction_id, log_ptr, &sotime, reason);
686 |
687 | /* Free resources */
688 | /* rpsl_object_delete(tr->object); */
689 | transaction_free(tr);
690 |
691 | return(result);
692 |
693 | } /* process_updates() */
694 |
695 |
696 | /************************************************************
697 | * *
698 | * int process_transaction() *
699 | * *
700 | * Processes the transaction *
701 | * *
702 | * ud_stream - pointer to UD_stream_t structure *
703 | * *
704 | * Returns: *
705 | * 0 - no error *
706 | * <0- number of failed objects *
707 | * *
708 | ************************************************************/
709 |
710 | /* It frees the obj */
711 |
712 | static int process_transaction(UD_stream_t *ud_stream,
713 | GString *g_obj_buff,
714 | int operation,
715 | long transaction_id)
716 | {
717 | Transaction_t *tr = NULL;
718 | int result;
719 | nic_handle_t *nh_ptr=NULL;
720 | rpsl_object_t *submitted_object=NULL, *sql_object=NULL;
721 | const GList *rpsl_err_list;
722 | gchar *object_txt, *flat_object_txt;
723 | ut_timer_t sotime;
724 | long serial_id;
725 |
726 |
727 | /* check if the requested transaction has already been processed */
728 | /* this may happen in case of crash. If so, just send an ack and return */
729 | if(TR_check(ud_stream->db_connection, transaction_id, (ud_stream->condat).sock))return(1);
730 |
731 | /* escape apostrophes, otherwise sql will be confused */
732 | g_obj_buff=escape_apostrophes(g_obj_buff);
733 |
734 | /* check if it is an object and init it */
735 | if ((submitted_object=rpsl_object_init(g_obj_buff->str))==NULL) return(-1);
736 |
737 | /* start new transaction now */
738 | tr = transaction_new(ud_stream->db_connection, rpsl_get_class_id(rpsl_object_get_class(submitted_object)));
739 | if (tr == NULL) die;
740 |
741 | tr->mode = ud_stream->ud_mode;
742 | tr->load_pass = ud_stream->load_pass;
743 | tr->source_hdl = ud_stream->source_hdl;
744 | tr->socket = (ud_stream->condat).sock;
745 | tr->transaction_id = transaction_id;
746 | tr->object = submitted_object;
747 |
748 | UT_timeget(&sotime);
749 |
750 |
751 | /* check for syntax errors (with whois_rip syntax set) */
752 | if((rpsl_err_list = rpsl_object_errors(submitted_object)) != NULL) {
753 | int num_err = 0;
754 | gboolean is_garbage = FALSE;
755 | gboolean is_only_comments = FALSE;
756 |
757 |
758 | /* check the severity of the errors */
759 | /* we need to catch: */
760 | /* template errors - missing primary keys _only_ */
761 | /* attribute syntax errors - just to abort without processing */
762 | do {
763 | const rpsl_attr_t *attr;
764 | const rpsl_error_t *err = rpsl_err_list->data;
765 | gboolean is_important_error = FALSE;
766 |
767 | switch (err->code) {
768 | /* totally bogus object, or missing primary keys */
769 | case RPSL_ERR_ONLYCOMMENTS:
770 | is_only_comments = TRUE;
771 | case RPSL_ERR_MISSINGKEY:
772 | case RPSL_ERR_BADCLASS:
773 | case RPSL_ERR_UNKNOWNCLASS:
774 | is_garbage = TRUE;
775 | is_important_error = TRUE;
776 | break;
777 | /* inappropriate, duplicate attributes */
778 | case RPSL_ERR_ATTRNOTALLOWED:
779 | attr = rpsl_object_get_attr_by_ofs(submitted_object, err->attr_num);
780 | if (rpsl_attr_is_key(submitted_object, rpsl_attr_get_name(attr))) {
781 | is_important_error = TRUE;
782 | }
783 | break;
784 | /* bad and single attribuets that have multiple appearance in the object are dangerous */
785 | case RPSL_ERR_BADATTR:
786 | case RPSL_ERR_ATTRSINGLE:
787 | is_important_error = TRUE;
788 | break;
789 |
790 | }
791 | /* report error */
792 | /* no need to have an extended report */
793 | /* basically for debuggung purposes */
794 | /* it will never be passed to the user */
795 | if (is_important_error) {
796 | g_string_sprintfa(tr->error_script,"E[%d]:%s\n", ERROR_U_OBJ, err->descr);
797 | num_err++;
798 | }
799 | rpsl_err_list = g_list_next(rpsl_err_list);
800 | } while (rpsl_err_list != NULL);
801 |
802 | if (num_err > 0) {
803 | tr->succeeded = 0;
804 | tr->error |= ERROR_U_OBJ;
805 | /* garbage just ignore */
806 | if (is_garbage){
807 | if(!is_only_comments)
808 | ER_dbg_va(FAC_UD, ASP_UD_UPDLOG, "garbage in NRTM stream [%s]", g_obj_buff->str);
809 | } else {
810 | ER_dbg_va(FAC_UD, ASP_UD_UPDLOG,"Malformed object:[%s]\n%s\n",tr->error_script->str, g_obj_buff->str);
811 | /* we cannot process this object and store it in the database */
812 | /* but in case of NRTM this indicates a malformed stream */
813 | /* we try our best to be in sync with the master, so we need to store */
814 | /* this block (maybe object) in failed_transaction */
815 | if(IS_NRTM_CLNT(ud_stream->ud_mode)){
816 | object_txt = rpsl_object_get_text(submitted_object, RPSL_STD_COLUMN);
817 | tr->object_txt = object_txt;
818 | if(operation==OP_DEL) tr->action=TA_DELETE; else tr->action=TA_CREATE;
819 | UD_lock_serial(tr);
820 | serial_id = UD_create_serial(tr);
821 | CP_CREATE_S_PASSED(tr->action); TR_update_status(tr);
822 | UD_commit_serial(tr);
823 | UD_unlock_serial(tr);
824 | /* Mark TR as clean */
825 | TR_mark_clean(tr);
826 | /* log the transaction */
827 | result=report_transaction(tr, serial_id, &ud_stream->log, &sotime, "M:---");
828 | }
829 | }
830 | UD_ack(tr);
831 | transaction_free(tr);
832 | return(-1);
833 | }
834 | }/* checked for syntax errors */
835 |
836 |
837 | /* If we maintain NHR, for person and role objects */
838 | /* check and replace the AUTO nic handle */
839 | if(!IS_NO_NHR(ud_stream->ud_mode)){
840 | if((tr->class_type == C_PN) || (tr->class_type == C_RO)) {
841 | nh_ptr = ud_replace_autonic(tr, submitted_object);
842 | /* NULL nh_ptr indicates a problem with a NIC handle */
843 | if(nh_ptr == NULL) {
844 | tr->succeeded = 0;
845 | tr->error |= ERROR_U_OBJ;
846 | g_string_sprintfa(tr->error_script,"E[%d]:Wrong NIC handle format\n", ERROR_U_OBJ);
847 | ER_inf_va(FAC_UD, ASP_UD_UPDLOG,"Malformed object:[Wrong NIC handle]\n%s\n", g_obj_buff->str);
848 | /* we cannot process this object and store it in the database */
849 | /* but in case of NRTM this indicates a malformed stream */
850 | /* we try our best to be in sync with the master, so we need to store */
851 | /* this block (maybe object) in failed_transaction */
852 | if(IS_NRTM_CLNT(ud_stream->ud_mode)){
853 | object_txt = rpsl_object_get_text(submitted_object, RPSL_STD_COLUMN);
854 | tr->object_txt = object_txt;
855 | if(operation==OP_DEL) tr->action=TA_DELETE; else tr->action=TA_CREATE;
856 | UD_lock_serial(tr);
857 | serial_id = UD_create_serial(tr);
858 | CP_CREATE_S_PASSED(tr->action); TR_update_status(tr);
859 | UD_commit_serial(tr);
860 | UD_unlock_serial(tr);
861 | /* Mark TR as clean */
862 | TR_mark_clean(tr);
863 | /* log the transaction */
864 | result=report_transaction(tr, serial_id, &ud_stream->log, &sotime, "M:---");
865 | }
866 | UD_ack(tr);
867 | transaction_free(tr);
868 | return(-1);
869 | }
870 | }
871 | }
872 |
873 | /* normalize the object (reorder and split attributes */
874 | sql_object = rpsl_object_copy_flattened(submitted_object);
875 |
876 | /* put nic-hdl and mnt-by at the top of the list */
877 | ud_reorder_attributes(sql_object);
878 |
879 | /* split the names */
880 | sql_object = ud_split_names(sql_object);
881 |
882 | /* save the original text of submitted object */
883 | object_txt = rpsl_object_get_text(submitted_object, RPSL_STD_COLUMN);
884 |
885 | tr->object = sql_object;
886 | tr->object_txt = object_txt; /* needs to be freed */
887 | tr->nh = nh_ptr;
888 |
889 | /* We perform no commit/rollback in the loader mode, so thread_id should be set to 0 */
890 | if(IS_STANDALONE(tr->mode)){ tr->thread_ins=0; tr->thread_upd=0; }
891 |
892 | /* For the first load pass we only create objects */
893 |
894 | if(tr->load_pass==1) {
895 | /* still we need to fill tr->K (last.pkey) field */
896 | /* tr->load_pass ==1 will trigger this behaviour in ud_each_primary_key_select */
897 | g_list_foreach((GList *)rpsl_object_get_all_attr(tr->object), ud_each_primary_key_select, tr);
898 | tr->object_id=0;
899 | }
900 | else tr->object_id=get_object_id(tr);
901 |
902 | /* check if error has been detected */
903 | /*XXX replace error codes with symbolic consts */
904 | if(tr->object_id==-2) { /* Object erorr - wrong syntax in one of the PK */
905 | ER_inf_va(FAC_UD, ASP_UD_UPDLOG,"Malformed object:[%s]\n%s\n",tr->error_script->str, g_obj_buff->str);
906 | UD_ack(tr);
907 | /* this object is not deleted by transaction_delete() */
908 | rpsl_object_delete(submitted_object);
909 | transaction_free(tr);
910 | return(-1);
911 | }
912 |
913 | /* Object cannot be retrieved */
914 | if(tr->object_id==-1) { /* DB error*/
915 | tr->succeeded=0;
916 | tr->error |= ERROR_U_DBS;
917 | ER_perror(FAC_UD, UD_SQL, "Object cannot be retrieved:[%s]", tr->object_txt);
918 | die;
919 | }
920 |
921 | /* Process transaction. tr and obj are freed inside the process_* functions */
922 | /* tr is freed there because in NRTM we need to save a transaction for later processing */
923 | if(IS_UPDATE(ud_stream->ud_mode))
924 | /* We are in update mode */
925 | result=process_updates(ud_stream, tr, operation);
926 | else
927 | /* We are in NRTM mode */
928 | result=process_nrtm(ud_stream, tr, operation);
929 |
930 |
931 | /* free the original submitted object */
932 | rpsl_object_delete(submitted_object);
933 | return(result);
934 |
935 | }
936 |
937 |
938 | /************************************************************
939 | * *
940 | * int UD_process_stream(UD_stream_t *ud_stream) *
941 | * *
942 | * Processes the stream *
943 | * *
944 | * ud_stream - pointer to UD_stream_t structure *
945 | * *
946 | * Returns: *
947 | * in update mode (!standalone)(1 object processed): *
948 | * 1 - no error *
949 | * <0- errors *
950 | * *
951 | * in NRTM & standalone modes *
952 | * total number of object processed *
953 | * *
954 | ************************************************************/
955 |
956 | int UD_process_stream(UD_stream_t *ud_stream)
957 | {
958 | char line_buff[STR_XXL];
959 | SQ_connection_t *sql_connection;
960 | struct _nrtm *nrtm;
961 | Log_t *log_ptr= &(ud_stream->log);
962 | ut_timer_t stime, ftime, sotime;
963 | float obj_second1, obj_second10, timediff;
964 | int result=0;
965 | int operation=0;
966 | int interrupt=0;
967 | int do_update;
968 | int default_ud_mode = ud_stream->ud_mode;
969 | Line_Type_t linetype;
970 | Transaction_t *tr;
971 | long transaction_id=0; /* transaction_id (to be supplied by DBupdate and stored in Database) */
972 | long serial_id;
973 | GString *g_obj_buff;
974 |
975 | nrtm=ud_stream->nrtm;
976 |
977 | if ((g_obj_buff = g_string_sized_new(STR_XXL)) == NULL){
978 | ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring");
979 | die;
980 | }
981 |
982 | /* Check connection to the database */
983 | if(SQ_ping(ud_stream->db_connection)) {
984 | ER_perror(FAC_UD, UD_SQL, "%s", SQ_error(ud_stream->db_connection));
985 | die;
986 | }
987 | sql_connection=ud_stream->db_connection;
988 |
989 | UT_timeget(&stime);
990 |
991 | /* Main loop. Reading input stream line by line */
992 | /* Empty line signals to start processing an object, if we have it */
993 | while (SK_cd_gets(&ud_stream->condat, line_buff, sizeof(line_buff))>0) {
994 |
995 |
996 | switch (linetype=line_type(line_buff, &transaction_id)) {
997 |
998 | case LINE_COMMENT:
999 | case LINE_EOF:
1000 | break;
1001 |
1002 | case LINE_ACK:
1003 | tr = transaction_new(ud_stream->db_connection, 0);
1004 | tr->transaction_id=transaction_id;
1005 | TR_delete_record(tr);
1006 | transaction_free(tr);
1007 | break;
1008 |
1009 |
1010 | case LINE_ADD:
1011 | /* restore the default operation mode */
1012 | operation=OP_ADD;
1013 | ud_stream->ud_mode=default_ud_mode;
1014 | break;
1015 |
1016 | case LINE_OVERRIDE_ADD:
1017 | /* for override - switch the dummy bit on */
1018 | operation=OP_ADD;
1019 | ud_stream->ud_mode=default_ud_mode|B_DUMMY;
1020 | break;
1021 |
1022 | case LINE_UPD:
1023 | /* restore the default operation mode */
1024 | operation=OP_UPD;
1025 | ud_stream->ud_mode=default_ud_mode;
1026 | break;
1027 |
1028 | case LINE_OVERRIDE_UPD:
1029 | /* for override - switch the dummy bit on */
1030 | operation=OP_UPD;
1031 | ud_stream->ud_mode=default_ud_mode|B_DUMMY;
1032 | break;
1033 |
1034 | case LINE_DEL:
1035 | /* restore the default operation mode */
1036 | operation=OP_DEL;
1037 | ud_stream->ud_mode=default_ud_mode;
1038 | break;
1039 |
1040 | case LINE_OVERRIDE_DEL:
1041 | /* for override - switch the dummy bit on */
1042 | operation=OP_DEL;
1043 | ud_stream->ud_mode=default_ud_mode|B_DUMMY;
1044 | break;
1045 |
1046 | case LINE_EMPTY:
1047 | /* start processing the object */
1048 | /* escape apostrophes, otherwise sql will be confused */
1049 | /* XXX */
1050 | /* print_object(obj); */
1051 | /* check if we have collected something */
1052 | if(g_obj_buff->len >0){
1053 | #if 0
1054 | /* no operation suggest a garbage in the stream - just ignore it */
1055 | if(IS_NRTM_CLNT(ud_stream->ud_mode) && (operation==OP_NOOP)) {
1056 | ER_inf_va(FAC_UD, ASP_UD_UPDLOG, "garbage in NRTM stream");
1057 | } else
1058 | #endif
1059 | {
1060 | /* start new transaction now */
1061 | result=process_transaction(ud_stream, g_obj_buff, operation, transaction_id);
1062 | /* process_transaction() frees tr and obj structures, */
1063 | /* so make sure we'll not reference these objects in the future */
1064 | operation=OP_NOOP;
1065 | transaction_id=0;
1066 | }
1067 | ud_stream->ud_mode=default_ud_mode;
1068 | if ((g_obj_buff = g_string_truncate(g_obj_buff,0)) == NULL){
1069 | ER_perror(FAC_UD, UD_MEM, "cannot allocate gstring");
1070 | die;
1071 | }
1072 | }
1073 | /* this is a good place for quick interrupt */
1074 | do_update=CO_get_do_update();
1075 | if (do_update) interrupt=0; else interrupt=1;
1076 |
1077 | break;
1078 |
1079 | default:
1080 | /* this may be an object - fill the buffer */
1081 | g_string_sprintfa(g_obj_buff, "%s", line_buff);
1082 | break;
1083 | } /* switch */
1084 |
1085 | /* Finish processing if interrupt has been set */
1086 | if (interrupt) break;
1087 | } /* Main loop of data stream processing : while */
1088 |
1089 |
1090 | /* Some postprocessing */
1091 | if(IS_NRTM_CLNT(ud_stream->ud_mode)){
1092 | /* We are in NRTM mode */
1093 | /* Clean up */
1094 | /* In NRTM mode there may be a saved object that is unprocessed */
1095 | if(nrtm->tr){ /*saved backlog?*/
1096 | /*XXX Do nothing with this backlog; next time we connect we will process it */
1097 | #if 0
1098 | /* restart the timer for statistics */
1099 | UT_timeget(&sotime);
1100 | object_process(nrtm->tr); /* delete the previous(saved) object*/
1101 | /* result=report_transaction(nrtm->tr, &(ud_stream->log), nrtm->object_name,
1102 | "NRTM:DEL:While deleting previous(saved) object"); */
1103 | /* create DEL serial record no matter what the result is */
1104 | UD_lock_serial(nrtm->tr);
1105 | serial_id = UD_create_serial(nrtm->tr);
1106 | CP_CREATE_S_PASSED(nrtm->tr->action); TR_update_status(nrtm->tr);
1107 | UD_commit_serial(nrtm->tr);
1108 | UD_unlock_serial(nrtm->tr);
1109 | /* Mark TR as clean */
1110 | TR_mark_clean(nrtm->tr);
1111 | /* log the transaction */
1112 | result=report_transaction(nrtm->tr, serial_id, log_ptr, &sotime, "M:DEL");
1113 | #endif
1114 | transaction_free(nrtm->tr); nrtm->tr=NULL;
1115 | }
1116 | }
1117 |
1118 | /* That's all. Free GString */
1119 | g_string_free(g_obj_buff, TRUE);
1120 |
1121 |
1122 | /* Calculate some statistics */
1123 | UT_timeget(&ftime);
1124 | timediff = UT_timediff(&stime, &ftime);
1125 | obj_second1 = (float)(log_ptr->num_ok)/timediff;
1126 | obj_second10 = (float)(log_ptr->num_ok+log_ptr->num_failed)/timediff;
1127 |
1128 | /* Print the report */
1129 | if(IS_STANDALONE(ud_stream->ud_mode) || (!IS_UPDATE(ud_stream->ud_mode))) {
1130 |
1131 | ER_inf_va(FAC_UD, ASP_UD_UPDLOG,"%s ******** report **********", UD_TAG);
1132 | ER_inf_va(FAC_UD, ASP_UD_UPDLOG,"%s %d objects OK (%7.4f obj/s)", UD_TAG, log_ptr->num_ok, obj_second1);
1133 | ER_inf_va(FAC_UD, ASP_UD_UPDLOG,"%s %d objects failed", UD_TAG, log_ptr->num_failed);
1134 | ER_inf_va(FAC_UD, ASP_UD_UPDLOG,"%s average processing time %7.4f obj/s (%6.2f obj/min)", UD_TAG,
1135 | obj_second10, obj_second10*60);
1136 | result=log_ptr->num_ok+log_ptr->num_failed;
1137 | }
1138 | return(result);
1139 |
1140 | } /* UD_process_stream */
1141 |