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