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