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