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