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