Source File modules/ud/ud_process_stream.c

    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  |