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 |