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