1 | /*************************************** 2 | $Revision: 1.36 $ 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 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 | #include <sys/types.h> 34 | #include <sys/socket.h> 35 | #include <netdb.h> 36 | #include <arpa/inet.h> 37 | #include <unistd.h> 38 | #include <sys/stat.h> 39 | #include <fcntl.h> 40 | #include <string.h> 41 | #include "constants.h" 42 | #include "query_command.h" 43 | #include "ud.h" 44 | #include "ud_int.h" 45 | #include "timediff.h" 46 | 47 | typedef enum _Line_Type_t { 48 | LINE_ATTRIBUTE, 49 | LINE_COMMENT, 50 | LINE_EMPTY, 51 | LINE_EOF, 52 | LINE_ADD, 53 | LINE_UPD, 54 | LINE_DEL, 55 | LINE_OVERRIDE_ADD, 56 | LINE_OVERRIDE_UPD, 57 | LINE_OVERRIDE_DEL, 58 | LINE_PLUS 59 | } Line_Type_t; 60 | 61 | /* Maximum number of objects(serials) we can consume at a time */ 62 | #define SBUNCH 1000 63 | 64 | static int report_transaction(Transaction_t *tr, Log_t *log, char *obj_name, char *reason); 65 | static int process_nrtm(UD_stream_t *ud_stream, Transaction_t *tr, char *object_name, int operation); 66 | static int process_updates(UD_stream_t *ud_stream, Transaction_t *tr, char *object_name, int operation); 67 | static int process_transaction(UD_stream_t *ud_stream,Object_t *obj,char *object_name,nic_handle_t *nh, int operation); 68 | 69 | /* Delimiters that separate list members, both RPS(,) and legacy( ) */ 70 | #define ATTR_DELIMITERS " ," 71 | 72 | 73 | void ud_parse_init(Obj_parse_t *parse){ 74 | bzero(parse, sizeof(Obj_parse_t)); 75 | parse->start_object=1; 76 | } 77 | 78 | void ud_parse_free(Obj_parse_t *parse){ 79 | free(parse->object_name); 80 | } 81 | 82 | 83 | 84 | static GSList *split_attribute(GSList *attr_list, A_Type_t attr_type, char *attr_value){ 85 | char *token; 86 | char *split; 87 | char *value, *n; 88 | Attribute_t *attr_split; 89 | GSList *the_list = attr_list; 90 | 91 | /* check for line continuation (+) */ 92 | if (strncmp(attr_value, "+", 1) == 0) attr_value++; 93 | /* check for end-of-line comments */ 94 | n = index(attr_value, '#'); 95 | /* if there is no comment check for trailing \n */ 96 | if(n == NULL) n = index(attr_value, '\n'); 97 | /* now copy the clean value into the attribute */ 98 | if(n == NULL) value = g_strdup(attr_value); 99 | else value = g_strndup(attr_value, (n - attr_value)); 100 | 101 | token=value; 102 | while((split=strsep(&token, ATTR_DELIMITERS))){ 103 | attr_split = attribute_new1(attr_type, split); 104 | if (attr_split) the_list = g_slist_append(the_list, attr_split); 105 | } 106 | free(value); 107 | return(the_list); 108 | } 109 | 110 | /************************************************************ 111 | * * 112 | * The function to reorder attributes in the List * 113 | * nic-hdl and mnt-by should come first * 114 | * * 115 | * should return 0 if they are equal, a negative value if * 116 | * the first element comes before the second, or a positive * 117 | * value if the first element comes after the second * 118 | * * 119 | ************************************************************/ 120 | static gint reorder_attributes(const void *element1, const void *element2) 121 | { 122 | Attribute_t *attr1 = (Attribute_t *)element1; 123 | Attribute_t *attr2 = (Attribute_t *)element2; 124 | gint order = -1; 125 | 126 | if(attr2->type == A_MB) order= 1; 127 | if(attr1->type == A_MB) order= -1; 128 | if(attr2->type == A_NH) order= 1; 129 | if(attr1->type == A_NH) order= -1; 130 | 131 | return(order); 132 | 133 | } 134 | 135 | /* XXX */ 136 | static void each_attribute_print(void *element_data, void *tr_ptr) 137 | { 138 | 139 | Attribute_t *attr = (Attribute_t *)element_data; 140 | 141 | fprintf(stderr, "[%d|%s]\n", attr->type, attr->value); 142 | 143 | } 144 | 145 | /* XXX */ 146 | static void print_object(Object_t *obj) 147 | { 148 | g_slist_foreach(obj->attributes, each_attribute_print, NULL); 149 | fprintf(stderr, ">>>>>\n%s\n", obj->object->str); 150 | } 151 | 152 | 153 | /****************************************************************** 154 | * GString *escape_apostrophes() * 155 | * Escapes apostrophes in the text so they do not confuse printf * 156 | * functions and don't corrupt SQL queries * 157 | * * 158 | * *****************************************************************/ 159 | GString *escape_apostrophes(GString *text) { 160 | int i; 161 | for (i=0; i < text->len; i++) { 162 | if ((text->str[i] == '\'') || (text->str[i] == '\\')) { 163 | text = g_string_insert_c(text, i, '\\'); 164 | i++; 165 | } 166 | } 167 | return(text); 168 | } /* escape_apostrophes() */ 169 | 170 | 171 | /****************************************************************** 172 | * Line_Type_t line_type(e) * 173 | * Determines the line type analysing the first letters * 174 | * * 175 | * ****************************************************************/ 176 | static Line_Type_t line_type(const char *line) { 177 | Line_Type_t result = -1; 178 | 179 | if (strncmp(line, "# EOF", 4) == 0) { 180 | result = LINE_EOF; 181 | } 182 | else if (strncmp(line, "#", 1) == 0) { 183 | result = LINE_COMMENT; 184 | } 185 | else if (strcmp(line, "\n") == 0) { 186 | result = LINE_EMPTY; 187 | } 188 | else if (strcmp(line, "ADD\n") == 0) { 189 | result = LINE_ADD; 190 | } 191 | else if (strcmp(line, "UPD\n") == 0) { 192 | result = LINE_UPD; 193 | } 194 | else if (strcmp(line, "DEL\n") == 0) { 195 | result = LINE_DEL; 196 | } 197 | else if (strcmp(line, "ADD_OVERRIDE\n") == 0) { 198 | result = LINE_OVERRIDE_ADD; 199 | } 200 | else if (strcmp(line, "UPD_OVERRIDE\n") == 0) { 201 | result = LINE_OVERRIDE_UPD; 202 | } 203 | else if (strcmp(line, "DEL_OVERRIDE\n") == 0) { 204 | result = LINE_OVERRIDE_DEL; 205 | } 206 | else if (strncmp(line, "+", 1) == 0) { 207 | result = LINE_PLUS; 208 | } 209 | else { 210 | result = LINE_ATTRIBUTE; 211 | } 212 | 213 | return result; 214 | } /* line_type() */ 215 | 216 | /****************************************************************** 217 | * Object_t *UD_parse_object() * 218 | * * 219 | * Parses the object accepting line by line * 220 | * * 221 | * ****************************************************************/ 222 | Object_t *UD_parse_object(SQ_connection_t *sql_connection, Obj_parse_t *parse, char *line_buff) 223 | { 224 | GString *g_line_buff; 225 | Attribute_t *class_attr, *attr; 226 | char *a_value, *ptr; 227 | char nic[MAX_NH_LENGTH]; 228 | 229 | 230 | if (parse->start_object == 1) { 231 | parse->obj = object_new(line_buff); 232 | } 233 | if (parse->obj) { 234 | 235 | if ((g_line_buff = g_string_sized_new(STR_XXL)) == NULL){ 236 | fprintf(stderr, "E: cannot allocate gstring\n"); 237 | die; 238 | } 239 | 240 | g_string_sprintf(g_line_buff, "%s", line_buff); 241 | /* escape apostrophes in the input line */ 242 | g_line_buff=escape_apostrophes(g_line_buff); 243 | 244 | if(parse->start_object == 1){ 245 | /* If this is the first attribute(==object name/type) */ 246 | parse->start_object=0; 247 | parse->object_name = g_strndup(g_line_buff->str, g_line_buff->len); 248 | *(parse->object_name+g_line_buff->len-1)='\0'; 249 | fprintf(stderr, "D: object: [%s] ", parse->object_name); 250 | /* Create an attribute - the first one determines a class */ 251 | parse->class_attr_list=NULL; /* Initialize the list that will hold splitted class attribute */ 252 | class_attr = attribute_new(g_line_buff->str); 253 | if (class_attr == NULL) die; /* Should not happen */ 254 | if((class_attr->type==A_PN)||(class_attr->type==A_RO)){ 255 | /* split names */ 256 | parse->class_attr_list = split_attribute(parse->class_attr_list, class_attr->type, class_attr->value); 257 | attribute_free(class_attr, NULL); 258 | } else { 259 | parse->class_attr_list = g_slist_append(parse->class_attr_list, class_attr); 260 | } 261 | /* do nothing more with this attribute - we will prepend it at the end */ 262 | } 263 | else { 264 | attr = attribute_new(g_line_buff->str); 265 | 266 | if (attr) { 267 | parse->a_type=attr->type; 268 | a_value=attr->value; 269 | if(parse->a_type==A_NH) { 270 | /* Parse the string into nh structure */ 271 | /* In case of an AUTO NIC handle check the ID in the database */ 272 | /* Possible errors leave to core processing */ 273 | if(NH_parse(attr->value, &parse->nh_ptr) == 0) { 274 | /* fprintf(stderr, "D:parsing NIC: [%s]\n", attr->value); */ 275 | /* Check if we can allocate it */ 276 | if(NH_check(parse->nh_ptr, sql_connection)>0){ 277 | /* Convert nh to the database format */ 278 | NH_convert(nic, parse->nh_ptr); 279 | /* fprintf(stderr, "D:NIC:[%s]\n", nic); */ 280 | /* Replace NIC handle in the string which is copied to the text object */ 281 | sprintf(line_buff, g_line_buff->str); 282 | ptr = strstr(line_buff, attr->value); 283 | /* parse new attribute string */ 284 | strcpy(ptr, nic); 285 | g_string_sprintf(g_line_buff, line_buff); 286 | g_string_sprintfa(g_line_buff, "\n"); 287 | /* Update the attribute */ 288 | attribute_upd(attr, attr->type, nic); 289 | /* fprintf(stderr, "D:attribute updated\n"); */ 290 | } 291 | } 292 | } /* NHR stuff */ 293 | } 294 | else 295 | a_value=g_line_buff->str; 296 | 297 | if (parse->a_type>=0) { /* This indicates that the input line contains the value of the attribute */ 298 | switch (parse->a_type) { 299 | /*these attributes may appear several on the line - split them*/ 300 | case A_PN: /* person */ 301 | case A_RO: /* role */ 302 | case A_MR: /* mbrs-by-ref */ 303 | case A_MB: /* mnt-by */ 304 | case A_MO: /* member-of */ 305 | case A_SD: /* sub-dom */ 306 | case A_RZ: /* rev-srv */ 307 | case A_NS: /* nserver */ 308 | parse->obj->attributes = split_attribute(parse->obj->attributes, parse->a_type, a_value); 309 | if (attr) attribute_free(attr, NULL); 310 | attr=NULL; 311 | break; 312 | default: break; 313 | } 314 | /* g_string_sprintfa(obj->object, "%s", g_line_buff->str); */ 315 | if(attr)parse->obj->attributes = g_slist_append(parse->obj->attributes, attr); 316 | } 317 | } /* if not start_object (not the first/class attribute) */ 318 | /* copy the line into object no matter whether it is an attribute or not (continualtion, etc.) */ 319 | g_string_sprintfa(parse->obj->object, "%s", g_line_buff->str); 320 | g_string_free(g_line_buff, TRUE); 321 | }/* if (obj) */ 322 | return(parse->obj); 323 | } 324 | 325 | /****************************************************************** 326 | * report_transaction() * 327 | * * 328 | * Prints error report to the log * 329 | * * 330 | * reason - additional message that will be included * 331 | * * 332 | * *****************************************************************/ 333 | static int report_transaction(Transaction_t *tr, Log_t *log, char *obj_name, char *reason) 334 | { 335 | int result=0; 336 | 337 | if(tr->succeeded==0) { 338 | result=tr->error; 339 | log->num_failed++; 340 | fprintf(stderr, "FAILED[%s][%s(%d)](%d/%d)\n ", obj_name, reason, result, log->num_failed, (log->num_failed)+(log->num_ok)); 341 | fprintf(log->logfile, "*FAILED[%s][%s](%d/%d)\n ", obj_name, reason, log->num_failed, (log->num_failed)+(log->num_ok)); 342 | if(result & ERROR_U_MEM) fprintf(log->logfile, "\t*Memory allocation error\n"); 343 | if(result & ERROR_U_DBS) fprintf(log->logfile, "\t*Database (SQL) error\n"); 344 | if(result & ERROR_U_OBJ) fprintf(log->logfile, "\t*Object (RF) error\n"); 345 | if(result & ERROR_U_AUT) fprintf(log->logfile, "\t*Object authentication error\n"); 346 | if(result & ERROR_U_BADOP) fprintf(log->logfile, "\t*Bad operation\n"); 347 | if(result & ERROR_U_COP) fprintf(log->logfile, "\t*Conflicting operation\n"); 348 | if(result & ERROR_U_NSUP) fprintf(log->logfile, "\t*Object of this type is not supported\n"); 349 | if(result & ERROR_U_BUG) fprintf(log->logfile, "\t*Software bug - report to <ripe-dbm@ripe.net>\n"); 350 | fprintf(log->logfile, "%s", (tr->error_script)->str); 351 | result=(-1)*result; 352 | fflush(log->logfile); 353 | } 354 | else { 355 | result=1; 356 | log->num_ok++; 357 | fprintf(stderr, "OK(%d/%d)\n", log->num_ok, (log->num_failed)+(log->num_ok)); 358 | } 359 | 360 | return(result); 361 | }/* report_transaction() */ 362 | 363 | 364 | 365 | /************************************************************ 366 | * process_nrtm() * 367 | * * 368 | * Process object in NRTM client mode * 369 | * * 370 | * nrtm - pointer to _nrtm structure * 371 | * log - pointer to Log_t structure * 372 | * object_name - name of the object * 373 | * operation - operation code (OP_ADD/OP_DEL) * 374 | * * 375 | * Returns: * 376 | * 1 - okay * 377 | * <0 - error * 378 | * * 379 | ************************************************************/ 380 | 381 | static int process_nrtm(UD_stream_t *ud_stream, Transaction_t *tr, char *object_name, int operation) 382 | { 383 | int result=0; 384 | int dummy=0; 385 | struct _nrtm *nrtm = ud_stream->nrtm; 386 | Log_t *log_ptr= &(ud_stream->log); 387 | 388 | /* We allow NRTM updates for some inconsistent objects */ 389 | /* One of the examples is reference by name which looks like nic-handle */ 390 | /* For this purpose we allow dummy creation when updating an object */ 391 | /* We also check for dummy allowance when deleting an object */ 392 | /* this is done to allow deletion of person objects referenced by name */ 393 | 394 | tr->dummy=1; 395 | 396 | switch (operation) { 397 | 398 | case OP_ADD: 399 | if(nrtm->tr){ /* DEL ADD => saved*/ 400 | if(tr->object_id==0) { 401 | /* object does not exist in the DB */ 402 | object_process(nrtm->tr); /* delete the previous(saved) object*/ 403 | result=report_transaction(nrtm->tr, log_ptr, nrtm->object_name, "NRTM:DEL:While deleting previous(saved)"); 404 | /* create DEL serial */ 405 | create_serial(nrtm->tr); 406 | object_free(nrtm->tr->object); 407 | transaction_free(nrtm->tr); nrtm->tr=NULL; 408 | /* Create an object and update NHR */ 409 | tr->action=(TA_CREATE | TA_UPD_NHR); 410 | /* fprintf(stderr,"CREATE next\n"); */ 411 | object_process(tr); /* create a new one*/ 412 | result=report_transaction(tr, log_ptr, object_name, "NRTM:ADD:While creating new"); 413 | /* create ADD serial */ 414 | create_serial(tr); 415 | } 416 | else { 417 | /* object already exists in the DB - update or dummy replacement*/ 418 | if(tr->object_id==nrtm->tr->object_id) {/*compare the two, may be we may collapse operations*/ 419 | object_free(nrtm->tr->object); 420 | transaction_free(nrtm->tr); nrtm->tr=NULL; 421 | /* fprintf(stderr,"DEL-ADD ->> UPDATE\n");*/ 422 | tr->action=TA_UPDATE; 423 | object_process(tr); 424 | report_transaction(tr, log_ptr, object_name,"NRTM:upd"); 425 | result=report_transaction(tr, log_ptr, object_name,"NRTM:upd"); 426 | /* create DEL+ADD serial records */ 427 | tr->action=TA_DELETE; create_serial(tr); 428 | tr->action=TA_CREATE; create_serial(tr); 429 | } 430 | else { /* this should be a dummy object in the database(that we are going to replace with the real one */ 431 | /* or an interleaved operation*/ 432 | /* fprintf(stderr,"DEL previous\n");*/ 433 | object_process(nrtm->tr); /* delete the previous(saved) object*/ 434 | result=report_transaction(nrtm->tr, log_ptr, nrtm->object_name, "NRTM:DEL:While deleting previous(saved)"); 435 | /* create a DEL serial record */ 436 | create_serial(nrtm->tr); 437 | object_free(nrtm->tr->object); 438 | transaction_free(nrtm->tr); nrtm->tr=NULL; 439 | tr->action=TA_UPDATE; 440 | dummy=isdummy(tr); 441 | /* If we are replacing dummy with a real object update NHR */ 442 | if(dummy==1) tr->action |= TA_UPD_NHR; 443 | /* fprintf(stderr,"UPDATE next(dummy)\n"); */ 444 | object_process(tr); /* create a new one*/ 445 | result=report_transaction(tr, log_ptr, object_name, "NRTM:ADD:While creating new"); 446 | /* For serials this is CREATE operation */ 447 | if(dummy==1) tr->action=TA_CREATE; 448 | /* create ADD serial record */ 449 | create_serial(tr); 450 | } 451 | } 452 | } 453 | else { /* ADD ADD =>brand new object*/ 454 | if(tr->object_id==0) { 455 | /* fprintf(stderr,"CREATE new\n");*/ 456 | /* Create an object and update NHR */ 457 | tr->action=(TA_CREATE | TA_UPD_NHR); 458 | object_process(tr); 459 | result=report_transaction(tr, log_ptr, object_name,"NRTM:ADD:While creating new"); 460 | /* create ADD serial */ 461 | create_serial(tr); 462 | } 463 | else { /* object already exists in the database */ 464 | /* this may happen because of dummies*/ 465 | /* or with some implementations of mirroring protocol that have atomic update */ 466 | /* instead of add + del */ 467 | /* fprintf(stderr,"CREATE new\n");*/ 468 | tr->action=TA_UPDATE; 469 | dummy=isdummy(tr); 470 | /* If we are replacing dummy with a real object update NHR */ 471 | if(dummy==1) tr->action |= TA_UPD_NHR; 472 | object_process(tr); 473 | result=report_transaction(tr, log_ptr, object_name,"NRTM:ADD:While creating new"); 474 | if(dummy==1) tr->action=TA_CREATE; /* we don't want to generate DEL serial for dummy replacement*/ 475 | /* create ADD serial record */ 476 | create_serial(tr); 477 | } 478 | } 479 | break; 480 | 481 | case OP_DEL: 482 | if(nrtm->tr){ /*DEL DEL =>saved */ 483 | /* fprintf(stderr,"DEL previous\n");*/ 484 | object_process(nrtm->tr); /* delete the previous(saved) object*/ 485 | result=report_transaction(nrtm->tr, log_ptr, nrtm->object_name, "NRTM:DEL:While deleting previous(saved) object"); 486 | /* create DEL serial record */ 487 | create_serial(nrtm->tr); 488 | object_free(nrtm->tr->object); 489 | transaction_free(nrtm->tr); nrtm->tr=NULL; 490 | } 491 | if(tr->object_id>0){ /* save the object*/ 492 | fprintf(stderr,"SAVED\n"); 493 | tr->action=TA_DELETE; 494 | nrtm->tr=tr; 495 | strcpy(nrtm->object_name, object_name); 496 | return(1); 497 | } 498 | else { /* this is an error - Trying to DEL non-existing object*/ 499 | tr->succeeded=0; tr->error|=ERROR_U_COP; 500 | result=report_transaction(tr, log_ptr, object_name, "NRTM:OOS:Trying to DEL non-existing object"); 501 | /* create DEL serial record anyway */ 502 | tr->action=TA_DELETE; 503 | create_serial(tr); 504 | } 505 | break; 506 | 507 | default: 508 | tr->succeeded=0; tr->error |=ERROR_U_BADOP; 509 | break; 510 | } 511 | 512 | /* Free resources */ 513 | object_free(tr->object); 514 | transaction_free(tr); 515 | 516 | return(result); 517 | } /* process_nrtm() */ 518 | 519 | 520 | 521 | /************************************************************ 522 | * process_updates() * 523 | * * 524 | * Process object in update mode * 525 | * * 526 | * ud_stream - pointer to UD_stream structure * 527 | * object_name - name of the object * 528 | * operation - operation code (OP_ADD/OP_DEL) * 529 | * * 530 | * Note: * 531 | * Frees tr and tr->obj on exit * 532 | * * 533 | * Returns: * 534 | * 1 - okay * 535 | * <0 - error * 536 | * * 537 | ************************************************************/ 538 | 539 | static int process_updates(UD_stream_t *ud_stream, Transaction_t *tr, char *object_name, int operation) 540 | { 541 | int result=0; 542 | Log_t *log_ptr= &(ud_stream->log); 543 | int dummy=0; 544 | 545 | switch(operation) { 546 | /* Compare operations and report an error if they do not match */ 547 | case OP_ADD: 548 | if(tr->object_id!=0) { /* trying to create, but object exists */ 549 | tr->succeeded=0; tr->error|=ERROR_U_COP; 550 | } else { 551 | /* Action: create the object and update NHR */ 552 | tr->action=(TA_CREATE | TA_UPD_NHR); 553 | object_process(tr); 554 | } 555 | break; 556 | case OP_UPD: 557 | if(tr->object_id==0) { /* trying to update non-existing object*/ 558 | tr->succeeded=0; tr->error|=ERROR_U_COP; 559 | } else { 560 | tr->action=TA_UPDATE; 561 | dummy=isdummy(tr); 562 | /* If we are replacing dummy with a real object update NHR */ 563 | if(dummy==1) tr->action |= TA_UPD_NHR; 564 | object_process(tr); 565 | } 566 | break; 567 | 568 | case OP_DEL: 569 | if(tr->object_id==0) { /* trying t delete non-existing object*/ 570 | tr->succeeded=0; tr->error|=ERROR_U_COP; 571 | } else { 572 | tr->action=TA_DELETE; 573 | object_process(tr); 574 | } 575 | break; 576 | 577 | default: 578 | /* bad operation for this mode if not standalone */ 579 | if(tr->standalone) { 580 | if(tr->object_id==0)tr->action=TA_CREATE; else tr->action=TA_UPDATE; 581 | object_process(tr); 582 | } 583 | else { 584 | tr->succeeded=0; 585 | tr->error|=ERROR_U_BADOP; 586 | } 587 | break; 588 | } 589 | /* Make a report */ 590 | result=report_transaction(tr, log_ptr, object_name, "RIPupd:"); 591 | 592 | /* If not in standalone mode create serial and copy error transcript */ 593 | if(!tr->standalone) { 594 | if(result==1)create_serial(tr); 595 | ud_stream->error_script=g_strdup((tr->error_script)->str); 596 | } 597 | 598 | /* Free resources */ 599 | object_free(tr->object); 600 | transaction_free(tr); 601 | 602 | return(result); 603 | 604 | } /* process_updates() */ 605 | 606 | 607 | /************************************************************ 608 | * * 609 | * int process_transaction() * 610 | * * 611 | * Processes the transaction * 612 | * * 613 | * ud_stream - pointer to UD_stream_t structure * 614 | * * 615 | * Returns: * 616 | * 1 - no error * 617 | * <0- errors * 618 | * * 619 | ************************************************************/ 620 | 621 | /* It frees the obj */ 622 | 623 | static int process_transaction(UD_stream_t *ud_stream, 624 | Object_t *obj, 625 | char *object_name, 626 | nic_handle_t *nh, 627 | int operation) 628 | { 629 | Transaction_t *tr = NULL; 630 | Log_t *log_ptr = &(ud_stream->log); 631 | Attribute_t *attr=NULL; 632 | int result; 633 | 634 | /* start new transaction now */ 635 | tr = transaction_new(ud_stream->db_connection, obj->type); 636 | 637 | /* Return with error if transaction cannot be created */ 638 | if (tr == NULL) die; 639 | 640 | tr->standalone=IS_STANDALONE(ud_stream->ud_mode); 641 | tr->dummy=IS_DUMMY_ALLOWED(ud_stream->ud_mode); 642 | tr->load_pass=ud_stream->load_pass; 643 | tr->object=obj; 644 | tr->nh=nh; 645 | tr->source_hdl=ud_stream->source_hdl; 646 | 647 | /* We perform no commit/rollback in the loader mode, so thread_id should be set to 0 */ 648 | if(ud_stream->load_pass!=0) { tr->thread_ins=0; tr->thread_upd=0; } 649 | 650 | /* For the first load pass we only create objects */ 651 | if(ud_stream->load_pass==1) tr->object_id=0; 652 | else tr->object_id=get_object_id(tr); 653 | 654 | /* Object cannot be retrieved */ 655 | if(tr->object_id==-1) { /* DB error*/ 656 | tr->succeeded=0; 657 | tr->error |= ERROR_U_DBS; 658 | report_transaction(tr, log_ptr, object_name, "Object cannot be retrieved"); 659 | transaction_free(tr); 660 | object_free(obj); 661 | die; 662 | } 663 | /* save the name of person/role as we need it for referential */ 664 | /* integrity check when deleting the object against names. */ 665 | /* This is needed to support legacy references by name rather */ 666 | /* then by nic_hdl */ 667 | if((tr->class_type==C_PN) || (tr->class_type==C_RO)){ 668 | attr = attribute_new(object_name); 669 | 670 | if (attr==NULL) { 671 | tr->succeeded=0; 672 | tr->error |= ERROR_U_MEM; 673 | report_transaction(tr, log_ptr, object_name, "Cannot allocate memory"); 674 | transaction_free(tr); 675 | object_free(obj); 676 | die; 677 | } 678 | 679 | /* Save the value */ 680 | tr->save=g_strdup(attr->value); 681 | /* fprintf(stderr, "Allocated [%s]\n", tr->save); */ 682 | attribute_free(attr, NULL); 683 | } 684 | 685 | /* Process transaction. tr and obj are freed inside the process_* functions */ 686 | 687 | if(IS_UPDATE(ud_stream->ud_mode)) 688 | /* We are in update mode */ 689 | result=process_updates(ud_stream, tr, object_name, operation); 690 | else 691 | /* We are in NRTM mode */ 692 | result=process_nrtm(ud_stream, tr, object_name, operation); 693 | 694 | return(result); 695 | 696 | } 697 | 698 | 699 | /************************************************************ 700 | * * 701 | * int UD_process_stream(UD_stream_t *ud_stream) * 702 | * * 703 | * Processes the stream * 704 | * * 705 | * ud_stream - pointer to UD_stream_t structure * 706 | * * 707 | * Returns: * 708 | * in update mode (!standalone)(1 object processed): * 709 | * 1 - no error * 710 | * <0- errors * 711 | * * 712 | * in NRTM & standalone modes * 713 | * total number of object processed * 714 | * * 715 | ************************************************************/ 716 | 717 | int UD_process_stream(UD_stream_t *ud_stream) 718 | { 719 | char line_buff[STR_XXL]; 720 | /* GString *g_line_buff; */ 721 | GSList *class_attr_list = NULL; 722 | Attribute_t *class_attr; 723 | Attribute_t *attr; 724 | nic_handle_t *nh_ptr = NULL; /* To save NIC handle structure */ 725 | Object_t *obj = NULL; 726 | SQ_connection_t *sql_connection; 727 | int start_object; 728 | int a_type; 729 | char *a_value; 730 | char *ptr; 731 | /* here we will store the parsed nic-hdl in required format */ 732 | char nic[MAX_NH_LENGTH]; 733 | struct _nrtm *nrtm; 734 | Log_t *log_ptr= &(ud_stream->log); 735 | /* time_t stime, ftime; */ 736 | ut_timer_t stime, ftime; 737 | float obj_second1, obj_second10, timediff; 738 | int result; 739 | int operation=0; 740 | int interrupt=0; 741 | int do_update; 742 | int default_ud_mode = ud_stream->ud_mode; 743 | Line_Type_t linetype; 744 | 745 | Obj_parse_t obj_parse; /* the structure used to parse a text object */ 746 | ud_parse_init(&obj_parse); 747 | 748 | nrtm=ud_stream->nrtm; 749 | start_object = 1; 750 | a_type=-1; 751 | 752 | 753 | /* Allocate line bufer */ 754 | /* if ((g_line_buff = g_string_sized_new(STR_XXL)) == NULL){ 755 | fprintf(stderr, "E: cannot allocate gstring\n"); 756 | die; 757 | } 758 | */ 759 | /* Check connection to the database */ 760 | if(mysql_ping(ud_stream->db_connection)) { 761 | fprintf(stderr, "D: ERROR: no SQL connection\n"); 762 | /* g_string_free(g_line_buff, TRUE);*/ 763 | return(-1); 764 | } 765 | 766 | fprintf(stderr, "OK\n"); 767 | sql_connection=ud_stream->db_connection; 768 | 769 | /* This is useful for loading DB from huge disk file. */ 770 | /* We may start from <num_skip>th object */ 771 | /* num_skip=ud_stream->num_skip; */ 772 | /* if(num_skip>0) fprintf(stderr, "skipping %lu records\n", num_skip); */ 773 | 774 | /* Start timer for statistics */ 775 | UT_timeget(&stime); 776 | /* stime=time(NULL); */ 777 | 778 | /* Main loop. Reading input stream line by line */ 779 | /* Empty line signals to start processing an object, if we have it */ 780 | while (fgets(line_buff, STR_XXL, ud_stream->stream) != NULL) { 781 | 782 | switch (linetype=line_type(line_buff)) { 783 | case LINE_PLUS: 784 | case LINE_ATTRIBUTE: 785 | 786 | obj = UD_parse_object(ud_stream->db_connection, &obj_parse, line_buff); 787 | 788 | break; 789 | 790 | case LINE_COMMENT: 791 | break; 792 | 793 | case LINE_EOF: 794 | break; 795 | 796 | case LINE_ADD: 797 | /* restore the default operation mode */ 798 | operation=OP_ADD; 799 | ud_stream->ud_mode=default_ud_mode; 800 | break; 801 | 802 | case LINE_OVERRIDE_ADD: 803 | /* for override - switch the dummy bit on */ 804 | operation=OP_ADD; 805 | ud_stream->ud_mode=default_ud_mode|B_DUMMY; 806 | break; 807 | 808 | case LINE_UPD: 809 | /* restore the default operation mode */ 810 | operation=OP_UPD; 811 | ud_stream->ud_mode=default_ud_mode; 812 | break; 813 | 814 | case LINE_OVERRIDE_UPD: 815 | /* for override - switch the dummy bit on */ 816 | operation=OP_UPD; 817 | ud_stream->ud_mode=default_ud_mode|B_DUMMY; 818 | break; 819 | 820 | case LINE_DEL: 821 | /* restore the default operation mode */ 822 | operation=OP_DEL; 823 | ud_stream->ud_mode=default_ud_mode; 824 | break; 825 | 826 | case LINE_OVERRIDE_DEL: 827 | /* for override - switch the dummy bit on */ 828 | operation=OP_DEL; 829 | ud_stream->ud_mode=default_ud_mode|B_DUMMY; 830 | break; 831 | 832 | case LINE_EMPTY: 833 | /* start processing the object */ 834 | if ((obj=obj_parse.obj)) { /* if not just garbage*/ 835 | /* reorder some attributes */ 836 | obj->attributes = g_slist_sort(obj->attributes, reorder_attributes); 837 | /* prepend the class attribute */ 838 | obj->attributes = g_slist_concat(obj_parse.class_attr_list, obj->attributes); 839 | /* XXX */ 840 | /* print_object(obj); */ 841 | 842 | /* start new transaction now */ 843 | result=process_transaction(ud_stream, obj, obj_parse.object_name, obj_parse.nh_ptr, operation); 844 | 845 | /* process_transaction() frees tr and obj structures, */ 846 | /* so make sure we'll not reference these objects in the future */ 847 | operation=OP_NOOP; 848 | ud_stream->ud_mode=default_ud_mode; 849 | ud_parse_free(&obj_parse); 850 | 851 | /* this is a good place for quick interrupt */ 852 | do_update=CO_get_do_update(); 853 | if (do_update) interrupt=0; else interrupt=1; 854 | /* we still need to exit in update server mode (only 1 object at a time */ 855 | if (IS_UPDATE(ud_stream->ud_mode) && (!IS_STANDALONE(ud_stream->ud_mode))) interrupt=1; 856 | } /* if this is a real object */ 857 | /* initialize the parsing structure */ 858 | ud_parse_init(&obj_parse); 859 | 860 | break; 861 | 862 | default: 863 | fprintf(stderr, "ERROR: Bad line type\n"); 864 | } /* switch */ 865 | 866 | /* Finish processing if interrupt has been set */ 867 | if (interrupt) break; 868 | } /* while */ 869 | 870 | /* Some postprocessing */ 871 | if(!IS_UPDATE(ud_stream->ud_mode)){ 872 | /* We are in NRTM mode */ 873 | /* Clean up */ 874 | fclose(ud_stream->stream); 875 | /* In NRTM mode there may be a saved object that is unprocessed */ 876 | if(nrtm->tr){ /*saved backlog?*/ 877 | object_process(nrtm->tr); /* delete the previous(saved) object*/ 878 | result=report_transaction(nrtm->tr, &(ud_stream->log), nrtm->object_name, 879 | "NRTM:DEL:While deleting previous(saved) object"); 880 | /* create DEL serial record no matter what the result is */ 881 | create_serial(nrtm->tr); 882 | object_free(nrtm->tr->object); 883 | transaction_free(nrtm->tr); nrtm->tr=NULL; 884 | } 885 | } 886 | 887 | /* That's all. Free GString */ 888 | /* g_string_free(g_line_buff, TRUE);*/ 889 | 890 | 891 | /* Calculate some statistics */ 892 | /* ftime=time(NULL); */ 893 | UT_timeget(&ftime); 894 | /* obj_second1 = (float)(log_ptr->num_ok)/(ftime-stime); 895 | obj_second10 = (float)(log_ptr->num_ok+log_ptr->num_failed)/(ftime-stime); */ 896 | timediff = UT_timediff(&stime, &ftime); 897 | obj_second1 = (float)(log_ptr->num_ok)/timediff; 898 | obj_second10 = (float)(log_ptr->num_ok+log_ptr->num_failed)/timediff; 899 | 900 | /* Print the report */ 901 | if(IS_STANDALONE(ud_stream->ud_mode) || (!IS_UPDATE(ud_stream->ud_mode))) { 902 | /* printf("\n\n******** report **********\n%d objects OK\n%d objects failed\n", 903 | log_ptr->num_ok, log_ptr->num_failed); */ 904 | fprintf(log_ptr->logfile,"\n******** report **********\n"); 905 | fprintf(log_ptr->logfile," %d objects OK (%7.4f obj/s)\n", log_ptr->num_ok, obj_second1); 906 | fprintf(log_ptr->logfile," %d objects failed\n", log_ptr->num_failed); 907 | fprintf(log_ptr->logfile," average processing time %7.4f obj/s (%6.2f obj/min)\n", 908 | obj_second10, obj_second10*60); 909 | result=log_ptr->num_ok+log_ptr->num_failed; 910 | } 911 | return(result); 912 | 913 | } /* UD_process_stream */ 914 |