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