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