modules/ud/ud_process_stream.c
/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following functions.
- LINE_ATTRIBUTE
- LINE_COMMENT
- LINE_EMPTY
- LINE_EOF
- LINE_ADD
- LINE_UPD
- LINE_DEL
- LINE_OVERRIDE_ADD
- LINE_OVERRIDE_UPD
- LINE_OVERRIDE_DEL
- LINE_ACK
- Line_Type_t
- ud_replace_autonic
- reorder_attr
- ud_reorder_attributes
- ud_split_names
- escape_apostrophes
- line_type
- report_transaction
- process_nrtm
- process_updates
- process_transaction
- UD_process_stream
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,
/* [<][>][^][v][top][bottom][index][help] */
50 LINE_COMMENT,
/* [<][>][^][v][top][bottom][index][help] */
51 LINE_EMPTY,
/* [<][>][^][v][top][bottom][index][help] */
52 LINE_EOF,
/* [<][>][^][v][top][bottom][index][help] */
53 LINE_ADD,
/* [<][>][^][v][top][bottom][index][help] */
54 LINE_UPD,
/* [<][>][^][v][top][bottom][index][help] */
55 LINE_DEL,
/* [<][>][^][v][top][bottom][index][help] */
56 LINE_OVERRIDE_ADD,
/* [<][>][^][v][top][bottom][index][help] */
57 LINE_OVERRIDE_UPD,
/* [<][>][^][v][top][bottom][index][help] */
58 LINE_OVERRIDE_DEL,
/* [<][>][^][v][top][bottom][index][help] */
59 LINE_ACK
/* [<][>][^][v][top][bottom][index][help] */
60 } Line_Type_t;
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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) {
/* [<][>][^][v][top][bottom][index][help] */
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) {
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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,
/* [<][>][^][v][top][bottom][index][help] */
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)
/* [<][>][^][v][top][bottom][index][help] */
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