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